Merge "Definitely abort reconnecting to native daemon during shutdown"
diff --git a/Android.bp b/Android.bp
index eb5240e..010f496 100644
--- a/Android.bp
+++ b/Android.bp
@@ -133,6 +133,8 @@
"core/java/android/content/pm/IPackageStatsObserver.aidl",
"core/java/android/content/pm/IPinItemRequest.aidl",
"core/java/android/content/pm/IShortcutService.aidl",
+ "core/java/android/content/pm/dex/IArtManager.aidl",
+ "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
"core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
"core/java/android/database/IContentObserver.aidl",
":libcamera_client_aidl",
@@ -200,6 +202,11 @@
"core/java/android/nfc/INfcUnlockHandler.aidl",
"core/java/android/nfc/INfcDta.aidl",
"core/java/android/nfc/ITagRemovedCallback.aidl",
+ "core/java/android/se/omapi/ISecureElementService.aidl",
+ "core/java/android/se/omapi/ISecureElementListener.aidl",
+ "core/java/android/se/omapi/ISecureElementChannel.aidl",
+ "core/java/android/se/omapi/ISecureElementReader.aidl",
+ "core/java/android/se/omapi/ISecureElementSession.aidl",
"core/java/android/os/IBatteryPropertiesListener.aidl",
"core/java/android/os/IBatteryPropertiesRegistrar.aidl",
"core/java/android/os/ICancellationSignal.aidl",
@@ -247,6 +254,8 @@
"core/java/android/service/euicc/IGetEidCallback.aidl",
"core/java/android/service/euicc/IGetEuiccInfoCallback.aidl",
"core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl",
+ "core/java/android/service/euicc/IGetOtaStatusCallback.aidl",
+ "core/java/android/service/euicc/IOtaStatusChangedCallback.aidl",
"core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl",
"core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
"core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
@@ -453,24 +462,28 @@
"telecomm/java/com/android/internal/telecom/IInCallService.aidl",
"telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
"telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
- "telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl",
+ "telephony/java/android/telephony/data/IDataService.aidl",
+ "telephony/java/android/telephony/data/IDataServiceCallback.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsConfig.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
+ "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
"telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
+ "telephony/java/android/telephony/INetworkService.aidl",
+ "telephony/java/android/telephony/INetworkServiceCallback.aidl",
"telephony/java/com/android/ims/internal/IImsCallSession.aidl",
"telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl",
"telephony/java/com/android/ims/internal/IImsConfig.aidl",
@@ -508,7 +521,30 @@
"telephony/java/com/android/internal/telephony/ITelephony.aidl",
"telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
"telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl",
"telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
+ "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
"wifi/java/android/net/wifi/IWifiManager.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl",
@@ -594,6 +630,7 @@
"android.hardware.vibrator-V1.0-java-constants",
"android.hardware.vibrator-V1.1-java-constants",
"android.hardware.wifi-V1.0-java-constants",
+ "android.hardware.radio-V1.0-java",
],
// Loaded with System.loadLibrary by android.view.textclassifier
diff --git a/Android.mk b/Android.mk
index 83cdd73..95c3340 100644
--- a/Android.mk
+++ b/Android.mk
@@ -32,224 +32,26 @@
# ============================================================
include $(CLEAR_VARS)
-aidl_files := \
- frameworks/base/telephony/java/android/telephony/mbms/DownloadRequest.aidl \
- frameworks/base/telephony/java/android/telephony/mbms/FileInfo.aidl \
- frameworks/base/telephony/java/android/telephony/mbms/FileServiceInfo.aidl \
- frameworks/base/telephony/java/android/telephony/mbms/ServiceInfo.aidl \
- frameworks/base/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl \
- frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
- frameworks/base/telephony/java/android/telephony/SubscriptionInfo.aidl \
- frameworks/base/telephony/java/android/telephony/CellInfo.aidl \
- frameworks/base/telephony/java/android/telephony/SignalStrength.aidl \
- frameworks/base/telephony/java/android/telephony/IccOpenLogicalChannelResponse.aidl \
- frameworks/base/telephony/java/android/telephony/NeighboringCellInfo.aidl \
- frameworks/base/telephony/java/android/telephony/ModemActivityInfo.aidl \
- frameworks/base/telephony/java/android/telephony/UiccAccessRule.aidl \
- frameworks/base/telephony/java/android/telephony/data/DataCallResponse.aidl \
- frameworks/base/telephony/java/android/telephony/data/DataProfile.aidl \
- frameworks/base/telephony/java/android/telephony/euicc/DownloadableSubscription.aidl \
- frameworks/base/telephony/java/android/telephony/euicc/EuiccInfo.aidl \
- frameworks/base/location/java/android/location/Location.aidl \
- frameworks/base/location/java/android/location/Address.aidl \
- frameworks/base/location/java/android/location/Criteria.aidl \
- frameworks/base/media/java/android/media/MediaMetadata.aidl \
- frameworks/base/media/java/android/media/MediaDescription.aidl \
- frameworks/base/media/java/android/media/Rating.aidl \
- frameworks/base/media/java/android/media/AudioAttributes.aidl \
- frameworks/base/media/java/android/media/AudioFocusInfo.aidl \
- frameworks/base/media/java/android/media/session/PlaybackState.aidl \
- frameworks/base/media/java/android/media/session/MediaSession.aidl \
- frameworks/base/media/java/android/media/tv/TvInputInfo.aidl \
- frameworks/base/media/java/android/media/tv/TvTrackInfo.aidl \
- frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \
- frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \
- frameworks/base/wifi/java/android/net/wifi/aware/ConfigRequest.aidl \
- frameworks/base/wifi/java/android/net/wifi/aware/PublishConfig.aidl \
- frameworks/base/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl \
- frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \
- frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \
- frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \
- frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl \
- frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl \
- frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl \
- frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \
- frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \
- frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \
- frameworks/base/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl \
- frameworks/base/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl \
- frameworks/base/wifi/java/android/net/wifi/WifiConfiguration.aidl \
- frameworks/base/wifi/java/android/net/wifi/WifiInfo.aidl \
- frameworks/base/graphics/java/android/graphics/Region.aidl \
- frameworks/base/graphics/java/android/graphics/Bitmap.aidl \
- frameworks/base/graphics/java/android/graphics/Point.aidl \
- frameworks/base/graphics/java/android/graphics/PointF.aidl \
- frameworks/base/graphics/java/android/graphics/RectF.aidl \
- frameworks/base/graphics/java/android/graphics/Rect.aidl \
- frameworks/base/graphics/java/android/graphics/drawable/Icon.aidl \
- frameworks/base/core/java/android/accounts/AuthenticatorDescription.aidl \
- frameworks/base/core/java/android/accounts/Account.aidl \
- frameworks/base/core/java/android/app/admin/ConnectEvent.aidl \
- frameworks/base/core/java/android/app/admin/DnsEvent.aidl \
- frameworks/base/core/java/android/app/admin/NetworkEvent.aidl \
- frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
- frameworks/base/core/java/android/app/admin/PasswordMetrics.aidl \
- frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
- frameworks/base/core/java/android/print/PageRange.aidl \
- frameworks/base/core/java/android/print/PrintAttributes.aidl \
- frameworks/base/core/java/android/print/PrinterCapabilitiesInfo.aidl \
- frameworks/base/core/java/android/print/PrinterId.aidl \
- frameworks/base/core/java/android/print/PrintJobInfo.aidl \
- frameworks/base/core/java/android/print/PrinterInfo.aidl \
- frameworks/base/core/java/android/print/PrintJobId.aidl \
- frameworks/base/core/java/android/printservice/recommendation/RecommendationInfo.aidl \
- frameworks/base/core/java/android/hardware/radio/ProgramSelector.aidl \
- frameworks/base/core/java/android/hardware/radio/RadioManager.aidl \
- frameworks/base/core/java/android/hardware/radio/RadioMetadata.aidl \
- frameworks/base/core/java/android/hardware/usb/UsbDevice.aidl \
- frameworks/base/core/java/android/hardware/usb/UsbInterface.aidl \
- frameworks/base/core/java/android/hardware/usb/UsbEndpoint.aidl \
- frameworks/base/core/java/android/hardware/usb/UsbAccessory.aidl \
- frameworks/base/core/java/android/os/Messenger.aidl \
- frameworks/base/core/java/android/os/PatternMatcher.aidl \
- frameworks/base/core/java/android/os/Message.aidl \
- frameworks/base/core/java/android/os/UserHandle.aidl \
- frameworks/base/core/java/android/os/ParcelUuid.aidl \
- frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
- frameworks/base/core/java/android/os/ResultReceiver.aidl \
- frameworks/base/core/java/android/os/WorkSource.aidl \
- frameworks/base/core/java/android/os/DropBoxManager.aidl \
- frameworks/base/core/java/android/os/Bundle.aidl \
- frameworks/base/core/java/android/os/Debug.aidl \
- frameworks/base/core/java/android/os/SharedMemory.aidl \
- frameworks/base/core/java/android/os/StrictMode.aidl \
- frameworks/base/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl \
- frameworks/base/core/java/android/net/Network.aidl \
- frameworks/base/core/java/android/net/RouteInfo.aidl \
- frameworks/base/core/java/android/net/NetworkInfo.aidl \
- frameworks/base/core/java/android/net/IpPrefix.aidl \
- frameworks/base/core/java/android/net/NetworkCapabilities.aidl \
- frameworks/base/core/java/android/net/DhcpInfo.aidl \
- frameworks/base/core/java/android/net/ProxyInfo.aidl \
- frameworks/base/core/java/android/net/LinkProperties.aidl \
- frameworks/base/core/java/android/net/Uri.aidl \
- frameworks/base/core/java/android/net/NetworkRequest.aidl \
- frameworks/base/core/java/android/net/LinkAddress.aidl \
- frameworks/base/core/java/android/util/MemoryIntArray.aidl \
- frameworks/base/core/java/android/view/Display.aidl \
- frameworks/base/core/java/android/view/InputDevice.aidl \
- frameworks/base/core/java/android/view/InputEvent.aidl \
- frameworks/native/aidl/gui/android/view/Surface.aidl \
- frameworks/base/core/java/android/view/WindowContentFrameStats.aidl \
- frameworks/base/core/java/android/view/inputmethod/InputMethodSubtype.aidl \
- frameworks/base/core/java/android/view/inputmethod/CursorAnchorInfo.aidl \
- frameworks/base/core/java/android/view/inputmethod/CompletionInfo.aidl \
- frameworks/base/core/java/android/view/inputmethod/ExtractedText.aidl \
- frameworks/base/core/java/android/view/inputmethod/EditorInfo.aidl \
- frameworks/base/core/java/android/view/inputmethod/InputMethodInfo.aidl \
- frameworks/base/core/java/android/view/inputmethod/CorrectionInfo.aidl \
- frameworks/base/core/java/android/view/inputmethod/InputBinding.aidl \
- frameworks/base/core/java/android/view/inputmethod/ExtractedTextRequest.aidl \
- frameworks/base/core/java/android/view/DragEvent.aidl \
- frameworks/base/core/java/android/view/KeyEvent.aidl \
- frameworks/base/core/java/android/view/WindowManager.aidl \
- frameworks/base/core/java/android/view/WindowAnimationFrameStats.aidl \
- frameworks/base/core/java/android/view/MotionEvent.aidl \
- frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl \
- frameworks/base/core/java/android/view/accessibility/AccessibilityRecord.aidl \
- frameworks/base/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl \
- frameworks/base/core/java/android/view/accessibility/AccessibilityEvent.aidl \
- frameworks/base/core/java/android/view/textservice/SpellCheckerSubtype.aidl \
- frameworks/base/core/java/android/view/textservice/TextInfo.aidl \
- frameworks/base/core/java/android/view/textservice/SpellCheckerInfo.aidl \
- frameworks/base/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl \
- frameworks/base/core/java/android/view/textservice/SuggestionsInfo.aidl \
- frameworks/base/core/java/android/service/carrier/CarrierIdentifier.aidl \
- frameworks/base/core/java/android/service/carrier/MessagePdu.aidl \
- frameworks/base/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.aidl \
- frameworks/base/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.aidl \
- frameworks/base/core/java/android/service/euicc/GetEuiccProfileInfoListResult.aidl \
- frameworks/base/core/java/android/service/notification/Adjustment.aidl \
- frameworks/base/core/java/android/service/notification/Condition.aidl \
- frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \
- frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
- frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
- frameworks/base/core/java/android/service/resolver/ResolverTarget.aidl \
- frameworks/base/core/java/android/speech/tts/Voice.aidl \
- frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \
- frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \
- frameworks/base/core/java/android/app/usage/StorageStats.aidl \
- frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
- frameworks/base/core/java/android/app/Notification.aidl \
- frameworks/base/core/java/android/app/NotificationManager.aidl \
- frameworks/base/core/java/android/app/WallpaperInfo.aidl \
- frameworks/base/core/java/android/app/AppOpsManager.aidl \
- frameworks/base/core/java/android/app/ActivityManager.aidl \
- frameworks/base/core/java/android/app/PendingIntent.aidl \
- frameworks/base/core/java/android/app/AlarmManager.aidl \
- frameworks/base/core/java/android/app/SearchableInfo.aidl \
- frameworks/base/core/java/android/app/VoiceInteractor.aidl \
- frameworks/base/core/java/android/app/assist/AssistContent.aidl \
- frameworks/base/core/java/android/app/assist/AssistStructure.aidl \
- frameworks/base/core/java/android/app/job/JobParameters.aidl \
- frameworks/base/core/java/android/app/job/JobInfo.aidl \
- frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
- frameworks/base/core/java/android/content/ClipDescription.aidl \
- frameworks/base/core/java/android/content/IntentFilter.aidl \
- frameworks/base/core/java/android/content/Intent.aidl \
- frameworks/base/core/java/android/content/res/Configuration.aidl \
- frameworks/base/core/java/android/content/res/ObbInfo.aidl \
- frameworks/base/core/java/android/content/RestrictionEntry.aidl \
- frameworks/base/core/java/android/content/ClipData.aidl \
- frameworks/base/core/java/android/content/SyncAdapterType.aidl \
- frameworks/base/core/java/android/content/SyncRequest.aidl \
- frameworks/base/core/java/android/content/PeriodicSync.aidl \
- frameworks/base/core/java/android/content/SyncResult.aidl \
- frameworks/base/core/java/android/content/pm/FeatureInfo.aidl \
- frameworks/base/core/java/android/content/pm/InstrumentationInfo.aidl \
- frameworks/base/core/java/android/content/pm/PackageInstaller.aidl \
- frameworks/base/core/java/android/content/pm/ServiceInfo.aidl \
- frameworks/base/core/java/android/content/pm/Signature.aidl \
- frameworks/base/core/java/android/content/pm/ApplicationInfo.aidl \
- frameworks/base/core/java/android/content/pm/PermissionInfo.aidl \
- frameworks/base/core/java/android/content/pm/ActivityInfo.aidl \
- frameworks/base/core/java/android/content/pm/ConfigurationInfo.aidl \
- frameworks/base/core/java/android/content/pm/PackageInfo.aidl \
- frameworks/base/core/java/android/content/pm/ResolveInfo.aidl \
- frameworks/base/core/java/android/content/pm/ProviderInfo.aidl \
- frameworks/base/core/java/android/content/pm/PackageStats.aidl \
- frameworks/base/core/java/android/content/pm/PermissionGroupInfo.aidl \
- frameworks/base/core/java/android/content/pm/ShortcutInfo.aidl \
- frameworks/base/core/java/android/content/pm/LabeledIntent.aidl \
- frameworks/base/core/java/android/content/ComponentName.aidl \
- frameworks/base/core/java/android/content/SyncStats.aidl \
- frameworks/base/core/java/android/content/ContentValues.aidl \
- frameworks/base/core/java/android/content/SyncInfo.aidl \
- frameworks/base/core/java/android/content/IntentSender.aidl \
- frameworks/base/core/java/android/widget/RemoteViews.aidl \
- frameworks/base/core/java/android/text/style/SuggestionSpan.aidl \
- frameworks/base/core/java/android/nfc/Tag.aidl \
- frameworks/base/core/java/android/nfc/NdefRecord.aidl \
- frameworks/base/core/java/android/nfc/NdefMessage.aidl \
- frameworks/base/core/java/android/database/CursorWindow.aidl \
- frameworks/base/core/java/android/service/quicksettings/Tile.aidl \
- frameworks/native/aidl/binder/android/os/PersistableBundle.aidl \
- system/bt/binder/android/bluetooth/BluetoothHealthAppConfiguration.aidl \
- system/bt/binder/android/bluetooth/le/AdvertiseSettings.aidl \
- system/bt/binder/android/bluetooth/le/ScanSettings.aidl \
- system/bt/binder/android/bluetooth/le/AdvertiseData.aidl \
- system/bt/binder/android/bluetooth/le/ScanFilter.aidl \
- system/bt/binder/android/bluetooth/le/ScanResult.aidl \
- system/bt/binder/android/bluetooth/BluetoothDevice.aidl \
- system/netd/server/binder/android/net/UidRange.aidl \
- frameworks/base/telephony/java/android/telephony/PcoData.aidl \
+aidl_parcelables :=
+define stubs-to-aidl-parcelables
+ gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/$1.aidl
+ aidl_parcelables += $$(gen)
+ $$(gen): $(call java-lib-header-files,$1) $(HOST_OUT_EXECUTABLES)/sdkparcelables
+ @echo Extract SDK parcelables: $$@
+ rm -f $$@
+ $(HOST_OUT_EXECUTABLES)/sdkparcelables $$< $$@
+endef
+
+$(foreach stubs,android_stubs_current android_test_stubs_current android_system_stubs_current,\
+ $(eval $(call stubs-to-aidl-parcelables,$(stubs))))
gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
-$(gen): PRIVATE_SRC_FILES := $(aidl_files)
-ALL_SDK_FILES += $(gen)
-$(gen): $(aidl_files) | $(AIDL)
- @echo Aidl Preprocess: $@
- $(hide) $(AIDL) --preprocess $@ $(PRIVATE_SRC_FILES)
+.KATI_RESTAT: $(gen)
+$(gen): $(aidl_parcelables)
+ @echo Combining SDK parcelables: $@
+ rm -f $@.tmp
+ cat $^ | sort -u > $@.tmp
+ $(call commit-change-for-toc,$@)
# the documentation
# ============================================================
@@ -280,7 +82,7 @@
# to document and check apis
files_to_check_apis := \
$(call find-other-java-files, \
- legacy-test/src \
+ test-base/src \
$(non_base_dirs) \
)
@@ -478,6 +280,8 @@
$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-referenceonly \
-api $(INTERNAL_PLATFORM_API_FILE) \
+ -privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+ -privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
-removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \
-nodocs
@@ -487,9 +291,9 @@
include $(BUILD_DROIDDOC)
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
-$(INTERNAL_PLATFORM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \
+ $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+ $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
# ==== the system api stubs ===================================
@@ -514,6 +318,8 @@
-referenceonly \
-showAnnotation android.annotation.SystemApi \
-api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+ -privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+ -privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \
-removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \
-exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \
-nodocs
@@ -524,9 +330,9 @@
include $(BUILD_DROIDDOC)
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
-$(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+ $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+ $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
# ==== the test api stubs ===================================
@@ -562,8 +368,6 @@
include $(BUILD_DROIDDOC)
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
$(INTERNAL_PLATFORM_TEST_API_FILE): $(full_target)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
@@ -593,9 +397,6 @@
include $(BUILD_DROIDDOC)
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
-
# Run this for checkbuild
checkbuild: doc-comment-check-docs
# Check comment when you are updating the API
@@ -893,9 +694,43 @@
LOCAL_SRC_FILES := \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
+# b/72714520
+LOCAL_ERROR_PRONE_FLAGS := -Xep:MissingOverride:OFF
include $(BUILD_HOST_JAVA_LIBRARY)
+# ==== hiddenapi lists =======================================
+
+# Copy blacklist and dark greylist over into the build folder.
+# This is for ART buildbots which need to mock these lists and have alternative
+# rules for building them. Other rules in the build system should depend on the
+# files in the build folder.
+
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
+
+# Generate light greylist as private API minus (blacklist plus dark greylist).
+
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+ if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
+ echo "There should be no overlap between $(BLACKLIST) and $(DARK_GREYLIST)" 1>&2; \
+ exit 1; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
+ echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ exit 2; \
+ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
+ echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+ exit 3; \
+ fi
+ comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(DARK_GREYLIST)) > $@
+
# Include subdirectory makefiles
# ============================================================
@@ -906,3 +741,4 @@
endif
endif # ANDROID_BUILD_EMBEDDED
+
diff --git a/api/current.txt b/api/current.txt
index e8d0ee2..8df028c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6,6 +6,7 @@
public static final class Manifest.permission {
ctor public Manifest.permission();
+ field public static final java.lang.String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
@@ -92,6 +93,7 @@
field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
field public static final java.lang.String NFC = "android.permission.NFC";
+ field public static final java.lang.String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
@@ -5985,16 +5987,16 @@
method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
- method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+ method public android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method public boolean injectInputEvent(android.view.InputEvent, boolean);
- method public final boolean performGlobalAction(int);
+ method public boolean performGlobalAction(int);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
method public void setRunAsMonkey(boolean);
- method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
+ method public void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public android.graphics.Bitmap takeScreenshot();
method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1
@@ -6532,6 +6534,7 @@
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
}
@@ -6949,6 +6952,7 @@
public static class NetworkStats.Bucket {
ctor public NetworkStats.Bucket();
+ method public int getDefaultNetwork();
method public long getEndTimeStamp();
method public int getMetered();
method public int getRoaming();
@@ -6960,6 +6964,9 @@
method public long getTxBytes();
method public long getTxPackets();
method public int getUid();
+ field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
+ field public static final int DEFAULT_NETWORK_NO = 1; // 0x1
+ field public static final int DEFAULT_NETWORK_YES = 2; // 0x2
field public static final int METERED_ALL = -1; // 0xffffffff
field public static final int METERED_NO = 1; // 0x1
field public static final int METERED_YES = 2; // 0x2
@@ -9042,6 +9049,7 @@
field public static final java.lang.String DISPLAY_SERVICE = "display";
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
+ field public static final java.lang.String EUICC_SERVICE = "euicc";
field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
@@ -10845,6 +10853,7 @@
field public static final java.lang.String FEATURE_SIP_VOIP = "android.software.sip.voip";
field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
+ field public static final java.lang.String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
field public static final java.lang.String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
@@ -11183,15 +11192,15 @@
public final class AssetManager implements java.lang.AutoCloseable {
method public void close();
- method public final java.lang.String[] getLocales();
- method public final java.lang.String[] list(java.lang.String) throws java.io.IOException;
- method public final java.io.InputStream open(java.lang.String) throws java.io.IOException;
- method public final java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
- method public final android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
- method public final android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
- method public final android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
- method public final android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
- method public final android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
+ method public java.lang.String[] getLocales();
+ method public java.lang.String[] list(java.lang.String) throws java.io.IOException;
+ method public java.io.InputStream open(java.lang.String) throws java.io.IOException;
+ method public java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
+ method public android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
+ method public android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
+ method public android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
+ method public android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
+ method public android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
field public static final int ACCESS_BUFFER = 3; // 0x3
field public static final int ACCESS_RANDOM = 1; // 0x1
field public static final int ACCESS_STREAMING = 2; // 0x2
@@ -11199,15 +11208,9 @@
}
public final class AssetManager.AssetInputStream extends java.io.InputStream {
- method public final int available() throws java.io.IOException;
- method public final void close() throws java.io.IOException;
- method public final void mark(int);
- method public final boolean markSupported();
- method public final int read() throws java.io.IOException;
- method public final int read(byte[]) throws java.io.IOException;
- method public final int read(byte[], int, int) throws java.io.IOException;
- method public final void reset() throws java.io.IOException;
- method public final long skip(long) throws java.io.IOException;
+ method public void mark(int);
+ method public int read() throws java.io.IOException;
+ method public void reset() throws java.io.IOException;
}
public class ColorStateList implements android.os.Parcelable {
@@ -11967,7 +11970,7 @@
method public java.util.List<android.util.Pair<java.lang.String, java.lang.String>> getAttachedDbs();
method public long getMaximumSize();
method public long getPageSize();
- method public final java.lang.String getPath();
+ method public java.lang.String getPath();
method public deprecated java.util.Map<java.lang.String, java.lang.String> getSyncedTables();
method public int getVersion();
method public boolean inTransaction();
@@ -12619,29 +12622,29 @@
method public void eraseColor(int);
method public android.graphics.Bitmap extractAlpha();
method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
- method public final int getAllocationByteCount();
- method public final int getByteCount();
- method public final android.graphics.ColorSpace getColorSpace();
- method public final android.graphics.Bitmap.Config getConfig();
+ method public int getAllocationByteCount();
+ method public int getByteCount();
+ method public android.graphics.ColorSpace getColorSpace();
+ method public android.graphics.Bitmap.Config getConfig();
method public int getDensity();
method public int getGenerationId();
- method public final int getHeight();
+ method public int getHeight();
method public byte[] getNinePatchChunk();
method public int getPixel(int, int);
method public void getPixels(int[], int, int, int, int, int, int);
- method public final int getRowBytes();
+ method public int getRowBytes();
method public int getScaledHeight(android.graphics.Canvas);
method public int getScaledHeight(android.util.DisplayMetrics);
method public int getScaledHeight(int);
method public int getScaledWidth(android.graphics.Canvas);
method public int getScaledWidth(android.util.DisplayMetrics);
method public int getScaledWidth(int);
- method public final int getWidth();
- method public final boolean hasAlpha();
- method public final boolean hasMipMap();
- method public final boolean isMutable();
- method public final boolean isPremultiplied();
- method public final boolean isRecycled();
+ method public int getWidth();
+ method public boolean hasAlpha();
+ method public boolean hasMipMap();
+ method public boolean isMutable();
+ method public boolean isPremultiplied();
+ method public boolean isRecycled();
method public void prepareToDraw();
method public void reconfigure(int, int, android.graphics.Bitmap.Config);
method public void recycle();
@@ -12649,11 +12652,11 @@
method public void setConfig(android.graphics.Bitmap.Config);
method public void setDensity(int);
method public void setHasAlpha(boolean);
- method public final void setHasMipMap(boolean);
+ method public void setHasMipMap(boolean);
method public void setHeight(int);
method public void setPixel(int, int, int);
method public void setPixels(int[], int, int, int, int, int, int);
- method public final void setPremultiplied(boolean);
+ method public void setPremultiplied(boolean);
method public void setWidth(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.Bitmap> CREATOR;
@@ -12725,7 +12728,7 @@
method public android.graphics.Bitmap decodeRegion(android.graphics.Rect, android.graphics.BitmapFactory.Options);
method public int getHeight();
method public int getWidth();
- method public final boolean isRecycled();
+ method public boolean isRecycled();
method public static android.graphics.BitmapRegionDecoder newInstance(byte[], int, int, boolean) throws java.io.IOException;
method public static android.graphics.BitmapRegionDecoder newInstance(java.io.FileDescriptor, boolean) throws java.io.IOException;
method public static android.graphics.BitmapRegionDecoder newInstance(java.io.InputStream, boolean) throws java.io.IOException;
@@ -13728,22 +13731,22 @@
ctor public Rect();
ctor public Rect(int, int, int, int);
ctor public Rect(android.graphics.Rect);
- method public final int centerX();
- method public final int centerY();
+ method public int centerX();
+ method public int centerY();
method public boolean contains(int, int);
method public boolean contains(int, int, int, int);
method public boolean contains(android.graphics.Rect);
method public int describeContents();
- method public final float exactCenterX();
- method public final float exactCenterY();
+ method public float exactCenterX();
+ method public float exactCenterY();
method public java.lang.String flattenToString();
- method public final int height();
+ method public int height();
method public void inset(int, int);
method public boolean intersect(int, int, int, int);
method public boolean intersect(android.graphics.Rect);
method public boolean intersects(int, int, int, int);
method public static boolean intersects(android.graphics.Rect, android.graphics.Rect);
- method public final boolean isEmpty();
+ method public boolean isEmpty();
method public void offset(int, int);
method public void offsetTo(int, int);
method public void readFromParcel(android.os.Parcel);
@@ -13757,7 +13760,7 @@
method public void union(int, int, int, int);
method public void union(android.graphics.Rect);
method public void union(int, int);
- method public final int width();
+ method public int width();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.Rect> CREATOR;
field public int bottom;
@@ -15293,9 +15296,7 @@
}
public static final class CameraCharacteristics.Key<T> {
- method public final boolean equals(java.lang.Object);
method public java.lang.String getName();
- method public final int hashCode();
}
public abstract class CameraConstrainedHighSpeedCaptureSession extends android.hardware.camera2.CameraCaptureSession {
@@ -15637,9 +15638,7 @@
}
public static final class CaptureRequest.Key<T> {
- method public final boolean equals(java.lang.Object);
method public java.lang.String getName();
- method public final int hashCode();
}
public class CaptureResult extends android.hardware.camera2.CameraMetadata {
@@ -15727,9 +15726,7 @@
}
public static final class CaptureResult.Key<T> {
- method public final boolean equals(java.lang.Object);
method public java.lang.String getName();
- method public final int hashCode();
}
public final class DngCreator implements java.lang.AutoCloseable {
@@ -15837,7 +15834,7 @@
method public float getComponent(int);
method public float getGreenEven();
method public float getGreenOdd();
- method public final float getRed();
+ method public float getRed();
field public static final int BLUE = 3; // 0x3
field public static final int COUNT = 4; // 0x4
field public static final int GREEN_EVEN = 1; // 0x1
@@ -15851,16 +15848,16 @@
method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRangesFor(android.util.Size);
method public android.util.Size[] getHighSpeedVideoSizes();
method public android.util.Size[] getHighSpeedVideoSizesFor(android.util.Range<java.lang.Integer>);
- method public final int[] getInputFormats();
+ method public int[] getInputFormats();
method public android.util.Size[] getInputSizes(int);
- method public final int[] getOutputFormats();
+ method public int[] getOutputFormats();
method public long getOutputMinFrameDuration(int, android.util.Size);
method public <T> long getOutputMinFrameDuration(java.lang.Class<T>, android.util.Size);
method public <T> android.util.Size[] getOutputSizes(java.lang.Class<T>);
method public android.util.Size[] getOutputSizes(int);
method public long getOutputStallDuration(int, android.util.Size);
method public <T> long getOutputStallDuration(java.lang.Class<T>, android.util.Size);
- method public final int[] getValidOutputFormatsForInput(int);
+ method public int[] getValidOutputFormatsForInput(int);
method public boolean isOutputSupportedFor(int);
method public static <T> boolean isOutputSupportedFor(java.lang.Class<T>);
method public boolean isOutputSupportedFor(android.view.Surface);
@@ -16154,12 +16151,12 @@
public final class UCharacter implements android.icu.lang.UCharacterEnums.ECharacterCategory android.icu.lang.UCharacterEnums.ECharacterDirection {
method public static int charCount(int);
- method public static final int codePointAt(java.lang.CharSequence, int);
- method public static final int codePointAt(char[], int);
- method public static final int codePointAt(char[], int, int);
- method public static final int codePointBefore(java.lang.CharSequence, int);
- method public static final int codePointBefore(char[], int);
- method public static final int codePointBefore(char[], int, int);
+ method public static int codePointAt(java.lang.CharSequence, int);
+ method public static int codePointAt(char[], int);
+ method public static int codePointAt(char[], int, int);
+ method public static int codePointBefore(java.lang.CharSequence, int);
+ method public static int codePointBefore(char[], int);
+ method public static int codePointBefore(char[], int, int);
method public static int codePointCount(java.lang.CharSequence, int, int);
method public static int codePointCount(char[], int, int);
method public static int digit(int, int);
@@ -16167,7 +16164,7 @@
method public static int foldCase(int, boolean);
method public static java.lang.String foldCase(java.lang.String, boolean);
method public static int foldCase(int, int);
- method public static final java.lang.String foldCase(java.lang.String, int);
+ method public static java.lang.String foldCase(java.lang.String, int);
method public static char forDigit(int, int);
method public static android.icu.util.VersionInfo getAge(int);
method public static int getBidiPairedBracket(int);
@@ -16219,8 +16216,8 @@
method public static boolean isPrintable(int);
method public static boolean isSpaceChar(int);
method public static boolean isSupplementary(int);
- method public static final boolean isSupplementaryCodePoint(int);
- method public static final boolean isSurrogatePair(char, char);
+ method public static boolean isSupplementaryCodePoint(int);
+ method public static boolean isSurrogatePair(char, char);
method public static boolean isTitleCase(int);
method public static boolean isUAlphabetic(int);
method public static boolean isULowercase(int);
@@ -16229,13 +16226,13 @@
method public static boolean isUnicodeIdentifierPart(int);
method public static boolean isUnicodeIdentifierStart(int);
method public static boolean isUpperCase(int);
- method public static final boolean isValidCodePoint(int);
+ method public static boolean isValidCodePoint(int);
method public static boolean isWhitespace(int);
method public static int offsetByCodePoints(java.lang.CharSequence, int, int);
method public static int offsetByCodePoints(char[], int, int, int, int);
- method public static final int toChars(int, char[], int);
- method public static final char[] toChars(int);
- method public static final int toCodePoint(char, char);
+ method public static int toChars(int, char[], int);
+ method public static char[] toChars(int);
+ method public static int toCodePoint(char, char);
method public static int toLowerCase(int);
method public static java.lang.String toLowerCase(java.lang.String);
method public static java.lang.String toLowerCase(java.util.Locale, java.lang.String);
@@ -16525,7 +16522,7 @@
}
public static final class UCharacter.UnicodeBlock extends java.lang.Character.Subset {
- method public static final android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String);
+ method public static android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String);
method public int getID();
method public static android.icu.lang.UCharacter.UnicodeBlock getInstance(int);
method public static android.icu.lang.UCharacter.UnicodeBlock of(int);
@@ -17332,20 +17329,20 @@
}
public final class UScript {
- method public static final boolean breaksBetweenLetters(int);
- method public static final int[] getCode(java.util.Locale);
- method public static final int[] getCode(android.icu.util.ULocale);
- method public static final int[] getCode(java.lang.String);
- method public static final int getCodeFromName(java.lang.String);
- method public static final java.lang.String getName(int);
- method public static final java.lang.String getSampleString(int);
- method public static final int getScript(int);
- method public static final int getScriptExtensions(int, java.util.BitSet);
- method public static final java.lang.String getShortName(int);
- method public static final android.icu.lang.UScript.ScriptUsage getUsage(int);
- method public static final boolean hasScript(int, int);
- method public static final boolean isCased(int);
- method public static final boolean isRightToLeft(int);
+ method public static boolean breaksBetweenLetters(int);
+ method public static int[] getCode(java.util.Locale);
+ method public static int[] getCode(android.icu.util.ULocale);
+ method public static int[] getCode(java.lang.String);
+ method public static int getCodeFromName(java.lang.String);
+ method public static java.lang.String getName(int);
+ method public static java.lang.String getSampleString(int);
+ method public static int getScript(int);
+ method public static int getScriptExtensions(int, java.util.BitSet);
+ method public static java.lang.String getShortName(int);
+ method public static android.icu.lang.UScript.ScriptUsage getUsage(int);
+ method public static boolean hasScript(int, int);
+ method public static boolean isCased(int);
+ method public static boolean isRightToLeft(int);
field public static final int ADLAM = 167; // 0xa7
field public static final int AFAKA = 147; // 0x93
field public static final int AHOM = 161; // 0xa1
@@ -17760,14 +17757,14 @@
method public deprecated int hashCode();
method public int next();
method public int previous();
- method public static final int primaryOrder(int);
+ method public static int primaryOrder(int);
method public void reset();
- method public static final int secondaryOrder(int);
+ method public static int secondaryOrder(int);
method public void setOffset(int);
method public void setText(java.lang.String);
method public void setText(android.icu.text.UCharacterIterator);
method public void setText(java.text.CharacterIterator);
- method public static final int tertiaryOrder(int);
+ method public static int tertiaryOrder(int);
field public static final int IGNORABLE = 0; // 0x0
field public static final int NULLORDER = -1; // 0xffffffff
}
@@ -18994,7 +18991,7 @@
method public boolean isUpperCaseFirst();
method public void setAlternateHandlingDefault();
method public void setAlternateHandlingShifted(boolean);
- method public final void setCaseFirstDefault();
+ method public void setCaseFirstDefault();
method public void setCaseLevel(boolean);
method public void setCaseLevelDefault();
method public void setDecompositionDefault();
@@ -19873,6 +19870,13 @@
ctor public ICUUncheckedIOException(java.lang.String, java.lang.Throwable);
}
+ public class IllformedLocaleException extends java.lang.RuntimeException {
+ ctor public IllformedLocaleException();
+ ctor public IllformedLocaleException(java.lang.String);
+ ctor public IllformedLocaleException(java.lang.String, int);
+ method public int getErrorIndex();
+ }
+
public class IndianCalendar extends android.icu.util.Calendar {
ctor public IndianCalendar();
ctor public IndianCalendar(android.icu.util.TimeZone);
@@ -19957,6 +19961,32 @@
field public static final int TAISHO;
}
+ public final class LocaleData {
+ method public static android.icu.util.VersionInfo getCLDRVersion();
+ method public java.lang.String getDelimiter(int);
+ method public static android.icu.util.LocaleData getInstance(android.icu.util.ULocale);
+ method public static android.icu.util.LocaleData getInstance();
+ method public static android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale);
+ method public boolean getNoSubstitute();
+ method public static android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale);
+ method public void setNoSubstitute(boolean);
+ field public static final int ALT_QUOTATION_END = 3; // 0x3
+ field public static final int ALT_QUOTATION_START = 2; // 0x2
+ field public static final int QUOTATION_END = 1; // 0x1
+ field public static final int QUOTATION_START = 0; // 0x0
+ }
+
+ public static final class LocaleData.MeasurementSystem {
+ field public static final android.icu.util.LocaleData.MeasurementSystem SI;
+ field public static final android.icu.util.LocaleData.MeasurementSystem UK;
+ field public static final android.icu.util.LocaleData.MeasurementSystem US;
+ }
+
+ public static final class LocaleData.PaperSize {
+ method public int getHeight();
+ method public int getWidth();
+ }
+
public class Measure {
ctor public Measure(java.lang.Number, android.icu.util.MeasureUnit);
method public java.lang.Number getNumber();
@@ -22119,40 +22149,40 @@
method public static android.media.MediaCodec createByCodecName(java.lang.String) throws java.io.IOException;
method public static android.media.MediaCodec createDecoderByType(java.lang.String) throws java.io.IOException;
method public static android.media.MediaCodec createEncoderByType(java.lang.String) throws java.io.IOException;
- method public final android.view.Surface createInputSurface();
+ method public android.view.Surface createInputSurface();
method public static android.view.Surface createPersistentInputSurface();
- method public final int dequeueInputBuffer(long);
- method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
+ method public int dequeueInputBuffer(long);
+ method public int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long);
method protected void finalize();
- method public final void flush();
+ method public void flush();
method public android.media.MediaCodecInfo getCodecInfo();
method public java.nio.ByteBuffer getInputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
- method public final android.media.MediaFormat getInputFormat();
+ method public android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
method public android.os.PersistableBundle getMetrics();
- method public final java.lang.String getName();
+ method public java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
- method public final android.media.MediaFormat getOutputFormat();
- method public final android.media.MediaFormat getOutputFormat(int);
+ method public android.media.MediaFormat getOutputFormat();
+ method public android.media.MediaFormat getOutputFormat(int);
method public android.media.Image getOutputImage(int);
- method public final void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
- method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
- method public final void release();
- method public final void releaseOutputBuffer(int, boolean);
- method public final void releaseOutputBuffer(int, long);
- method public final void reset();
+ method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
+ method public void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
+ method public void release();
+ method public void releaseOutputBuffer(int, boolean);
+ method public void releaseOutputBuffer(int, long);
+ method public void reset();
method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler);
method public void setCallback(android.media.MediaCodec.Callback);
method public void setInputSurface(android.view.Surface);
method public void setOnFrameRenderedListener(android.media.MediaCodec.OnFrameRenderedListener, android.os.Handler);
method public void setOutputSurface(android.view.Surface);
- method public final void setParameters(android.os.Bundle);
- method public final void setVideoScalingMode(int);
- method public final void signalEndOfInputStream();
- method public final void start();
- method public final void stop();
+ method public void setParameters(android.os.Bundle);
+ method public void setVideoScalingMode(int);
+ method public void signalEndOfInputStream();
+ method public void start();
+ method public void stop();
field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2
field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
@@ -22246,10 +22276,10 @@
}
public final class MediaCodecInfo {
- method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
- method public final java.lang.String getName();
- method public final java.lang.String[] getSupportedTypes();
- method public final boolean isEncoder();
+ method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String);
+ method public java.lang.String getName();
+ method public java.lang.String[] getSupportedTypes();
+ method public boolean isEncoder();
}
public static final class MediaCodecInfo.AudioCapabilities {
@@ -22269,9 +22299,9 @@
method public int getMaxSupportedInstances();
method public java.lang.String getMimeType();
method public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
- method public final boolean isFeatureRequired(java.lang.String);
- method public final boolean isFeatureSupported(java.lang.String);
- method public final boolean isFormatSupported(android.media.MediaFormat);
+ method public boolean isFeatureRequired(java.lang.String);
+ method public boolean isFeatureSupported(java.lang.String);
+ method public boolean isFormatSupported(android.media.MediaFormat);
field public static final deprecated int COLOR_Format12bitRGB444 = 3; // 0x3
field public static final deprecated int COLOR_Format16bitARGB1555 = 5; // 0x5
field public static final deprecated int COLOR_Format16bitARGB4444 = 4; // 0x4
@@ -22528,11 +22558,11 @@
public final class MediaCodecList {
ctor public MediaCodecList(int);
- method public final java.lang.String findDecoderForFormat(android.media.MediaFormat);
- method public final java.lang.String findEncoderForFormat(android.media.MediaFormat);
- method public static final deprecated int getCodecCount();
- method public static final deprecated android.media.MediaCodecInfo getCodecInfoAt(int);
- method public final android.media.MediaCodecInfo[] getCodecInfos();
+ method public java.lang.String findDecoderForFormat(android.media.MediaFormat);
+ method public java.lang.String findEncoderForFormat(android.media.MediaFormat);
+ method public static deprecated int getCodecCount();
+ method public static deprecated android.media.MediaCodecInfo getCodecInfoAt(int);
+ method public android.media.MediaCodecInfo[] getCodecInfos();
field public static final int ALL_CODECS = 1; // 0x1
field public static final int REGULAR_CODECS = 0; // 0x0
}
@@ -22540,10 +22570,10 @@
public final class MediaCrypto {
ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException;
method protected void finalize();
- method public static final boolean isCryptoSchemeSupported(java.util.UUID);
- method public final void release();
- method public final boolean requiresSecureDecoderComponent(java.lang.String);
- method public final void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
+ method public static boolean isCryptoSchemeSupported(java.util.UUID);
+ method public void release();
+ method public boolean requiresSecureDecoderComponent(java.lang.String);
+ method public void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException;
}
public final class MediaCryptoException extends java.lang.Exception {
@@ -22559,10 +22589,10 @@
public final class MediaDescrambler implements java.lang.AutoCloseable {
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
- method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
+ method public int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
method protected void finalize();
- method public final boolean requiresSecureDecoderComponent(java.lang.String);
- method public final void setMediaCasSession(android.media.MediaCas.Session);
+ method public boolean requiresSecureDecoderComponent(java.lang.String);
+ method public void setMediaCasSession(android.media.MediaCas.Session);
}
public class MediaDescription implements android.os.Parcelable {
@@ -22611,13 +22641,13 @@
method public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
method public byte[] getSecureStop(byte[]);
method public java.util.List<byte[]> getSecureStops();
- method public static final boolean isCryptoSchemeSupported(java.util.UUID);
- method public static final boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
+ method public static boolean isCryptoSchemeSupported(java.util.UUID);
+ method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
- method public final void release();
+ method public void release();
method public void releaseAllSecureStops();
method public void releaseSecureStops(byte[]);
method public void removeKeys(byte[]);
@@ -22710,21 +22740,21 @@
method public int getSampleFlags();
method public long getSampleTime();
method public int getSampleTrackIndex();
- method public final int getTrackCount();
+ method public int getTrackCount();
method public android.media.MediaFormat getTrackFormat(int);
method public boolean hasCacheReachedEndOfStream();
method public int readSampleData(java.nio.ByteBuffer, int);
- method public final void release();
+ method public void release();
method public void seekTo(long, int);
method public void selectTrack(int);
- method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
- method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
- method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
- method public final void setDataSource(java.lang.String) throws java.io.IOException;
- method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
- method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
- method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
- method public final void setMediaCas(android.media.MediaCas);
+ method public void setDataSource(android.media.MediaDataSource) throws java.io.IOException;
+ method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+ method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
+ method public void setDataSource(java.lang.String) throws java.io.IOException;
+ method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+ method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
+ method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
+ method public void setMediaCas(android.media.MediaCas);
method public void unselectTrack(int);
field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2
field public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; // 0x4
@@ -22747,22 +22777,22 @@
public final class MediaFormat {
ctor public MediaFormat();
- method public final boolean containsKey(java.lang.String);
- method public static final android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
- method public static final android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
- method public static final android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
- method public final java.nio.ByteBuffer getByteBuffer(java.lang.String);
+ method public boolean containsKey(java.lang.String);
+ method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
+ method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
+ method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
+ method public java.nio.ByteBuffer getByteBuffer(java.lang.String);
method public boolean getFeatureEnabled(java.lang.String);
- method public final float getFloat(java.lang.String);
- method public final int getInteger(java.lang.String);
- method public final long getLong(java.lang.String);
- method public final java.lang.String getString(java.lang.String);
- method public final void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
+ method public float getFloat(java.lang.String);
+ method public int getInteger(java.lang.String);
+ method public long getLong(java.lang.String);
+ method public java.lang.String getString(java.lang.String);
+ method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
method public void setFeatureEnabled(java.lang.String, boolean);
- method public final void setFloat(java.lang.String, float);
- method public final void setInteger(java.lang.String, int);
- method public final void setLong(java.lang.String, long);
- method public final void setString(java.lang.String, java.lang.String);
+ method public void setFloat(java.lang.String, float);
+ method public void setInteger(java.lang.String, int);
+ method public void setLong(java.lang.String, long);
+ method public void setString(java.lang.String, java.lang.String);
field public static final int COLOR_RANGE_FULL = 1; // 0x1
field public static final int COLOR_RANGE_LIMITED = 2; // 0x2
field public static final int COLOR_STANDARD_BT2020 = 6; // 0x6
@@ -23469,14 +23499,14 @@
public final class MediaSync {
ctor public MediaSync();
- method public final android.view.Surface createInputSurface();
+ method public android.view.Surface createInputSurface();
method protected void finalize();
method public void flush();
method public android.media.PlaybackParams getPlaybackParams();
method public android.media.SyncParams getSyncParams();
method public android.media.MediaTimestamp getTimestamp();
method public void queueAudio(java.nio.ByteBuffer, int, long);
- method public final void release();
+ method public void release();
method public void setAudioTrack(android.media.AudioTrack);
method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler);
@@ -24431,7 +24461,7 @@
public final class MidiInputPort extends android.media.midi.MidiReceiver implements java.io.Closeable {
method public void close() throws java.io.IOException;
- method public final int getPortNumber();
+ method public int getPortNumber();
method public void onSend(byte[], int, int, long) throws java.io.IOException;
}
@@ -24456,7 +24486,7 @@
public final class MidiOutputPort extends android.media.midi.MidiSender implements java.io.Closeable {
method public void close() throws java.io.IOException;
- method public final int getPortNumber();
+ method public int getPortNumber();
method public void onConnect(android.media.midi.MidiReceiver);
method public void onDisconnect(android.media.midi.MidiReceiver);
}
@@ -24731,7 +24761,7 @@
package android.media.tv {
public final class TvContentRating {
- method public final boolean contains(android.media.tv.TvContentRating);
+ method public boolean contains(android.media.tv.TvContentRating);
method public static android.media.tv.TvContentRating createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...);
method public java.lang.String flattenToString();
method public java.lang.String getDomain();
@@ -24781,7 +24811,7 @@
}
public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
- method public static final java.lang.String getVideoResolution(java.lang.String);
+ method public static java.lang.String getVideoResolution(java.lang.String);
field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color";
field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
@@ -25305,18 +25335,18 @@
public final class TvTrackInfo implements android.os.Parcelable {
method public int describeContents();
- method public final int getAudioChannelCount();
- method public final int getAudioSampleRate();
- method public final java.lang.CharSequence getDescription();
- method public final android.os.Bundle getExtra();
- method public final java.lang.String getId();
- method public final java.lang.String getLanguage();
- method public final int getType();
- method public final byte getVideoActiveFormatDescription();
- method public final float getVideoFrameRate();
- method public final int getVideoHeight();
- method public final float getVideoPixelAspectRatio();
- method public final int getVideoWidth();
+ method public int getAudioChannelCount();
+ method public int getAudioSampleRate();
+ method public java.lang.CharSequence getDescription();
+ method public android.os.Bundle getExtra();
+ method public java.lang.String getId();
+ method public java.lang.String getLanguage();
+ method public int getType();
+ method public byte getVideoActiveFormatDescription();
+ method public float getVideoFrameRate();
+ method public int getVideoHeight();
+ method public float getVideoPixelAspectRatio();
+ method public int getVideoWidth();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR;
field public static final int TYPE_AUDIO = 0; // 0x0
@@ -25327,16 +25357,16 @@
public static final class TvTrackInfo.Builder {
ctor public TvTrackInfo.Builder(int, java.lang.String);
method public android.media.tv.TvTrackInfo build();
- method public final android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
- method public final android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
- method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
- method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
- method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
- method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
- method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
- method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
- method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
- method public final android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
+ method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
+ method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
+ method public android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence);
+ method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
+ method public android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String);
+ method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte);
+ method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float);
+ method public android.media.tv.TvTrackInfo.Builder setVideoHeight(int);
+ method public android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float);
+ method public android.media.tv.TvTrackInfo.Builder setVideoWidth(int);
}
public class TvView extends android.view.ViewGroup {
@@ -25567,34 +25597,34 @@
}
public final class MtpObjectInfo {
- method public final int getAssociationDesc();
- method public final int getAssociationType();
- method public final int getCompressedSize();
- method public final long getCompressedSizeLong();
- method public final long getDateCreated();
- method public final long getDateModified();
- method public final int getFormat();
- method public final int getImagePixDepth();
- method public final long getImagePixDepthLong();
- method public final int getImagePixHeight();
- method public final long getImagePixHeightLong();
- method public final int getImagePixWidth();
- method public final long getImagePixWidthLong();
- method public final java.lang.String getKeywords();
- method public final java.lang.String getName();
- method public final int getObjectHandle();
- method public final int getParent();
- method public final int getProtectionStatus();
- method public final int getSequenceNumber();
- method public final long getSequenceNumberLong();
- method public final int getStorageId();
- method public final int getThumbCompressedSize();
- method public final long getThumbCompressedSizeLong();
- method public final int getThumbFormat();
- method public final int getThumbPixHeight();
- method public final long getThumbPixHeightLong();
- method public final int getThumbPixWidth();
- method public final long getThumbPixWidthLong();
+ method public int getAssociationDesc();
+ method public int getAssociationType();
+ method public int getCompressedSize();
+ method public long getCompressedSizeLong();
+ method public long getDateCreated();
+ method public long getDateModified();
+ method public int getFormat();
+ method public int getImagePixDepth();
+ method public long getImagePixDepthLong();
+ method public int getImagePixHeight();
+ method public long getImagePixHeightLong();
+ method public int getImagePixWidth();
+ method public long getImagePixWidthLong();
+ method public java.lang.String getKeywords();
+ method public java.lang.String getName();
+ method public int getObjectHandle();
+ method public int getParent();
+ method public int getProtectionStatus();
+ method public int getSequenceNumber();
+ method public long getSequenceNumberLong();
+ method public int getStorageId();
+ method public int getThumbCompressedSize();
+ method public long getThumbCompressedSizeLong();
+ method public int getThumbFormat();
+ method public int getThumbPixHeight();
+ method public long getThumbPixHeightLong();
+ method public int getThumbPixWidth();
+ method public long getThumbPixWidthLong();
}
public static class MtpObjectInfo.Builder {
@@ -25624,11 +25654,11 @@
}
public final class MtpStorageInfo {
- method public final java.lang.String getDescription();
- method public final long getFreeSpace();
- method public final long getMaxCapacity();
- method public final int getStorageId();
- method public final java.lang.String getVolumeIdentifier();
+ method public java.lang.String getDescription();
+ method public long getFreeSpace();
+ method public long getMaxCapacity();
+ method public int getStorageId();
+ method public java.lang.String getVolumeIdentifier();
}
}
@@ -25686,7 +25716,7 @@
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
field public static final java.lang.String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
- field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+ field public static final deprecated java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
@@ -25781,12 +25811,18 @@
}
public final class IpSecManager {
- method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
- method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public void applyTransportModeTransform(java.net.Socket, int, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.net.DatagramSocket, int, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.io.FileDescriptor, int, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.Socket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.net.DatagramSocket) throws java.io.IOException;
+ method public void removeTransportModeTransforms(java.io.FileDescriptor) throws java.io.IOException;
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
}
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
@@ -25809,18 +25845,15 @@
public final class IpSecTransform implements java.lang.AutoCloseable {
method public void close();
- field public static final int DIRECTION_IN = 0; // 0x0
- field public static final int DIRECTION_OUT = 1; // 0x1
}
public static class IpSecTransform.Builder {
ctor public IpSecTransform.Builder(android.content.Context);
- method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(int, android.net.IpSecAlgorithm);
- method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
- method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthenticatedEncryption(android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setAuthentication(android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(android.net.IpSecAlgorithm);
method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
- method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
}
public class LinkAddress implements android.os.Parcelable {
@@ -25840,7 +25873,9 @@
method public android.net.ProxyInfo getHttpProxy();
method public java.lang.String getInterfaceName();
method public java.util.List<android.net.LinkAddress> getLinkAddresses();
+ method public java.lang.String getPrivateDnsServerName();
method public java.util.List<android.net.RouteInfo> getRoutes();
+ method public boolean isPrivateDnsActive();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
}
@@ -25903,10 +25938,10 @@
}
public final class MacAddress implements android.os.Parcelable {
- method public int addressType();
method public int describeContents();
method public static android.net.MacAddress fromBytes(byte[]);
method public static android.net.MacAddress fromString(java.lang.String);
+ method public int getAddressType();
method public boolean isLocallyAssigned();
method public byte[] toByteArray();
method public java.lang.String toOuiString();
@@ -25916,7 +25951,6 @@
field public static final int TYPE_BROADCAST = 3; // 0x3
field public static final int TYPE_MULTICAST = 2; // 0x2
field public static final int TYPE_UNICAST = 1; // 0x1
- field public static final int TYPE_UNKNOWN = 0; // 0x0
}
public class MailTo {
@@ -25958,6 +25992,7 @@
field public static final int NET_CAPABILITY_CBS = 5; // 0x5
field public static final int NET_CAPABILITY_DUN = 2; // 0x2
field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
+ field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
field public static final int NET_CAPABILITY_IA = 7; // 0x7
field public static final int NET_CAPABILITY_IMS = 4; // 0x4
@@ -25966,6 +26001,7 @@
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
+ field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
field public static final int NET_CAPABILITY_RCS = 8; // 0x8
field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
@@ -26056,10 +26092,10 @@
public final class Proxy {
ctor public Proxy();
- method public static final deprecated java.lang.String getDefaultHost();
- method public static final deprecated int getDefaultPort();
- method public static final deprecated java.lang.String getHost(android.content.Context);
- method public static final deprecated int getPort(android.content.Context);
+ method public static deprecated java.lang.String getDefaultHost();
+ method public static deprecated int getDefaultPort();
+ method public static deprecated java.lang.String getHost(android.content.Context);
+ method public static deprecated int getPort(android.content.Context);
field public static final deprecated java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
}
@@ -27502,10 +27538,14 @@
field public static final java.lang.String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field public static final java.lang.String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
field public static final java.lang.String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
+ field public static final java.lang.String ACTION_TRANSACTION_DETECTED = "android.nfc.action.TRANSACTION_DETECTED";
field public static final java.lang.String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
+ field public static final java.lang.String EXTRA_AID = "android.nfc.extra.AID";
+ field public static final java.lang.String EXTRA_DATA = "android.nfc.extra.DATA";
field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
+ field public static final java.lang.String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
field public static final int FLAG_READER_NFC_A = 1; // 0x1
field public static final int FLAG_READER_NFC_B = 2; // 0x2
@@ -30999,14 +31039,14 @@
}
public final class Debug {
- method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
+ method public static void attachJvmtiAgent(java.lang.String, java.lang.String, java.lang.ClassLoader) throws java.io.IOException;
method public static deprecated void changeDebugPort(int);
method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
method public static void enableEmulatorTraceOutput();
- method public static final int getBinderDeathObjectCount();
- method public static final int getBinderLocalObjectCount();
- method public static final int getBinderProxyObjectCount();
+ method public static int getBinderDeathObjectCount();
+ method public static int getBinderLocalObjectCount();
+ method public static int getBinderProxyObjectCount();
method public static int getBinderReceivedTransactions();
method public static int getBinderSentTransactions();
method public static deprecated int getGlobalAllocCount();
@@ -31414,114 +31454,114 @@
}
public final class Parcel {
- method public final void appendFrom(android.os.Parcel, int, int);
- method public final android.os.IBinder[] createBinderArray();
- method public final java.util.ArrayList<android.os.IBinder> createBinderArrayList();
- method public final boolean[] createBooleanArray();
- method public final byte[] createByteArray();
- method public final char[] createCharArray();
- method public final double[] createDoubleArray();
- method public final float[] createFloatArray();
- method public final int[] createIntArray();
- method public final long[] createLongArray();
- method public final java.lang.String[] createStringArray();
- method public final java.util.ArrayList<java.lang.String> createStringArrayList();
- method public final <T> T[] createTypedArray(android.os.Parcelable.Creator<T>);
- method public final <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>);
- method public final int dataAvail();
- method public final int dataCapacity();
- method public final int dataPosition();
- method public final int dataSize();
- method public final void enforceInterface(java.lang.String);
- method public final boolean hasFileDescriptors();
- method public final byte[] marshall();
+ method public void appendFrom(android.os.Parcel, int, int);
+ method public android.os.IBinder[] createBinderArray();
+ method public java.util.ArrayList<android.os.IBinder> createBinderArrayList();
+ method public boolean[] createBooleanArray();
+ method public byte[] createByteArray();
+ method public char[] createCharArray();
+ method public double[] createDoubleArray();
+ method public float[] createFloatArray();
+ method public int[] createIntArray();
+ method public long[] createLongArray();
+ method public java.lang.String[] createStringArray();
+ method public java.util.ArrayList<java.lang.String> createStringArrayList();
+ method public <T> T[] createTypedArray(android.os.Parcelable.Creator<T>);
+ method public <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>);
+ method public int dataAvail();
+ method public int dataCapacity();
+ method public int dataPosition();
+ method public int dataSize();
+ method public void enforceInterface(java.lang.String);
+ method public boolean hasFileDescriptors();
+ method public byte[] marshall();
method public static android.os.Parcel obtain();
- method public final java.lang.Object[] readArray(java.lang.ClassLoader);
- method public final java.util.ArrayList readArrayList(java.lang.ClassLoader);
- method public final void readBinderArray(android.os.IBinder[]);
- method public final void readBinderList(java.util.List<android.os.IBinder>);
- method public final void readBooleanArray(boolean[]);
- method public final android.os.Bundle readBundle();
- method public final android.os.Bundle readBundle(java.lang.ClassLoader);
- method public final byte readByte();
- method public final void readByteArray(byte[]);
- method public final void readCharArray(char[]);
- method public final double readDouble();
- method public final void readDoubleArray(double[]);
- method public final void readException();
- method public final void readException(int, java.lang.String);
- method public final android.os.ParcelFileDescriptor readFileDescriptor();
- method public final float readFloat();
- method public final void readFloatArray(float[]);
- method public final java.util.HashMap readHashMap(java.lang.ClassLoader);
- method public final int readInt();
- method public final void readIntArray(int[]);
- method public final void readList(java.util.List, java.lang.ClassLoader);
- method public final long readLong();
- method public final void readLongArray(long[]);
- method public final void readMap(java.util.Map, java.lang.ClassLoader);
- method public final <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
- method public final android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
- method public final android.os.PersistableBundle readPersistableBundle();
- method public final android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
- method public final java.io.Serializable readSerializable();
- method public final android.util.Size readSize();
- method public final android.util.SizeF readSizeF();
- method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
- method public final android.util.SparseBooleanArray readSparseBooleanArray();
- method public final java.lang.String readString();
- method public final void readStringArray(java.lang.String[]);
- method public final void readStringList(java.util.List<java.lang.String>);
- method public final android.os.IBinder readStrongBinder();
- method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
- method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
- method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>);
- method public final java.lang.Object readValue(java.lang.ClassLoader);
- method public final void recycle();
- method public final void setDataCapacity(int);
- method public final void setDataPosition(int);
- method public final void setDataSize(int);
- method public final void unmarshall(byte[], int, int);
- method public final void writeArray(java.lang.Object[]);
- method public final void writeBinderArray(android.os.IBinder[]);
- method public final void writeBinderList(java.util.List<android.os.IBinder>);
- method public final void writeBooleanArray(boolean[]);
- method public final void writeBundle(android.os.Bundle);
- method public final void writeByte(byte);
- method public final void writeByteArray(byte[]);
- method public final void writeByteArray(byte[], int, int);
- method public final void writeCharArray(char[]);
- method public final void writeDouble(double);
- method public final void writeDoubleArray(double[]);
- method public final void writeException(java.lang.Exception);
- method public final void writeFileDescriptor(java.io.FileDescriptor);
- method public final void writeFloat(float);
- method public final void writeFloatArray(float[]);
- method public final void writeInt(int);
- method public final void writeIntArray(int[]);
- method public final void writeInterfaceToken(java.lang.String);
- method public final void writeList(java.util.List);
- method public final void writeLong(long);
- method public final void writeLongArray(long[]);
- method public final void writeMap(java.util.Map);
- method public final void writeNoException();
- method public final void writeParcelable(android.os.Parcelable, int);
- method public final <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
- method public final void writePersistableBundle(android.os.PersistableBundle);
- method public final void writeSerializable(java.io.Serializable);
- method public final void writeSize(android.util.Size);
- method public final void writeSizeF(android.util.SizeF);
- method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
- method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
- method public final void writeString(java.lang.String);
- method public final void writeStringArray(java.lang.String[]);
- method public final void writeStringList(java.util.List<java.lang.String>);
- method public final void writeStrongBinder(android.os.IBinder);
- method public final void writeStrongInterface(android.os.IInterface);
- method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int);
- method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
- method public final <T extends android.os.Parcelable> void writeTypedObject(T, int);
- method public final void writeValue(java.lang.Object);
+ method public java.lang.Object[] readArray(java.lang.ClassLoader);
+ method public java.util.ArrayList readArrayList(java.lang.ClassLoader);
+ method public void readBinderArray(android.os.IBinder[]);
+ method public void readBinderList(java.util.List<android.os.IBinder>);
+ method public void readBooleanArray(boolean[]);
+ method public android.os.Bundle readBundle();
+ method public android.os.Bundle readBundle(java.lang.ClassLoader);
+ method public byte readByte();
+ method public void readByteArray(byte[]);
+ method public void readCharArray(char[]);
+ method public double readDouble();
+ method public void readDoubleArray(double[]);
+ method public void readException();
+ method public void readException(int, java.lang.String);
+ method public android.os.ParcelFileDescriptor readFileDescriptor();
+ method public float readFloat();
+ method public void readFloatArray(float[]);
+ method public java.util.HashMap readHashMap(java.lang.ClassLoader);
+ method public int readInt();
+ method public void readIntArray(int[]);
+ method public void readList(java.util.List, java.lang.ClassLoader);
+ method public long readLong();
+ method public void readLongArray(long[]);
+ method public void readMap(java.util.Map, java.lang.ClassLoader);
+ method public <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
+ method public android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
+ method public android.os.PersistableBundle readPersistableBundle();
+ method public android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
+ method public java.io.Serializable readSerializable();
+ method public android.util.Size readSize();
+ method public android.util.SizeF readSizeF();
+ method public android.util.SparseArray readSparseArray(java.lang.ClassLoader);
+ method public android.util.SparseBooleanArray readSparseBooleanArray();
+ method public java.lang.String readString();
+ method public void readStringArray(java.lang.String[]);
+ method public void readStringList(java.util.List<java.lang.String>);
+ method public android.os.IBinder readStrongBinder();
+ method public <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
+ method public <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
+ method public <T> T readTypedObject(android.os.Parcelable.Creator<T>);
+ method public java.lang.Object readValue(java.lang.ClassLoader);
+ method public void recycle();
+ method public void setDataCapacity(int);
+ method public void setDataPosition(int);
+ method public void setDataSize(int);
+ method public void unmarshall(byte[], int, int);
+ method public void writeArray(java.lang.Object[]);
+ method public void writeBinderArray(android.os.IBinder[]);
+ method public void writeBinderList(java.util.List<android.os.IBinder>);
+ method public void writeBooleanArray(boolean[]);
+ method public void writeBundle(android.os.Bundle);
+ method public void writeByte(byte);
+ method public void writeByteArray(byte[]);
+ method public void writeByteArray(byte[], int, int);
+ method public void writeCharArray(char[]);
+ method public void writeDouble(double);
+ method public void writeDoubleArray(double[]);
+ method public void writeException(java.lang.Exception);
+ method public void writeFileDescriptor(java.io.FileDescriptor);
+ method public void writeFloat(float);
+ method public void writeFloatArray(float[]);
+ method public void writeInt(int);
+ method public void writeIntArray(int[]);
+ method public void writeInterfaceToken(java.lang.String);
+ method public void writeList(java.util.List);
+ method public void writeLong(long);
+ method public void writeLongArray(long[]);
+ method public void writeMap(java.util.Map);
+ method public void writeNoException();
+ method public void writeParcelable(android.os.Parcelable, int);
+ method public <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
+ method public void writePersistableBundle(android.os.PersistableBundle);
+ method public void writeSerializable(java.io.Serializable);
+ method public void writeSize(android.util.Size);
+ method public void writeSizeF(android.util.SizeF);
+ method public void writeSparseArray(android.util.SparseArray<java.lang.Object>);
+ method public void writeSparseBooleanArray(android.util.SparseBooleanArray);
+ method public void writeString(java.lang.String);
+ method public void writeStringArray(java.lang.String[]);
+ method public void writeStringList(java.util.List<java.lang.String>);
+ method public void writeStrongBinder(android.os.IBinder);
+ method public void writeStrongInterface(android.os.IInterface);
+ method public <T extends android.os.Parcelable> void writeTypedArray(T[], int);
+ method public <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
+ method public <T extends android.os.Parcelable> void writeTypedObject(T, int);
+ method public void writeValue(java.lang.Object);
field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
}
@@ -33088,7 +33128,7 @@
}
public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
field public static final android.net.Uri CONTENT_URI;
}
@@ -33217,7 +33257,7 @@
}
public static final class CalendarContract.EventDays implements android.provider.CalendarContract.EventDaysColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]);
field public static final android.net.Uri CONTENT_URI;
}
@@ -33310,8 +33350,8 @@
}
public static final class CalendarContract.Instances implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long);
- method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String);
+ method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long);
+ method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String);
field public static final java.lang.String BEGIN = "begin";
field public static final android.net.Uri CONTENT_BY_DAY_URI;
field public static final android.net.Uri CONTENT_SEARCH_BY_DAY_URI;
@@ -33326,7 +33366,7 @@
}
public static final class CalendarContract.Reminders implements android.provider.BaseColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.RemindersColumns {
- method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]);
field public static final android.net.Uri CONTENT_URI;
}
@@ -33434,7 +33474,7 @@
method public static deprecated java.lang.Object decodeImProtocol(java.lang.String);
method public static deprecated java.lang.String encodeCustomImProtocol(java.lang.String);
method public static deprecated java.lang.String encodePredefinedImProtocol(int);
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence);
field public static final deprecated java.lang.String CONTENT_EMAIL_ITEM_TYPE = "vnd.android.cursor.item/email";
field public static final deprecated java.lang.String CONTENT_EMAIL_TYPE = "vnd.android.cursor.dir/email";
field public static final deprecated android.net.Uri CONTENT_EMAIL_URI;
@@ -33584,7 +33624,7 @@
}
public static final deprecated class Contacts.Organizations implements android.provider.BaseColumns android.provider.Contacts.OrganizationColumns {
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
field public static final deprecated java.lang.String CONTENT_DIRECTORY = "organizations";
field public static final deprecated android.net.Uri CONTENT_URI;
field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "company, title, isprimary ASC";
@@ -33641,8 +33681,8 @@
}
public static final deprecated class Contacts.Phones implements android.provider.BaseColumns android.provider.Contacts.PeopleColumns android.provider.Contacts.PhonesColumns {
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]);
- method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]);
+ method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence);
field public static final deprecated android.net.Uri CONTENT_FILTER_URL;
field public static final deprecated java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone";
field public static final deprecated java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone";
@@ -33782,8 +33822,8 @@
}
public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String ADDRESS = "data1";
field public static final android.net.Uri CONTENT_FILTER_URI;
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2";
@@ -33803,7 +33843,7 @@
}
public static final class ContactsContract.CommonDataKinds.Event implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
method public static int getTypeResource(java.lang.Integer);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_event";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -33834,10 +33874,10 @@
}
public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getProtocolLabelResource(int);
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getProtocolLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
field public static final java.lang.String CUSTOM_PROTOCOL = "data6";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
@@ -33882,8 +33922,8 @@
}
public static final class ContactsContract.CommonDataKinds.Organization implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String COMPANY = "data1";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
field public static final java.lang.String DEPARTMENT = "data5";
@@ -33901,8 +33941,8 @@
}
public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final android.net.Uri CONTENT_FILTER_URI;
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone_v2";
@@ -33947,8 +33987,8 @@
}
public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -33971,8 +34011,8 @@
}
public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
@@ -34002,8 +34042,8 @@
}
public static final class ContactsContract.CommonDataKinds.StructuredPostal implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
- method public static final int getTypeLabelResource(int);
+ method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
+ method public static int getTypeLabelResource(int);
field public static final java.lang.String CITY = "data7";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address_v2";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/postal-address_v2";
@@ -34828,7 +34868,7 @@
public static final class MediaStore.Audio.Artists.Albums implements android.provider.MediaStore.Audio.AlbumColumns {
ctor public MediaStore.Audio.Artists.Albums();
- method public static final android.net.Uri getContentUri(java.lang.String, long);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
}
public static abstract interface MediaStore.Audio.AudioColumns implements android.provider.MediaStore.MediaColumns {
@@ -34864,7 +34904,7 @@
public static final class MediaStore.Audio.Genres.Members implements android.provider.MediaStore.Audio.AudioColumns {
ctor public MediaStore.Audio.Genres.Members();
- method public static final android.net.Uri getContentUri(java.lang.String, long);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
field public static final java.lang.String AUDIO_ID = "audio_id";
field public static final java.lang.String CONTENT_DIRECTORY = "members";
field public static final java.lang.String DEFAULT_SORT_ORDER = "title_key";
@@ -34900,8 +34940,8 @@
public static final class MediaStore.Audio.Playlists.Members implements android.provider.MediaStore.Audio.AudioColumns {
ctor public MediaStore.Audio.Playlists.Members();
- method public static final android.net.Uri getContentUri(java.lang.String, long);
- method public static final boolean moveItem(android.content.ContentResolver, long, int, int);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
+ method public static boolean moveItem(android.content.ContentResolver, long, int, int);
field public static final java.lang.String AUDIO_ID = "audio_id";
field public static final java.lang.String CONTENT_DIRECTORY = "members";
field public static final java.lang.String DEFAULT_SORT_ORDER = "play_order";
@@ -34924,7 +34964,7 @@
public static final class MediaStore.Files {
ctor public MediaStore.Files();
method public static android.net.Uri getContentUri(java.lang.String);
- method public static final android.net.Uri getContentUri(java.lang.String, long);
+ method public static android.net.Uri getContentUri(java.lang.String, long);
}
public static abstract interface MediaStore.Files.FileColumns implements android.provider.MediaStore.MediaColumns {
@@ -34958,13 +34998,13 @@
public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
ctor public MediaStore.Images.Media();
- method public static final android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
+ method public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
method public static android.net.Uri getContentUri(java.lang.String);
- method public static final java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
- method public static final java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String);
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String);
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+ method public static java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ method public static java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/image";
field public static final java.lang.String DEFAULT_SORT_ORDER = "bucket_display_name";
field public static final android.net.Uri EXTERNAL_CONTENT_URI;
@@ -35009,7 +35049,7 @@
public static final class MediaStore.Video {
ctor public MediaStore.Video();
- method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
+ method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
field public static final java.lang.String DEFAULT_SORT_ORDER = "_display_name";
}
@@ -35238,12 +35278,12 @@
method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static java.lang.String getString(android.content.ContentResolver, java.lang.String);
method public static android.net.Uri getUriFor(java.lang.String);
- method public static final deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String);
+ method public static deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String);
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
- method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
+ method public static deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled";
field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
field public static final deprecated java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
@@ -35382,6 +35422,7 @@
field public static final deprecated java.lang.String RADIO_NFC = "nfc";
field public static final deprecated java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String RINGTONE = "ringtone";
+ field public static final java.lang.String RTT_CALLING_MODE = "rtt_calling_mode";
field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness";
field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
field public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; // 0x1
@@ -35513,10 +35554,17 @@
field public static final java.lang.String ADDRESS = "address";
}
+ public static final class Telephony.CarrierIdentification implements android.provider.BaseColumns {
+ method public static android.net.Uri getUriForSubscriptionId(int);
+ field public static final java.lang.String CID = "carrier_id";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String NAME = "carrier_name";
+ }
+
public static final class Telephony.Carriers implements android.provider.BaseColumns {
field public static final java.lang.String APN = "apn";
field public static final java.lang.String AUTH_TYPE = "authtype";
- field public static final java.lang.String BEARER = "bearer";
+ field public static final deprecated java.lang.String BEARER = "bearer";
field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String CURRENT = "current";
@@ -35529,6 +35577,7 @@
field public static final java.lang.String MVNO_MATCH_DATA = "mvno_match_data";
field public static final java.lang.String MVNO_TYPE = "mvno_type";
field public static final java.lang.String NAME = "name";
+ field public static final java.lang.String NETWORK_TYPE_BITMASK = "network_type_bitmask";
field public static final java.lang.String NUMERIC = "numeric";
field public static final java.lang.String PASSWORD = "password";
field public static final java.lang.String PORT = "port";
@@ -35682,6 +35731,7 @@
field public static final int RESULT_SMS_HANDLED = 1; // 0x1
field public static final int RESULT_SMS_OUT_OF_MEMORY = 3; // 0x3
field public static final int RESULT_SMS_UNSUPPORTED = 4; // 0x4
+ field public static final java.lang.String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
field public static final java.lang.String SIM_FULL_ACTION = "android.provider.Telephony.SIM_FULL";
field public static final java.lang.String SMS_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_CB_RECEIVED";
field public static final java.lang.String SMS_DELIVER_ACTION = "android.provider.Telephony.SMS_DELIVER";
@@ -36970,6 +37020,54 @@
}
+package android.se.omapi {
+
+ public class Channel {
+ method public void close();
+ method public byte[] getSelectResponse();
+ method public android.se.omapi.Session getSession();
+ method public boolean isBasicChannel();
+ method public boolean isClosed();
+ method public boolean selectNext() throws java.io.IOException;
+ method public byte[] transmit(byte[]) throws java.io.IOException;
+ }
+
+ public class Reader {
+ method public void closeSessions();
+ method public java.lang.String getName();
+ method public android.se.omapi.SEService getSEService();
+ method public boolean isSecureElementPresent();
+ method public android.se.omapi.Session openSession() throws java.io.IOException;
+ }
+
+ public class SEService {
+ ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener);
+ method public android.se.omapi.Reader[] getReaders();
+ method public java.lang.String getVersion();
+ method public boolean isConnected();
+ method public void shutdown();
+ }
+
+ public static abstract class SEService.SecureElementListener extends android.os.Binder {
+ ctor public SEService.SecureElementListener();
+ method public android.os.IBinder asBinder();
+ method public void serviceConnected();
+ }
+
+ public class Session {
+ method public void close();
+ method public void closeChannels();
+ method public byte[] getATR();
+ method public android.se.omapi.Reader getReader();
+ method public boolean isClosed();
+ method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
+ method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
+ method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
+ method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
+ }
+
+}
+
package android.security {
public final class KeyChain {
@@ -37410,6 +37508,7 @@
public class CarrierIdentifier implements android.os.Parcelable {
ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ ctor public CarrierIdentifier(byte[], java.lang.String, java.lang.String);
method public int describeContents();
method public java.lang.String getGid1();
method public java.lang.String getGid2();
@@ -38362,11 +38461,6 @@
field public final int errno;
}
- public class Int32Ref {
- ctor public Int32Ref(int);
- field public int value;
- }
-
public class Int64Ref {
ctor public Int64Ref(long);
field public long value;
@@ -38466,7 +38560,6 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39032,6 +39125,7 @@
public final class Call {
method public void answer(int);
method public void conference(android.telecom.Call);
+ method public void deflect(android.net.Uri);
method public void disconnect();
method public java.util.List<java.lang.String> getCannedTextResponses();
method public java.util.List<android.telecom.Call> getChildren();
@@ -39050,12 +39144,12 @@
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
method public void pullExternalCall();
- method public final void putExtras(android.os.Bundle);
+ method public void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, java.lang.String);
- method public final void removeExtras(java.util.List<java.lang.String>);
- method public final void removeExtras(java.lang.String...);
+ method public void removeExtras(java.util.List<java.lang.String>);
+ method public void removeExtras(java.lang.String...);
method public void respondToRttRequest(int, boolean);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
method public void sendRttRequest();
@@ -39142,6 +39236,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ 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 PROPERTY_CONFERENCE = 1; // 0x1
@@ -39151,6 +39246,7 @@
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+ field public static final int PROPERTY_RTT = 1024; // 0x400
field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
field public static final int PROPERTY_WIFI = 8; // 0x8
}
@@ -39244,8 +39340,8 @@
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
- method public final void setConnectionElapsedTime(long);
method public final void setConnectionProperties(int);
+ method public final void setConnectionStartElapsedRealTime(long);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -39281,14 +39377,17 @@
method public final int getState();
method public final android.telecom.StatusHints getStatusHints();
method public final android.telecom.Connection.VideoProvider getVideoProvider();
+ method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream);
method public final boolean isRingbackRequested();
method public void onAbort();
method public void onAnswer(int);
method public void onAnswer();
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEvent(java.lang.String, android.os.Bundle);
+ method public void onDeflect(android.net.Uri);
method public void onDisconnect();
method public void onExtrasChanged(android.os.Bundle);
+ method public void onHandoverComplete();
method public void onHold();
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
@@ -39297,8 +39396,10 @@
method public void onReject(java.lang.String);
method public void onSeparate();
method public void onShowIncomingCallUi();
+ method public void onStartRtt(android.telecom.Connection.RttTextStream);
method public void onStateChanged(int);
method public void onStopDtmfTone();
+ method public void onStopRtt();
method public void onUnhold();
method public static java.lang.String propertiesToString(int);
method public final void putExtras(android.os.Bundle);
@@ -39306,6 +39407,10 @@
method public final void removeExtras(java.lang.String...);
method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
+ method public final void sendRemoteRttRequest();
+ method public final void sendRttInitiationFailure(int);
+ method public final void sendRttInitiationSuccess();
+ method public final void sendRttSessionRemotelyTerminated();
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
@@ -39348,6 +39453,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ field public static final int CAPABILITY_SUPPORT_DEFLECT = 33554432; // 0x2000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
@@ -39359,6 +39465,7 @@
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+ field public static final int PROPERTY_IS_RTT = 256; // 0x100
field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
@@ -39378,6 +39485,12 @@
field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
}
+ public static final class Connection.RttTextStream {
+ method public java.lang.String read() throws java.io.IOException;
+ method public java.lang.String readImmediately() throws java.io.IOException;
+ method public void write(java.lang.String) throws java.io.IOException;
+ }
+
public static abstract class Connection.VideoProvider {
ctor public Connection.VideoProvider();
method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -39418,7 +39531,9 @@
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public android.os.Bundle getExtras();
+ method public android.telecom.Connection.RttTextStream getRttTextStream();
method public int getVideoState();
+ method public boolean isRequestingRtt();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
}
@@ -39428,12 +39543,15 @@
method public final void addConference(android.telecom.Conference);
method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection);
method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
+ method public final void connectionServiceFocusReleased();
method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public final java.util.Collection<android.telecom.Conference> getAllConferences();
method public final java.util.Collection<android.telecom.Connection> getAllConnections();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
+ method public void onConnectionServiceFocusGained();
+ method public void onConnectionServiceFocusLost();
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -39602,23 +39720,23 @@
public final class RemoteConference {
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
- method public final int getConnectionCapabilities();
- method public final int getConnectionProperties();
- method public final java.util.List<android.telecom.RemoteConnection> getConnections();
+ method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
+ method public java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
- method public final android.os.Bundle getExtras();
- method public final int getState();
+ method public android.os.Bundle getExtras();
+ method public int getState();
method public void hold();
method public void merge();
method public void playDtmfTone(char);
- method public final void registerCallback(android.telecom.RemoteConference.Callback);
- method public final void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
+ method public void registerCallback(android.telecom.RemoteConference.Callback);
+ method public void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler);
method public void separate(android.telecom.RemoteConnection);
method public void setCallAudioState(android.telecom.CallAudioState);
method public void stopDtmfTone();
method public void swap();
method public void unhold();
- method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
+ method public void unregisterCallback(android.telecom.RemoteConference.Callback);
}
public static abstract class RemoteConference.Callback {
@@ -39647,10 +39765,10 @@
method public int getConnectionCapabilities();
method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
- method public final android.os.Bundle getExtras();
+ method public android.os.Bundle getExtras();
method public int getState();
method public android.telecom.StatusHints getStatusHints();
- method public final android.telecom.RemoteConnection.VideoProvider getVideoProvider();
+ method public android.telecom.RemoteConnection.VideoProvider getVideoProvider();
method public int getVideoState();
method public void hold();
method public boolean isRingbackRequested();
@@ -39833,6 +39951,114 @@
package android.telephony {
+ public final class AccessNetworkConstants {
+ ctor public AccessNetworkConstants();
+ }
+
+ public static final class AccessNetworkConstants.AccessNetworkType {
+ ctor public AccessNetworkConstants.AccessNetworkType();
+ field public static final int CDMA2000 = 4; // 0x4
+ field public static final int EUTRAN = 3; // 0x3
+ field public static final int GERAN = 1; // 0x1
+ field public static final int IWLAN = 5; // 0x5
+ field public static final int UNKNOWN = 0; // 0x0
+ field public static final int UTRAN = 2; // 0x2
+ }
+
+ public static final class AccessNetworkConstants.EutranBand {
+ ctor public AccessNetworkConstants.EutranBand();
+ field public static final int BAND_1 = 1; // 0x1
+ field public static final int BAND_10 = 10; // 0xa
+ field public static final int BAND_11 = 11; // 0xb
+ field public static final int BAND_12 = 12; // 0xc
+ field public static final int BAND_13 = 13; // 0xd
+ field public static final int BAND_14 = 14; // 0xe
+ field public static final int BAND_17 = 17; // 0x11
+ field public static final int BAND_18 = 18; // 0x12
+ field public static final int BAND_19 = 19; // 0x13
+ field public static final int BAND_2 = 2; // 0x2
+ field public static final int BAND_20 = 20; // 0x14
+ field public static final int BAND_21 = 21; // 0x15
+ field public static final int BAND_22 = 22; // 0x16
+ field public static final int BAND_23 = 23; // 0x17
+ field public static final int BAND_24 = 24; // 0x18
+ field public static final int BAND_25 = 25; // 0x19
+ field public static final int BAND_26 = 26; // 0x1a
+ field public static final int BAND_27 = 27; // 0x1b
+ field public static final int BAND_28 = 28; // 0x1c
+ field public static final int BAND_3 = 3; // 0x3
+ field public static final int BAND_30 = 30; // 0x1e
+ field public static final int BAND_31 = 31; // 0x1f
+ field public static final int BAND_33 = 33; // 0x21
+ field public static final int BAND_34 = 34; // 0x22
+ field public static final int BAND_35 = 35; // 0x23
+ field public static final int BAND_36 = 36; // 0x24
+ field public static final int BAND_37 = 37; // 0x25
+ field public static final int BAND_38 = 38; // 0x26
+ field public static final int BAND_39 = 39; // 0x27
+ field public static final int BAND_4 = 4; // 0x4
+ field public static final int BAND_40 = 40; // 0x28
+ field public static final int BAND_41 = 41; // 0x29
+ field public static final int BAND_42 = 42; // 0x2a
+ field public static final int BAND_43 = 43; // 0x2b
+ field public static final int BAND_44 = 44; // 0x2c
+ field public static final int BAND_45 = 45; // 0x2d
+ field public static final int BAND_46 = 46; // 0x2e
+ field public static final int BAND_47 = 47; // 0x2f
+ field public static final int BAND_48 = 48; // 0x30
+ field public static final int BAND_5 = 5; // 0x5
+ field public static final int BAND_6 = 6; // 0x6
+ field public static final int BAND_65 = 65; // 0x41
+ field public static final int BAND_66 = 66; // 0x42
+ field public static final int BAND_68 = 68; // 0x44
+ field public static final int BAND_7 = 7; // 0x7
+ field public static final int BAND_70 = 70; // 0x46
+ field public static final int BAND_8 = 8; // 0x8
+ field public static final int BAND_9 = 9; // 0x9
+ }
+
+ public static final class AccessNetworkConstants.GeranBand {
+ ctor public AccessNetworkConstants.GeranBand();
+ field public static final int BAND_450 = 3; // 0x3
+ field public static final int BAND_480 = 4; // 0x4
+ field public static final int BAND_710 = 5; // 0x5
+ field public static final int BAND_750 = 6; // 0x6
+ field public static final int BAND_850 = 8; // 0x8
+ field public static final int BAND_DCS1800 = 12; // 0xc
+ field public static final int BAND_E900 = 10; // 0xa
+ field public static final int BAND_ER900 = 14; // 0xe
+ field public static final int BAND_P900 = 9; // 0x9
+ field public static final int BAND_PCS1900 = 13; // 0xd
+ field public static final int BAND_R900 = 11; // 0xb
+ field public static final int BAND_T380 = 1; // 0x1
+ field public static final int BAND_T410 = 2; // 0x2
+ field public static final int BAND_T810 = 7; // 0x7
+ }
+
+ public static final class AccessNetworkConstants.UtranBand {
+ ctor public AccessNetworkConstants.UtranBand();
+ field public static final int BAND_1 = 1; // 0x1
+ field public static final int BAND_10 = 10; // 0xa
+ field public static final int BAND_11 = 11; // 0xb
+ field public static final int BAND_12 = 12; // 0xc
+ field public static final int BAND_13 = 13; // 0xd
+ field public static final int BAND_14 = 14; // 0xe
+ field public static final int BAND_19 = 19; // 0x13
+ field public static final int BAND_2 = 2; // 0x2
+ field public static final int BAND_20 = 20; // 0x14
+ field public static final int BAND_21 = 21; // 0x15
+ field public static final int BAND_22 = 22; // 0x16
+ field public static final int BAND_25 = 25; // 0x19
+ field public static final int BAND_26 = 26; // 0x1a
+ field public static final int BAND_3 = 3; // 0x3
+ field public static final int BAND_4 = 4; // 0x4
+ field public static final int BAND_5 = 5; // 0x5
+ field public static final int BAND_6 = 6; // 0x6
+ field public static final int BAND_7 = 7; // 0x7
+ field public static final int BAND_8 = 8; // 0x8
+ field public static final int BAND_9 = 9; // 0x9
+ }
+
public class CarrierConfigManager {
method public android.os.PersistableBundle getConfig();
method public android.os.PersistableBundle getConfigForSubId(int);
@@ -39840,6 +40066,8 @@
method public void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
+ field public static final java.lang.String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+ field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -39881,6 +40109,7 @@
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
+ field public static final java.lang.String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
@@ -39980,8 +40209,13 @@
field public static final java.lang.String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
}
- public final class CellIdentityCdma implements android.os.Parcelable {
+ public abstract class CellIdentity 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.CellIdentity> CREATOR;
+ }
+
+ public final class CellIdentityCdma extends android.telephony.CellIdentity {
method public int getBasestationId();
method public int getLatitude();
method public int getLongitude();
@@ -39993,8 +40227,7 @@
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
}
- public final class CellIdentityGsm implements android.os.Parcelable {
- method public int describeContents();
+ public final class CellIdentityGsm extends android.telephony.CellIdentity {
method public int getArfcn();
method public int getBsic();
method public int getCid();
@@ -40011,8 +40244,8 @@
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
}
- public final class CellIdentityLte implements android.os.Parcelable {
- method public int describeContents();
+ public final class CellIdentityLte extends android.telephony.CellIdentity {
+ method public int getBandwidth();
method public int getCi();
method public int getEarfcn();
method public deprecated int getMcc();
@@ -40028,8 +40261,17 @@
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityLte> CREATOR;
}
- public final class CellIdentityWcdma implements android.os.Parcelable {
- method public int describeContents();
+ public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
+ method public int getCid();
+ method public int getCpid();
+ method public int getLac();
+ method public java.lang.String getMccStr();
+ method public java.lang.String getMncStr();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
+ }
+
+ public final class CellIdentityWcdma extends android.telephony.CellIdentity {
method public int getCid();
method public int getLac();
method public deprecated int getMcc();
@@ -40047,8 +40289,13 @@
public abstract class CellInfo implements android.os.Parcelable {
method public int describeContents();
+ method public int getCellConnectionStatus();
method public long getTimeStamp();
method public boolean isRegistered();
+ field public static final int CONNECTION_NONE = 0; // 0x0
+ field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+ field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+ field public static final int CONNECTION_UNKNOWN = 2147483647; // 0x7fffffff
field public static final android.os.Parcelable.Creator<android.telephony.CellInfo> CREATOR;
}
@@ -40165,13 +40412,13 @@
public class MbmsDownloadSession implements java.lang.AutoCloseable {
method public int cancelDownload(android.telephony.mbms.DownloadRequest);
method public void close();
- method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
- method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback);
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback);
method public int download(android.telephony.mbms.DownloadRequest);
- method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
method public java.io.File getTempFileRootDirectory();
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
- method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+ method public int registerStateCallback(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStateCallback);
+ method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
method public void requestUpdateFileServices(java.util.List<java.lang.String>);
method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
method public void setTempFileRootDirectory(java.io.File);
@@ -40198,10 +40445,10 @@
public class MbmsStreamingSession implements java.lang.AutoCloseable {
method public void close();
- method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, int, android.os.Handler);
- method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler);
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsStreamingSessionCallback);
method public void requestUpdateStreamingServices(java.util.List<java.lang.String>);
- method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler);
+ method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
public class NeighboringCellInfo implements android.os.Parcelable {
@@ -40330,6 +40577,7 @@
method public void onServiceStateChanged(android.telephony.ServiceState);
method public deprecated void onSignalStrengthChanged(int);
method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
+ method public void onUserMobileDataStateChanged(boolean);
field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
field public static final int LISTEN_CALL_STATE = 32; // 0x20
field public static final int LISTEN_CELL_INFO = 1024; // 0x400
@@ -40341,6 +40589,7 @@
field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
field public static final deprecated int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
+ field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
}
public final class RadioAccessSpecifier implements android.os.Parcelable {
@@ -40353,123 +40602,23 @@
field public static final android.os.Parcelable.Creator<android.telephony.RadioAccessSpecifier> CREATOR;
}
- public final class RadioNetworkConstants {
- ctor public RadioNetworkConstants();
- }
-
- public static final class RadioNetworkConstants.EutranBands {
- ctor public RadioNetworkConstants.EutranBands();
- field public static final int BAND_1 = 1; // 0x1
- field public static final int BAND_10 = 10; // 0xa
- field public static final int BAND_11 = 11; // 0xb
- field public static final int BAND_12 = 12; // 0xc
- field public static final int BAND_13 = 13; // 0xd
- field public static final int BAND_14 = 14; // 0xe
- field public static final int BAND_17 = 17; // 0x11
- field public static final int BAND_18 = 18; // 0x12
- field public static final int BAND_19 = 19; // 0x13
- field public static final int BAND_2 = 2; // 0x2
- field public static final int BAND_20 = 20; // 0x14
- field public static final int BAND_21 = 21; // 0x15
- field public static final int BAND_22 = 22; // 0x16
- field public static final int BAND_23 = 23; // 0x17
- field public static final int BAND_24 = 24; // 0x18
- field public static final int BAND_25 = 25; // 0x19
- field public static final int BAND_26 = 26; // 0x1a
- field public static final int BAND_27 = 27; // 0x1b
- field public static final int BAND_28 = 28; // 0x1c
- field public static final int BAND_3 = 3; // 0x3
- field public static final int BAND_30 = 30; // 0x1e
- field public static final int BAND_31 = 31; // 0x1f
- field public static final int BAND_33 = 33; // 0x21
- field public static final int BAND_34 = 34; // 0x22
- field public static final int BAND_35 = 35; // 0x23
- field public static final int BAND_36 = 36; // 0x24
- field public static final int BAND_37 = 37; // 0x25
- field public static final int BAND_38 = 38; // 0x26
- field public static final int BAND_39 = 39; // 0x27
- field public static final int BAND_4 = 4; // 0x4
- field public static final int BAND_40 = 40; // 0x28
- field public static final int BAND_41 = 41; // 0x29
- field public static final int BAND_42 = 42; // 0x2a
- field public static final int BAND_43 = 43; // 0x2b
- field public static final int BAND_44 = 44; // 0x2c
- field public static final int BAND_45 = 45; // 0x2d
- field public static final int BAND_46 = 46; // 0x2e
- field public static final int BAND_47 = 47; // 0x2f
- field public static final int BAND_48 = 48; // 0x30
- field public static final int BAND_5 = 5; // 0x5
- field public static final int BAND_6 = 6; // 0x6
- field public static final int BAND_65 = 65; // 0x41
- field public static final int BAND_66 = 66; // 0x42
- field public static final int BAND_68 = 68; // 0x44
- field public static final int BAND_7 = 7; // 0x7
- field public static final int BAND_70 = 70; // 0x46
- field public static final int BAND_8 = 8; // 0x8
- field public static final int BAND_9 = 9; // 0x9
- }
-
- public static final class RadioNetworkConstants.GeranBands {
- ctor public RadioNetworkConstants.GeranBands();
- field public static final int BAND_450 = 3; // 0x3
- field public static final int BAND_480 = 4; // 0x4
- field public static final int BAND_710 = 5; // 0x5
- field public static final int BAND_750 = 6; // 0x6
- field public static final int BAND_850 = 8; // 0x8
- field public static final int BAND_DCS1800 = 12; // 0xc
- field public static final int BAND_E900 = 10; // 0xa
- field public static final int BAND_ER900 = 14; // 0xe
- field public static final int BAND_P900 = 9; // 0x9
- field public static final int BAND_PCS1900 = 13; // 0xd
- field public static final int BAND_R900 = 11; // 0xb
- field public static final int BAND_T380 = 1; // 0x1
- field public static final int BAND_T410 = 2; // 0x2
- field public static final int BAND_T810 = 7; // 0x7
- }
-
- public static final class RadioNetworkConstants.RadioAccessNetworks {
- ctor public RadioNetworkConstants.RadioAccessNetworks();
- field public static final int EUTRAN = 3; // 0x3
- field public static final int GERAN = 1; // 0x1
- field public static final int UTRAN = 2; // 0x2
- }
-
- public static final class RadioNetworkConstants.UtranBands {
- ctor public RadioNetworkConstants.UtranBands();
- field public static final int BAND_1 = 1; // 0x1
- field public static final int BAND_10 = 10; // 0xa
- field public static final int BAND_11 = 11; // 0xb
- field public static final int BAND_12 = 12; // 0xc
- field public static final int BAND_13 = 13; // 0xd
- field public static final int BAND_14 = 14; // 0xe
- field public static final int BAND_19 = 19; // 0x13
- field public static final int BAND_2 = 2; // 0x2
- field public static final int BAND_20 = 20; // 0x14
- field public static final int BAND_21 = 21; // 0x15
- field public static final int BAND_22 = 22; // 0x16
- field public static final int BAND_25 = 25; // 0x19
- field public static final int BAND_26 = 26; // 0x1a
- field public static final int BAND_3 = 3; // 0x3
- field public static final int BAND_4 = 4; // 0x4
- field public static final int BAND_5 = 5; // 0x5
- field public static final int BAND_6 = 6; // 0x6
- field public static final int BAND_7 = 7; // 0x7
- field public static final int BAND_8 = 8; // 0x8
- field public static final int BAND_9 = 9; // 0x9
- }
-
public class ServiceState implements android.os.Parcelable {
ctor public ServiceState();
ctor public ServiceState(android.telephony.ServiceState);
ctor public ServiceState(android.os.Parcel);
method protected void copyFrom(android.telephony.ServiceState);
method public int describeContents();
+ method public int[] getCellBandwidths();
+ method public int getChannelNumber();
+ method public int getDuplexMode();
method public boolean getIsManualSelection();
+ method public int getNetworkId();
method public java.lang.String getOperatorAlphaLong();
method public java.lang.String getOperatorAlphaShort();
method public java.lang.String getOperatorNumeric();
method public boolean getRoaming();
method public int getState();
+ method public int getSystemId();
method public void setIsManualSelection(boolean);
method public void setOperatorName(java.lang.String, java.lang.String, java.lang.String);
method public void setRoaming(boolean);
@@ -40478,10 +40627,14 @@
method public void setStateOutOfService();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.ServiceState> CREATOR;
+ field public static final int DUPLEX_MODE_FDD = 1; // 0x1
+ field public static final int DUPLEX_MODE_TDD = 2; // 0x2
+ field public static final int DUPLEX_MODE_UNKNOWN = 0; // 0x0
field public static final int STATE_EMERGENCY_ONLY = 2; // 0x2
field public static final int STATE_IN_SERVICE = 0; // 0x0
field public static final int STATE_OUT_OF_SERVICE = 1; // 0x1
field public static final int STATE_POWER_OFF = 3; // 0x3
+ field public static final int UNKNOWN_ID = -1; // 0xffffffff
}
public class SignalStrength implements android.os.Parcelable {
@@ -40640,13 +40793,16 @@
method public java.lang.String getNumber();
method public int getSimSlotIndex();
method public int getSubscriptionId();
+ method public boolean isEmbedded();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionInfo> CREATOR;
}
public class SubscriptionManager {
method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+ method public boolean canManageSubscription(android.telephony.SubscriptionInfo);
method public static android.telephony.SubscriptionManager from(android.content.Context);
+ method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
method public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
method public int getActiveSubscriptionInfoCount();
method public int getActiveSubscriptionInfoCountMax();
@@ -40676,6 +40832,8 @@
method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method public int getAndroidCarrierIdForSubscription();
+ method public java.lang.CharSequence getAndroidCarrierNameForSubscription();
method public int getCallState();
method public android.os.PersistableBundle getCarrierConfig();
method public deprecated android.telephony.CellLocation getCellLocation();
@@ -40695,6 +40853,7 @@
method public java.lang.String getMeid(int);
method public java.lang.String getMmsUAProfUrl();
method public java.lang.String getMmsUserAgent();
+ method public java.lang.String getNai();
method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public java.lang.String getNetworkCountryIso();
method public java.lang.String getNetworkOperator();
@@ -40726,11 +40885,12 @@
method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isConcurrentVoiceAndDataSupported();
- method public boolean isDataEnabled();
+ method public deprecated boolean isDataEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
method public boolean isSmsCapable();
method public deprecated boolean isTtyModeSupported();
+ method public boolean isUserMobileDataEnabled();
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
@@ -40740,12 +40900,13 @@
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(java.lang.String, int, java.lang.String, android.app.PendingIntent);
- method public void setDataEnabled(boolean);
+ method public deprecated void setDataEnabled(boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
method public void setNetworkSelectionModeAutomatic();
method public boolean setNetworkSelectionModeManual(java.lang.String, boolean);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
+ method public void setUserMobileDataEnabled(boolean);
method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
@@ -40754,6 +40915,7 @@
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+ field public static final java.lang.String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -40774,6 +40936,8 @@
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+ field public static final java.lang.String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
+ field public static final java.lang.String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
field public static final java.lang.String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final java.lang.String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH";
@@ -40784,6 +40948,7 @@
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final java.lang.String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
@@ -40819,6 +40984,7 @@
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 UNKNOWN_CARRIER_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 java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm";
@@ -40906,6 +41072,117 @@
}
+package android.telephony.data {
+
+ public class ApnSetting implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getApnName();
+ method public int getAuthType();
+ method public java.lang.String getEntryName();
+ method public int getId();
+ method public int getMmsPort();
+ method public java.net.InetAddress getMmsProxy();
+ method public java.net.URL getMmsc();
+ method public java.lang.String getMvnoType();
+ method public int getNetworkTypeBitmask();
+ method public java.lang.String getOperatorNumeric();
+ method public java.lang.String getPassword();
+ method public int getPort();
+ method public java.lang.String getProtocol();
+ method public java.net.InetAddress getProxy();
+ method public java.lang.String getRoamingProtocol();
+ method public java.util.List<java.lang.String> getTypes();
+ method public java.lang.String getUser();
+ method public boolean isEnabled();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int AUTH_TYPE_CHAP = 2; // 0x2
+ field public static final int AUTH_TYPE_NONE = 0; // 0x0
+ field public static final int AUTH_TYPE_PAP = 1; // 0x1
+ field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3
+ field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR;
+ field public static final java.lang.String MVNO_TYPE_GID = "gid";
+ field public static final java.lang.String MVNO_TYPE_ICCID = "iccid";
+ field public static final java.lang.String MVNO_TYPE_IMSI = "imsi";
+ field public static final java.lang.String MVNO_TYPE_SPN = "spn";
+ field public static final java.lang.String PROTOCOL_IP = "IP";
+ field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6";
+ field public static final java.lang.String PROTOCOL_IPV6 = "IPV6";
+ field public static final java.lang.String PROTOCOL_PPP = "PPP";
+ field public static final java.lang.String TYPE_ALL = "*";
+ field public static final java.lang.String TYPE_CBS = "cbs";
+ field public static final java.lang.String TYPE_DEFAULT = "default";
+ field public static final java.lang.String TYPE_DUN = "dun";
+ field public static final java.lang.String TYPE_EMERGENCY = "emergency";
+ field public static final java.lang.String TYPE_FOTA = "fota";
+ field public static final java.lang.String TYPE_HIPRI = "hipri";
+ field public static final java.lang.String TYPE_IA = "ia";
+ field public static final java.lang.String TYPE_IMS = "ims";
+ field public static final java.lang.String TYPE_MMS = "mms";
+ field public static final java.lang.String TYPE_SUPL = "supl";
+ }
+
+ public static class ApnSetting.Builder {
+ ctor public ApnSetting.Builder();
+ method public android.telephony.data.ApnSetting build();
+ method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setAuthType(int);
+ method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
+ method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
+ method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
+ method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
+ method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
+ method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setPort(int);
+ method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress);
+ method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String);
+ method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>);
+ method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
+ }
+
+}
+
+package android.telephony.euicc {
+
+ public final class DownloadableSubscription implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.telephony.euicc.DownloadableSubscription forActivationCode(java.lang.String);
+ method public java.lang.String getConfirmationCode();
+ method public java.lang.String getEncodedActivationCode();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.euicc.DownloadableSubscription> CREATOR;
+ }
+
+ public final class EuiccInfo implements android.os.Parcelable {
+ ctor public EuiccInfo(java.lang.String);
+ method public int describeContents();
+ method public java.lang.String getOsVersion();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccInfo> CREATOR;
+ }
+
+ public class EuiccManager {
+ method public void deleteSubscription(int, android.app.PendingIntent);
+ method public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent);
+ method public java.lang.String getEid();
+ method public android.telephony.euicc.EuiccInfo getEuiccInfo();
+ method public boolean isEnabled();
+ method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException;
+ method public void switchToSubscription(int, android.app.PendingIntent);
+ field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+ field public static final java.lang.String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE = "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
+ field public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; // 0x2
+ field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0
+ field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1
+ field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
+ field public static final java.lang.String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
+ }
+
+}
+
package android.telephony.gsm {
public class GsmCellLocation extends android.telephony.CellLocation {
@@ -40920,11 +41197,11 @@
}
public final deprecated class SmsManager {
- method public final deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
- method public static final deprecated android.telephony.gsm.SmsManager getDefault();
- method public final deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
- method public final deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
- method public final deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
+ method public deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
+ method public static deprecated android.telephony.gsm.SmsManager getDefault();
+ method public deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
+ method public deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
+ method public deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
field public static final deprecated int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1
field public static final deprecated int RESULT_ERROR_NO_SERVICE = 4; // 0x4
field public static final deprecated int RESULT_ERROR_NULL_PDU = 3; // 0x3
@@ -40999,20 +41276,23 @@
package android.telephony.mbms {
public final class DownloadRequest implements android.os.Parcelable {
- method public static android.telephony.mbms.DownloadRequest copy(android.telephony.mbms.DownloadRequest);
method public int describeContents();
+ method public android.net.Uri getDestinationUri();
method public java.lang.String getFileServiceId();
method public static int getMaxAppIntentSize();
method public static int getMaxDestinationUriSize();
method public android.net.Uri getSourceUri();
method public int getSubscriptionId();
+ method public byte[] toByteArray();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.mbms.DownloadRequest> CREATOR;
}
public static class DownloadRequest.Builder {
- ctor public DownloadRequest.Builder(android.net.Uri);
+ ctor public DownloadRequest.Builder(android.net.Uri, android.net.Uri);
method public android.telephony.mbms.DownloadRequest build();
+ method public static android.telephony.mbms.DownloadRequest.Builder fromDownloadRequest(android.telephony.mbms.DownloadRequest);
+ method public static android.telephony.mbms.DownloadRequest.Builder fromSerializedRequest(byte[]);
method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
@@ -41066,6 +41346,7 @@
public static class MbmsErrors.DownloadErrors {
field public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401; // 0x191
field public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; // 0x192
+ field public static final int ERROR_UNKNOWN_FILE_INFO = 403; // 0x193
}
public static class MbmsErrors.GeneralErrors {
@@ -41107,10 +41388,10 @@
method public java.util.Date getSessionStartTime();
}
- public class StreamingService {
+ public class StreamingService implements java.lang.AutoCloseable {
+ method public void close();
method public android.telephony.mbms.StreamingServiceInfo getInfo();
method public android.net.Uri getPlaybackUri();
- method public void stopStreaming();
field public static final int BROADCAST_METHOD = 1; // 0x1
field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
field public static final int REASON_END_OF_SESSION = 2; // 0x2
@@ -41453,6 +41734,10 @@
package android.test.mock {
+ public deprecated class MockAccountManager {
+ method public static android.accounts.AccountManager newMockAccountManager(android.content.Context);
+ }
+
public deprecated class MockApplication extends android.app.Application {
ctor public MockApplication();
}
@@ -41462,6 +41747,7 @@
ctor public MockContentProvider(android.content.Context);
ctor public MockContentProvider(android.content.Context, java.lang.String, java.lang.String, android.content.pm.PathPermission[]);
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>);
+ method public static deprecated void attachInfoForTesting(android.content.ContentProvider, android.content.Context, android.content.pm.ProviderInfo);
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public java.lang.String getType(android.net.Uri);
method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
@@ -41742,6 +42028,10 @@
method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
}
+ public deprecated class MockService {
+ method public static <T extends android.app.Service> void attachForTesting(android.app.Service, android.content.Context, java.lang.String, android.app.Application);
+ }
+
}
package android.test.suitebuilder {
@@ -43874,6 +44164,7 @@
field public static final int DENSITY_360 = 360; // 0x168
field public static final int DENSITY_400 = 400; // 0x190
field public static final int DENSITY_420 = 420; // 0x1a4
+ field public static final int DENSITY_440 = 440; // 0x1b8
field public static final int DENSITY_560 = 560; // 0x230
field public static final int DENSITY_DEFAULT = 160; // 0xa0
field public static final int DENSITY_DEVICE_STABLE;
@@ -44157,42 +44448,42 @@
method public void previousMonth();
}
- public final class MutableBoolean {
+ public final deprecated class MutableBoolean {
ctor public MutableBoolean(boolean);
field public boolean value;
}
- public final class MutableByte {
+ public final deprecated class MutableByte {
ctor public MutableByte(byte);
field public byte value;
}
- public final class MutableChar {
+ public final deprecated class MutableChar {
ctor public MutableChar(char);
field public char value;
}
- public final class MutableDouble {
+ public final deprecated class MutableDouble {
ctor public MutableDouble(double);
field public double value;
}
- public final class MutableFloat {
+ public final deprecated class MutableFloat {
ctor public MutableFloat(float);
field public float value;
}
- public final class MutableInt {
+ public final deprecated class MutableInt {
ctor public MutableInt(int);
field public int value;
}
- public final class MutableLong {
+ public final deprecated class MutableLong {
ctor public MutableLong(long);
field public long value;
}
- public final class MutableShort {
+ public final deprecated class MutableShort {
ctor public MutableShort(short);
field public short value;
}
@@ -45539,76 +45830,76 @@
public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
method public static java.lang.String actionToString(int);
- method public final void addBatch(long, float, float, float, float, int);
- method public final void addBatch(long, android.view.MotionEvent.PointerCoords[], int);
+ method public void addBatch(long, float, float, float, float, int);
+ method public void addBatch(long, android.view.MotionEvent.PointerCoords[], int);
method public static int axisFromString(java.lang.String);
method public static java.lang.String axisToString(int);
- method public final int findPointerIndex(int);
- method public final int getAction();
- method public final int getActionButton();
- method public final int getActionIndex();
- method public final int getActionMasked();
- method public final float getAxisValue(int);
- method public final float getAxisValue(int, int);
- method public final int getButtonState();
- method public final int getDeviceId();
- method public final long getDownTime();
- method public final int getEdgeFlags();
- method public final long getEventTime();
- method public final int getFlags();
- method public final float getHistoricalAxisValue(int, int);
- method public final float getHistoricalAxisValue(int, int, int);
- method public final long getHistoricalEventTime(int);
- method public final float getHistoricalOrientation(int);
- method public final float getHistoricalOrientation(int, int);
- method public final void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords);
- method public final float getHistoricalPressure(int);
- method public final float getHistoricalPressure(int, int);
- method public final float getHistoricalSize(int);
- method public final float getHistoricalSize(int, int);
- method public final float getHistoricalToolMajor(int);
- method public final float getHistoricalToolMajor(int, int);
- method public final float getHistoricalToolMinor(int);
- method public final float getHistoricalToolMinor(int, int);
- method public final float getHistoricalTouchMajor(int);
- method public final float getHistoricalTouchMajor(int, int);
- method public final float getHistoricalTouchMinor(int);
- method public final float getHistoricalTouchMinor(int, int);
- method public final float getHistoricalX(int);
- method public final float getHistoricalX(int, int);
- method public final float getHistoricalY(int);
- method public final float getHistoricalY(int, int);
- method public final int getHistorySize();
- method public final int getMetaState();
- method public final float getOrientation();
- method public final float getOrientation(int);
- method public final void getPointerCoords(int, android.view.MotionEvent.PointerCoords);
- method public final int getPointerCount();
- method public final int getPointerId(int);
- method public final void getPointerProperties(int, android.view.MotionEvent.PointerProperties);
- method public final float getPressure();
- method public final float getPressure(int);
- method public final float getRawX();
- method public final float getRawY();
- method public final float getSize();
- method public final float getSize(int);
- method public final int getSource();
- method public final float getToolMajor();
- method public final float getToolMajor(int);
- method public final float getToolMinor();
- method public final float getToolMinor(int);
- method public final int getToolType(int);
- method public final float getTouchMajor();
- method public final float getTouchMajor(int);
- method public final float getTouchMinor();
- method public final float getTouchMinor(int);
- method public final float getX();
- method public final float getX(int);
- method public final float getXPrecision();
- method public final float getY();
- method public final float getY(int);
- method public final float getYPrecision();
- method public final boolean isButtonPressed(int);
+ method public int findPointerIndex(int);
+ method public int getAction();
+ method public int getActionButton();
+ method public int getActionIndex();
+ method public int getActionMasked();
+ method public float getAxisValue(int);
+ method public float getAxisValue(int, int);
+ method public int getButtonState();
+ method public int getDeviceId();
+ method public long getDownTime();
+ method public int getEdgeFlags();
+ method public long getEventTime();
+ method public int getFlags();
+ method public float getHistoricalAxisValue(int, int);
+ method public float getHistoricalAxisValue(int, int, int);
+ method public long getHistoricalEventTime(int);
+ method public float getHistoricalOrientation(int);
+ method public float getHistoricalOrientation(int, int);
+ method public void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords);
+ method public float getHistoricalPressure(int);
+ method public float getHistoricalPressure(int, int);
+ method public float getHistoricalSize(int);
+ method public float getHistoricalSize(int, int);
+ method public float getHistoricalToolMajor(int);
+ method public float getHistoricalToolMajor(int, int);
+ method public float getHistoricalToolMinor(int);
+ method public float getHistoricalToolMinor(int, int);
+ method public float getHistoricalTouchMajor(int);
+ method public float getHistoricalTouchMajor(int, int);
+ method public float getHistoricalTouchMinor(int);
+ method public float getHistoricalTouchMinor(int, int);
+ method public float getHistoricalX(int);
+ method public float getHistoricalX(int, int);
+ method public float getHistoricalY(int);
+ method public float getHistoricalY(int, int);
+ method public int getHistorySize();
+ method public int getMetaState();
+ method public float getOrientation();
+ method public float getOrientation(int);
+ method public void getPointerCoords(int, android.view.MotionEvent.PointerCoords);
+ method public int getPointerCount();
+ method public int getPointerId(int);
+ method public void getPointerProperties(int, android.view.MotionEvent.PointerProperties);
+ method public float getPressure();
+ method public float getPressure(int);
+ method public float getRawX();
+ method public float getRawY();
+ method public float getSize();
+ method public float getSize(int);
+ method public int getSource();
+ method public float getToolMajor();
+ method public float getToolMajor(int);
+ method public float getToolMinor();
+ method public float getToolMinor(int);
+ method public int getToolType(int);
+ method public float getTouchMajor();
+ method public float getTouchMajor(int);
+ method public float getTouchMinor();
+ method public float getTouchMinor(int);
+ method public float getX();
+ method public float getX(int);
+ method public float getXPrecision();
+ method public float getY();
+ method public float getY(int);
+ method public float getYPrecision();
+ method public boolean isButtonPressed(int);
method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
@@ -45616,13 +45907,13 @@
method public static android.view.MotionEvent obtain(long, long, int, float, float, int);
method public static android.view.MotionEvent obtain(android.view.MotionEvent);
method public static android.view.MotionEvent obtainNoHistory(android.view.MotionEvent);
- method public final void offsetLocation(float, float);
- method public final void recycle();
- method public final void setAction(int);
- method public final void setEdgeFlags(int);
- method public final void setLocation(float, float);
- method public final void setSource(int);
- method public final void transform(android.graphics.Matrix);
+ method public void offsetLocation(float, float);
+ method public void recycle();
+ method public void setAction(int);
+ method public void setEdgeFlags(int);
+ method public void setLocation(float, float);
+ method public void setSource(int);
+ method public void transform(android.graphics.Matrix);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_BUTTON_PRESS = 11; // 0xb
field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc
@@ -47320,9 +47611,9 @@
method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
- method public final void dispatchOnDraw();
- method public final void dispatchOnGlobalLayout();
- method public final boolean dispatchOnPreDraw();
+ method public void dispatchOnDraw();
+ method public void dispatchOnGlobalLayout();
+ method public boolean dispatchOnPreDraw();
method public boolean isAlive();
method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
@@ -49314,7 +49605,7 @@
ctor public URLUtil();
method public static java.lang.String composeSearchUrl(java.lang.String, java.lang.String, java.lang.String);
method public static byte[] decode(byte[]) throws java.lang.IllegalArgumentException;
- method public static final java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String);
+ method public static java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String);
method public static java.lang.String guessUrl(java.lang.String);
method public static boolean isAboutUrl(java.lang.String);
method public static boolean isAssetUrl(java.lang.String);
@@ -54360,7 +54651,7 @@
}
public static final class Character.UnicodeBlock extends java.lang.Character.Subset {
- method public static final java.lang.Character.UnicodeBlock forName(java.lang.String);
+ method public static java.lang.Character.UnicodeBlock forName(java.lang.String);
method public static java.lang.Character.UnicodeBlock of(char);
method public static java.lang.Character.UnicodeBlock of(int);
field public static final java.lang.Character.UnicodeBlock AEGEAN_NUMBERS;
@@ -54587,7 +54878,7 @@
}
public static final class Character.UnicodeScript extends java.lang.Enum {
- method public static final java.lang.Character.UnicodeScript forName(java.lang.String);
+ method public static java.lang.Character.UnicodeScript forName(java.lang.String);
method public static java.lang.Character.UnicodeScript of(int);
method public static java.lang.Character.UnicodeScript valueOf(java.lang.String);
method public static final java.lang.Character.UnicodeScript[] values();
@@ -56250,6 +56541,7 @@
method public boolean enqueue();
method public T get();
method public boolean isEnqueued();
+ method public static void reachabilityFence(java.lang.Object);
}
public class ReferenceQueue<T> {
@@ -57402,8 +57694,8 @@
ctor public URL(java.net.URL, java.lang.String) throws java.net.MalformedURLException;
ctor public URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
method public java.lang.String getAuthority();
- method public final java.lang.Object getContent() throws java.io.IOException;
- method public final java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
+ method public java.lang.Object getContent() throws java.io.IOException;
+ method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
method public int getDefaultPort();
method public java.lang.String getFile();
method public java.lang.String getHost();
@@ -57416,7 +57708,7 @@
method public synchronized int hashCode();
method public java.net.URLConnection openConnection() throws java.io.IOException;
method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
- method public final java.io.InputStream openStream() throws java.io.IOException;
+ method public java.io.InputStream openStream() throws java.io.IOException;
method public boolean sameFile(java.net.URL);
method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
method public java.lang.String toExternalForm();
@@ -62072,13 +62364,13 @@
method public int getOffset();
method public int next();
method public int previous();
- method public static final int primaryOrder(int);
+ method public static int primaryOrder(int);
method public void reset();
- method public static final short secondaryOrder(int);
+ method public static short secondaryOrder(int);
method public void setOffset(int);
method public void setText(java.lang.String);
method public void setText(java.text.CharacterIterator);
- method public static final short tertiaryOrder(int);
+ method public static short tertiaryOrder(int);
field public static final int NULLORDER = -1; // 0xffffffff
}
@@ -63359,7 +63651,7 @@
}
public final class HijrahDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime);
method public static java.time.chrono.HijrahDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.HijrahChronology getChronology();
method public java.time.chrono.HijrahEra getEra();
@@ -63446,7 +63738,7 @@
}
public final class JapaneseDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime);
method public static java.time.chrono.JapaneseDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.JapaneseChronology getChronology();
method public java.time.chrono.JapaneseEra getEra();
@@ -63502,7 +63794,7 @@
}
public final class MinguoDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime);
method public static java.time.chrono.MinguoDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.MinguoChronology getChronology();
method public java.time.chrono.MinguoEra getEra();
@@ -63555,7 +63847,7 @@
}
public final class ThaiBuddhistDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
- method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime);
+ method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime);
method public static java.time.chrono.ThaiBuddhistDate from(java.time.temporal.TemporalAccessor);
method public java.time.chrono.ThaiBuddhistChronology getChronology();
method public java.time.chrono.ThaiBuddhistEra getEra();
@@ -63607,8 +63899,8 @@
method public <T> T parse(java.lang.CharSequence, java.time.temporal.TemporalQuery<T>);
method public java.time.temporal.TemporalAccessor parseBest(java.lang.CharSequence, java.time.temporal.TemporalQuery<?>...);
method public java.time.temporal.TemporalAccessor parseUnresolved(java.lang.CharSequence, java.text.ParsePosition);
- method public static final java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays();
- method public static final java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond();
+ method public static java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays();
+ method public static java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond();
method public java.text.Format toFormat();
method public java.text.Format toFormat(java.time.temporal.TemporalQuery<?>);
method public java.time.format.DateTimeFormatter withChronology(java.time.chrono.Chronology);
@@ -65057,15 +65349,15 @@
method public java.lang.String getCountry();
method public static java.util.Locale getDefault();
method public static java.util.Locale getDefault(java.util.Locale.Category);
- method public final java.lang.String getDisplayCountry();
+ method public java.lang.String getDisplayCountry();
method public java.lang.String getDisplayCountry(java.util.Locale);
- method public final java.lang.String getDisplayLanguage();
+ method public java.lang.String getDisplayLanguage();
method public java.lang.String getDisplayLanguage(java.util.Locale);
- method public final java.lang.String getDisplayName();
+ method public java.lang.String getDisplayName();
method public java.lang.String getDisplayName(java.util.Locale);
method public java.lang.String getDisplayScript();
method public java.lang.String getDisplayScript(java.util.Locale);
- method public final java.lang.String getDisplayVariant();
+ method public java.lang.String getDisplayVariant();
method public java.lang.String getDisplayVariant(java.util.Locale);
method public java.lang.String getExtension(char);
method public java.util.Set<java.lang.Character> getExtensionKeys();
@@ -65086,7 +65378,6 @@
method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
method public java.util.Locale stripExtensions();
method public java.lang.String toLanguageTag();
- method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
field public static final java.util.Locale CANADA_FRENCH;
field public static final java.util.Locale CHINA;
@@ -69272,7 +69563,6 @@
public class ExemptionMechanism {
ctor protected ExemptionMechanism(javax.crypto.ExemptionMechanismSpi, java.security.Provider, java.lang.String);
- method protected void finalize();
method public final byte[] genExemptionBlob() throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException;
method public final int genExemptionBlob(byte[]) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
method public final int genExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 3a71189..e3336db 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20,6 +20,7 @@
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
+ field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
@@ -28,6 +29,8 @@
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
+ field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
+ field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
@@ -123,6 +126,7 @@
field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
field public static final java.lang.String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
+ field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
@@ -170,6 +174,7 @@
field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS";
field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
+ field public static final java.lang.String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
field public static final java.lang.String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
@@ -615,6 +620,7 @@
public final class BluetoothAdapter {
method public boolean disableBLE();
method public boolean enableBLE();
+ method public boolean enableNoAutoConnect();
method public boolean isBleScanAlwaysAvailable();
method public boolean isLeEnabled();
field public static final java.lang.String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
@@ -622,8 +628,25 @@
}
public final class BluetoothDevice implements android.os.Parcelable {
+ method public boolean cancelBondProcess();
method public boolean isConnected();
method public boolean isEncrypted();
+ method public boolean removeBond();
+ method public boolean setPhonebookAccessPermission(int);
+ field public static final int ACCESS_ALLOWED = 1; // 0x1
+ field public static final int ACCESS_REJECTED = 2; // 0x2
+ field public static final int ACCESS_UNKNOWN = 0; // 0x0
+ }
+
+ public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
+ method public boolean connect(android.bluetooth.BluetoothDevice);
+ method public boolean disconnect(android.bluetooth.BluetoothDevice);
+ method public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+ }
+
+ public abstract interface BluetoothProfile {
+ field public static final int PRIORITY_OFF = 0; // 0x0
+ field public static final int PRIORITY_ON = 100; // 0x64
}
}
@@ -675,10 +698,12 @@
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
field public static final java.lang.String BACKUP_SERVICE = "backup";
field public static final java.lang.String CONTEXTHUB_SERVICE = "contexthub";
+ field public static final java.lang.String EUICC_CARD_SERVICE = "euicc_card";
field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score";
field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+ field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
field public static final java.lang.String VR_SERVICE = "vrmanager";
field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -709,7 +734,7 @@
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
- field public static final java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
+ field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
@@ -823,6 +848,7 @@
public abstract class PackageManager {
method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
+ method public android.content.pm.dex.ArtManager getArtManager();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
@@ -924,6 +950,26 @@
}
+package android.content.pm.dex {
+
+ public class ArtManager {
+ method public boolean isRuntimeProfilingEnabled(int);
+ method public void snapshotRuntimeProfile(int, java.lang.String, java.lang.String, java.util.concurrent.Executor, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback);
+ field public static final int PROFILE_APPS = 0; // 0x0
+ field public static final int PROFILE_BOOT_IMAGE = 1; // 0x1
+ field public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; // 0x1
+ field public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; // 0x2
+ field public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; // 0x0
+ }
+
+ public static abstract class ArtManager.SnapshotRuntimeProfileCallback {
+ ctor public ArtManager.SnapshotRuntimeProfileCallback();
+ method public abstract void onError(int);
+ method public abstract void onSuccess(android.os.ParcelFileDescriptor);
+ }
+
+}
+
package android.content.pm.permission {
public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
@@ -2304,10 +2350,10 @@
package android.media.tv {
public final class TvContentRatingSystemInfo implements android.os.Parcelable {
- method public static final android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
+ method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
method public int describeContents();
- method public final android.net.Uri getXmlUri();
- method public final boolean isSystemDefined();
+ method public android.net.Uri getXmlUri();
+ method public boolean isSystemDefined();
method public void writeToParcel(android.os.Parcel, int);
}
@@ -2559,9 +2605,33 @@
method public void onTetheringStarted();
}
+ public final class IpSecManager {
+ method public void applyTunnelModeTransform(android.net.IpSecManager.IpSecTunnelInterface, int, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ }
+
+ public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method public void close();
+ method public java.lang.String getInterfaceName();
+ }
+
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void startNattKeepalive(android.net.IpSecTransform.NattKeepaliveCallback, int, android.os.Handler) throws java.io.IOException;
+ method public void stopNattKeepalive();
+ }
+
public static class IpSecTransform.Builder {
- method public android.net.IpSecTransform.Builder setNattKeepalive(int);
- method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
+ method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ }
+
+ public static class IpSecTransform.NattKeepaliveCallback {
+ ctor public IpSecTransform.NattKeepaliveCallback();
+ method public void onError(int);
+ method public void onStarted();
+ method public void onStopped();
+ field public static final int ERROR_HARDWARE_ERROR = 3; // 0x3
+ field public static final int ERROR_HARDWARE_UNSUPPORTED = 2; // 0x2
+ field public static final int ERROR_INVALID_NETWORK = 1; // 0x1
}
public class NetworkKey implements android.os.Parcelable {
@@ -3461,6 +3531,7 @@
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+ field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
@@ -3503,6 +3574,125 @@
}
+package android.service.euicc {
+
+ public final class EuiccProfileInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.service.carrier.CarrierIdentifier getCarrierIdentifier();
+ method public java.lang.String getIccid();
+ method public java.lang.String getNickname();
+ method public int getPolicyRules();
+ method public int getProfileClass();
+ method public java.lang.String getProfileName();
+ method public java.lang.String getServiceProviderName();
+ method public int getState();
+ method public java.util.List<android.telephony.UiccAccessRule> getUiccAccessRules();
+ method public boolean hasPolicyRule(int);
+ method public boolean hasPolicyRules();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.euicc.EuiccProfileInfo> CREATOR;
+ field public static final int POLICY_RULE_DELETE_AFTER_DISABLING = 4; // 0x4
+ field public static final int POLICY_RULE_DO_NOT_DELETE = 2; // 0x2
+ field public static final int POLICY_RULE_DO_NOT_DISABLE = 1; // 0x1
+ field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
+ field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
+ field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
+ field public static final int PROFILE_STATE_DISABLED = 0; // 0x0
+ field public static final int PROFILE_STATE_ENABLED = 1; // 0x1
+ }
+
+ public static final class EuiccProfileInfo.Builder {
+ ctor public EuiccProfileInfo.Builder(java.lang.String);
+ ctor public EuiccProfileInfo.Builder(android.service.euicc.EuiccProfileInfo);
+ method public android.service.euicc.EuiccProfileInfo build();
+ method public android.service.euicc.EuiccProfileInfo.Builder setCarrierIdentifier(android.service.carrier.CarrierIdentifier);
+ method public android.service.euicc.EuiccProfileInfo.Builder setIccid(java.lang.String);
+ method public android.service.euicc.EuiccProfileInfo.Builder setNickname(java.lang.String);
+ method public android.service.euicc.EuiccProfileInfo.Builder setPolicyRules(int);
+ method public android.service.euicc.EuiccProfileInfo.Builder setProfileClass(int);
+ method public android.service.euicc.EuiccProfileInfo.Builder setProfileName(java.lang.String);
+ method public android.service.euicc.EuiccProfileInfo.Builder setServiceProviderName(java.lang.String);
+ method public android.service.euicc.EuiccProfileInfo.Builder setState(int);
+ method public android.service.euicc.EuiccProfileInfo.Builder setUiccAccessRule(java.util.List<android.telephony.UiccAccessRule>);
+ }
+
+ public static abstract class EuiccProfileInfo.PolicyRule implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class EuiccProfileInfo.ProfileClass implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class EuiccProfileInfo.ProfileState implements java.lang.annotation.Annotation {
+ }
+
+ public abstract class EuiccService extends android.app.Service {
+ ctor public EuiccService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract int onDeleteSubscription(int, java.lang.String);
+ method public abstract int onDownloadSubscription(int, android.telephony.euicc.DownloadableSubscription, boolean, boolean);
+ method public abstract int onEraseSubscriptions(int);
+ method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
+ method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
+ method public abstract java.lang.String onGetEid(int);
+ method public abstract android.telephony.euicc.EuiccInfo onGetEuiccInfo(int);
+ method public abstract android.service.euicc.GetEuiccProfileInfoListResult onGetEuiccProfileInfoList(int);
+ method public abstract int onGetOtaStatus(int);
+ method public abstract int onRetainSubscriptionsForFactoryReset(int);
+ method public abstract void onStartOtaIfNecessary(int, android.service.euicc.EuiccService.OtaStatusChangedCallback);
+ method public abstract int onSwitchToSubscription(int, java.lang.String, boolean);
+ method public abstract int onUpdateSubscriptionNickname(int, java.lang.String, java.lang.String);
+ field public static final java.lang.String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+ field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final java.lang.String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
+ field public static final java.lang.String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
+ field public static final java.lang.String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
+ field public static final java.lang.String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
+ field public static final java.lang.String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
+ field public static final java.lang.String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
+ field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
+ field public static final java.lang.String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
+ field public static final java.lang.String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
+ field public static final int RESULT_FIRST_USER = 1; // 0x1
+ field public static final int RESULT_MUST_DEACTIVATE_SIM = -1; // 0xffffffff
+ field public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // 0xfffffffe
+ field public static final int RESULT_OK = 0; // 0x0
+ }
+
+ public static abstract class EuiccService.OtaStatusChangedCallback {
+ ctor public EuiccService.OtaStatusChangedCallback();
+ method public abstract void onOtaStatusChanged(int);
+ }
+
+ public final class GetDefaultDownloadableSubscriptionListResult implements android.os.Parcelable {
+ ctor public GetDefaultDownloadableSubscriptionListResult(int, android.telephony.euicc.DownloadableSubscription[]);
+ method public int describeContents();
+ method public java.util.List<android.telephony.euicc.DownloadableSubscription> getDownloadableSubscriptions();
+ method public int getResult();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.euicc.GetDefaultDownloadableSubscriptionListResult> CREATOR;
+ }
+
+ public final class GetDownloadableSubscriptionMetadataResult implements android.os.Parcelable {
+ ctor public GetDownloadableSubscriptionMetadataResult(int, android.telephony.euicc.DownloadableSubscription);
+ method public int describeContents();
+ method public android.telephony.euicc.DownloadableSubscription getDownloadableSubscription();
+ method public int getResult();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.euicc.GetDownloadableSubscriptionMetadataResult> CREATOR;
+ }
+
+ public final class GetEuiccProfileInfoListResult implements android.os.Parcelable {
+ ctor public GetEuiccProfileInfoListResult(int, android.service.euicc.EuiccProfileInfo[], boolean);
+ method public int describeContents();
+ method public boolean getIsRemovable();
+ method public java.util.List<android.service.euicc.EuiccProfileInfo> getProfiles();
+ method public int getResult();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.euicc.GetEuiccProfileInfoListResult> CREATOR;
+ }
+
+}
+
package android.service.notification {
public final class Adjustment implements android.os.Parcelable {
@@ -3817,15 +4007,15 @@
}
public final deprecated class Phone {
- method public final void addListener(android.telecom.Phone.Listener);
- method public final boolean canAddCall();
- method public final deprecated android.telecom.AudioState getAudioState();
- method public final android.telecom.CallAudioState getCallAudioState();
- method public final java.util.List<android.telecom.Call> getCalls();
- method public final void removeListener(android.telecom.Phone.Listener);
+ method public void addListener(android.telecom.Phone.Listener);
+ method public boolean canAddCall();
+ method public deprecated android.telecom.AudioState getAudioState();
+ method public android.telecom.CallAudioState getCallAudioState();
+ method public java.util.List<android.telecom.Call> getCalls();
+ method public void removeListener(android.telecom.Phone.Listener);
method public void requestBluetoothAudio(java.lang.String);
- method public final void setAudioRoute(int);
- method public final void setMuted(boolean);
+ method public void setAudioRoute(int);
+ method public void setMuted(boolean);
}
public static abstract class Phone.Listener {
@@ -3919,6 +4109,12 @@
package android.telephony {
+ public static final class AccessNetworkConstants.TransportType {
+ ctor public AccessNetworkConstants.TransportType();
+ field public static final int WLAN = 2; // 0x2
+ field public static final int WWAN = 1; // 0x1
+ }
+
public class CarrierConfigManager {
method public static android.os.PersistableBundle getDefaultConfig();
method public void updateConfigForPhoneId(int, java.lang.String);
@@ -3933,13 +4129,96 @@
field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
+ public class NetworkRegistrationState implements android.os.Parcelable {
+ ctor public NetworkRegistrationState(int, int, int, int, int, boolean, int[], android.telephony.CellIdentity);
+ ctor protected NetworkRegistrationState(android.os.Parcel);
+ method public int describeContents();
+ method public int getAccessNetworkTechnology();
+ method public int[] getAvailableServices();
+ method public android.telephony.CellIdentity getCellIdentity();
+ method public int getDomain();
+ method public int getReasonForDenial();
+ method public int getRegState();
+ method public int getTransportType();
+ method public boolean isEmergencyEnabled();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationState> CREATOR;
+ field public static final int DOMAIN_CS = 1; // 0x1
+ field public static final int DOMAIN_PS = 2; // 0x2
+ field public static final int REG_STATE_DENIED = 3; // 0x3
+ field public static final int REG_STATE_HOME = 1; // 0x1
+ field public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0; // 0x0
+ field public static final int REG_STATE_NOT_REG_SEARCHING = 2; // 0x2
+ field public static final int REG_STATE_ROAMING = 5; // 0x5
+ field public static final int REG_STATE_UNKNOWN = 4; // 0x4
+ field public static final int SERVICE_TYPE_DATA = 2; // 0x2
+ field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5
+ field public static final int SERVICE_TYPE_SMS = 3; // 0x3
+ field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4
+ field public static final int SERVICE_TYPE_VOICE = 1; // 0x1
+ }
+
+ public abstract class NetworkService extends android.app.Service {
+ method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
+ field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
+ field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
+ }
+
+ public class NetworkService.NetworkServiceProvider {
+ ctor public NetworkService.NetworkServiceProvider(int);
+ method public void getNetworkRegistrationState(int, android.telephony.NetworkServiceCallback);
+ method public final int getSlotId();
+ method public final void notifyNetworkRegistrationStateChanged();
+ method protected void onDestroy();
+ }
+
+ public class NetworkServiceCallback {
+ method public void onGetNetworkRegistrationStateComplete(int, android.telephony.NetworkRegistrationState);
+ field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+ field public static final int RESULT_ERROR_FAILED = 5; // 0x5
+ field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+ field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+ field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
+ public class ServiceState implements android.os.Parcelable {
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
+ method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
+ method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+ }
+
public final class SmsManager {
method public void sendMultipartTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
method public void sendTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
+ field public static final int RESULT_CANCELLED = 23; // 0x17
+ field public static final int RESULT_ENCODING_ERROR = 18; // 0x12
+ field public static final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; // 0x6
+ field public static final int RESULT_ERROR_NONE = 0; // 0x0
+ field public static final int RESULT_INTERNAL_ERROR = 21; // 0x15
+ field public static final int RESULT_INVALID_ARGUMENTS = 11; // 0xb
+ field public static final int RESULT_INVALID_SMSC_ADDRESS = 19; // 0x13
+ field public static final int RESULT_INVALID_SMS_FORMAT = 14; // 0xe
+ field public static final int RESULT_INVALID_STATE = 12; // 0xc
+ field public static final int RESULT_MODEM_ERROR = 16; // 0x10
+ field public static final int RESULT_NETWORK_ERROR = 17; // 0x11
+ field public static final int RESULT_NETWORK_REJECT = 10; // 0xa
+ field public static final int RESULT_NO_MEMORY = 13; // 0xd
+ field public static final int RESULT_NO_RESOURCES = 22; // 0x16
+ field public static final int RESULT_OPERATION_NOT_ALLOWED = 20; // 0x14
+ field public static final int RESULT_RADIO_NOT_AVAILABLE = 9; // 0x9
+ field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18
+ field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf
+ }
+
+ public class SubscriptionInfo implements android.os.Parcelable {
+ method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
}
public class SubscriptionManager {
+ method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
+ method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
}
@@ -4016,7 +4295,10 @@
method public int getCurrentPhoneType(int);
method public deprecated boolean getDataEnabled();
method public deprecated boolean getDataEnabled(int);
+ method public int getSimApplicationState();
+ method public int getSimCardState();
method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
+ method public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
method public android.os.Bundle getVisualVoicemailSettings();
method public boolean handlePinMmi(java.lang.String);
method public boolean handlePinMmiForSubscriber(int, java.lang.String);
@@ -4029,7 +4311,7 @@
method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
- method public void setDataEnabled(int, boolean);
+ method public deprecated void setDataEnabled(int, boolean);
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
@@ -4038,14 +4320,47 @@
method public int[] supplyPinReportResult(java.lang.String);
method public boolean supplyPuk(java.lang.String, java.lang.String);
method public int[] supplyPukReportResult(java.lang.String, java.lang.String);
+ method public boolean switchSlots(int[]);
method public void toggleRadioOnOff();
method public void updateServiceLocation();
+ field public static final java.lang.String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+ field public static final java.lang.String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
+ field public static final java.lang.String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
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 java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+ field public static final int SIM_STATE_LOADED = 10; // 0xa
+ field public static final int SIM_STATE_PRESENT = 11; // 0xb
+ }
+
+ public final class UiccAccessRule implements android.os.Parcelable {
+ ctor public UiccAccessRule(byte[], java.lang.String, long);
+ method public int describeContents();
+ method public int getCarrierPrivilegeStatus(android.content.pm.PackageInfo);
+ method public int getCarrierPrivilegeStatus(android.content.pm.Signature, java.lang.String);
+ method public java.lang.String getPackageName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.UiccAccessRule> CREATOR;
+ }
+
+ public class UiccSlotInfo implements android.os.Parcelable {
+ ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int);
+ method public int describeContents();
+ method public java.lang.String getCardId();
+ method public int getCardStateInfo();
+ method public boolean getIsActive();
+ method public boolean getIsEuicc();
+ method public int getLogicalSlotIdx();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
+ field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
+ field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
+ field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
+ field public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
}
public abstract class VisualVoicemailService extends android.app.Service {
@@ -4058,11 +4373,11 @@
package android.telephony.data {
public final class DataCallResponse implements android.os.Parcelable {
- ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.telephony.data.InterfaceAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
+ ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.net.LinkAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
ctor public DataCallResponse(android.os.Parcel);
method public int describeContents();
method public int getActive();
- method public java.util.List<android.telephony.data.InterfaceAddress> getAddresses();
+ method public java.util.List<android.net.LinkAddress> getAddresses();
method public int getCallId();
method public java.util.List<java.net.InetAddress> getDnses();
method public java.util.List<java.net.InetAddress> getGateways();
@@ -4105,35 +4420,874 @@
field public static final int TYPE_COMMON = 0; // 0x0
}
- public final class InterfaceAddress implements android.os.Parcelable {
- ctor public InterfaceAddress(java.net.InetAddress, int);
- ctor public InterfaceAddress(java.lang.String, int) throws java.net.UnknownHostException;
- ctor public InterfaceAddress(android.os.Parcel);
+ public abstract class DataService extends android.app.Service {
+ method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
+ field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+ field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+ field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
+ field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
+ field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
+ }
+
+ public class DataService.DataServiceProvider {
+ ctor public DataService.DataServiceProvider(int);
+ method public void deactivateDataCall(int, int, android.telephony.data.DataServiceCallback);
+ method public void getDataCallList(android.telephony.data.DataServiceCallback);
+ method public final int getSlotId();
+ method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method protected void onDestroy();
+ method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
+ method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
+ method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, int, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
+ }
+
+ public class DataServiceCallback {
+ method public void onDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onDeactivateDataCallComplete(int);
+ method public void onGetDataCallListComplete(int, java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onSetDataProfileComplete(int);
+ method public void onSetInitialAttachApnComplete(int);
+ method public void onSetupDataCallComplete(int, android.telephony.data.DataCallResponse);
+ field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+ field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+ field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+ field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
+}
+
+package android.telephony.euicc {
+
+ public final class DownloadableSubscription implements android.os.Parcelable {
+ method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
+ method public java.lang.String getCarrierName();
+ }
+
+ public static final class DownloadableSubscription.Builder {
+ ctor public DownloadableSubscription.Builder();
+ ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription);
+ method public android.telephony.euicc.DownloadableSubscription build();
+ method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List<android.telephony.UiccAccessRule>);
+ method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(java.lang.String);
+ method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(java.lang.String);
+ method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(java.lang.String);
+ }
+
+ public class EuiccCardManager {
+ method public void authenticateServer(java.lang.String, java.lang.String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void cancelSession(java.lang.String, byte[], int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void deleteProfile(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void disableProfile(java.lang.String, java.lang.String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void listNotifications(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
+ method public void loadBoundProfilePackage(java.lang.String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void prepareDownload(java.lang.String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void removeNotificationFromList(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void requestAllProfiles(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo[]>);
+ method public void requestDefaultSmdpAddress(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
+ method public void requestEuiccChallenge(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void requestEuiccInfo1(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void requestEuiccInfo2(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void requestProfile(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+ method public void requestRulesAuthTable(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccRulesAuthTable>);
+ method public void requestSmdsAddress(java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
+ method public void resetMemory(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void retrieveNotification(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification>);
+ method public void retrieveNotificationList(java.lang.String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
+ method public void setDefaultSmdpAddress(java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void setNickname(java.lang.String, java.lang.String, java.lang.String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void switchToProfile(java.lang.String, java.lang.String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+ field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0
+ field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1
+ field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3
+ field public static final int CANCEL_REASON_TIMEOUT = 2; // 0x2
+ field public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2; // 0x2
+ field public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; // 0x1
+ field public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 4; // 0x4
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
+ }
+
+ public static abstract class EuiccCardManager.CancelReason implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class EuiccCardManager.ResetOption implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract interface EuiccCardManager.ResultCallback<T> {
+ method public abstract void onComplete(int, T);
+ }
+
+ public class EuiccManager {
+ method public void continueOperation(android.content.Intent, android.os.Bundle);
+ method public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
+ method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
+ method public int getOtaStatus();
+ field public static final java.lang.String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+ field public static final java.lang.String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final int EUICC_OTA_FAILED = 2; // 0x2
+ field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1
+ field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
+ field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
+ field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
+ field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
+ field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
+ }
+
+ public static abstract class EuiccManager.OtaStatus implements java.lang.annotation.Annotation {
+ }
+
+ public final class EuiccNotification implements android.os.Parcelable {
+ ctor public EuiccNotification(int, java.lang.String, int, byte[]);
method public int describeContents();
- method public java.net.InetAddress getAddress();
- method public int getNetworkPrefixLength();
+ method public byte[] getData();
+ method public int getEvent();
+ method public int getSeq();
+ method public java.lang.String getTargetAddr();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.data.InterfaceAddress> CREATOR;
+ field public static final int ALL_EVENTS = 15; // 0xf
+ field public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccNotification> CREATOR;
+ field public static final int EVENT_DELETE = 8; // 0x8
+ field public static final int EVENT_DISABLE = 4; // 0x4
+ field public static final int EVENT_ENABLE = 2; // 0x2
+ field public static final int EVENT_INSTALL = 1; // 0x1
+ }
+
+ public static abstract class EuiccNotification.Event implements java.lang.annotation.Annotation {
+ }
+
+ public final class EuiccRulesAuthTable implements android.os.Parcelable {
+ method public int describeContents();
+ method public int findIndex(int, android.service.carrier.CarrierIdentifier);
+ method public boolean hasPolicyRuleFlag(int, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccRulesAuthTable> CREATOR;
+ field public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1; // 0x1
+ }
+
+ public static final class EuiccRulesAuthTable.Builder {
+ ctor public EuiccRulesAuthTable.Builder(int);
+ method public android.telephony.euicc.EuiccRulesAuthTable.Builder add(int, java.util.List<android.service.carrier.CarrierIdentifier>, int);
+ method public android.telephony.euicc.EuiccRulesAuthTable build();
+ }
+
+ public static abstract class EuiccRulesAuthTable.PolicyRuleFlag implements java.lang.annotation.Annotation {
}
}
package android.telephony.ims {
+ public final class ImsCallForwardInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCondition();
+ method public java.lang.String getNumber();
+ method public int getServiceClass();
+ method public int getStatus();
+ method public int getTimeSeconds();
+ method public int getToA();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallForwardInfo> CREATOR;
+ }
+
+ public final class ImsCallProfile implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getCallExtra(java.lang.String);
+ method public java.lang.String getCallExtra(java.lang.String, java.lang.String);
+ method public boolean getCallExtraBoolean(java.lang.String);
+ method public boolean getCallExtraBoolean(java.lang.String, boolean);
+ method public int getCallExtraInt(java.lang.String);
+ method public int getCallExtraInt(java.lang.String, int);
+ method public android.os.Bundle getCallExtras();
+ method public int getCallType();
+ method public static int getCallTypeFromVideoState(int);
+ method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
+ method public int getRestrictCause();
+ method public int getServiceType();
+ method public static int getVideoStateFromCallType(int);
+ method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+ method public boolean isVideoCall();
+ method public boolean isVideoPaused();
+ method public static int presentationToOir(int);
+ method public void setCallExtra(java.lang.String, java.lang.String);
+ method public void setCallExtraBoolean(java.lang.String, boolean);
+ method public void setCallExtraInt(java.lang.String, int);
+ method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
+ method public void updateCallType(android.telephony.ims.ImsCallProfile);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CALL_RESTRICT_CAUSE_DISABLED = 2; // 0x2
+ field public static final int CALL_RESTRICT_CAUSE_HD = 3; // 0x3
+ field public static final int CALL_RESTRICT_CAUSE_NONE = 0; // 0x0
+ field public static final int CALL_RESTRICT_CAUSE_RAT = 1; // 0x1
+ field public static final int CALL_TYPE_VIDEO_N_VOICE = 3; // 0x3
+ field public static final int CALL_TYPE_VOICE = 2; // 0x2
+ field public static final int CALL_TYPE_VOICE_N_VIDEO = 1; // 0x1
+ field public static final int CALL_TYPE_VS = 8; // 0x8
+ field public static final int CALL_TYPE_VS_RX = 10; // 0xa
+ field public static final int CALL_TYPE_VS_TX = 9; // 0x9
+ field public static final int CALL_TYPE_VT = 4; // 0x4
+ field public static final int CALL_TYPE_VT_NODIR = 7; // 0x7
+ field public static final int CALL_TYPE_VT_RX = 6; // 0x6
+ field public static final int CALL_TYPE_VT_TX = 5; // 0x5
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallProfile> CREATOR;
+ field public static final int DIALSTRING_NORMAL = 0; // 0x0
+ field public static final int DIALSTRING_SS_CONF = 1; // 0x1
+ field public static final int DIALSTRING_USSD = 2; // 0x2
+ field public static final java.lang.String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+ field public static final java.lang.String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+ field public static final java.lang.String EXTRA_CHILD_NUMBER = "ChildNum";
+ field public static final java.lang.String EXTRA_CNA = "cna";
+ field public static final java.lang.String EXTRA_CNAP = "cnap";
+ field public static final java.lang.String EXTRA_CODEC = "Codec";
+ field public static final java.lang.String EXTRA_DIALSTRING = "dialstring";
+ field public static final java.lang.String EXTRA_DISPLAY_TEXT = "DisplayText";
+ field public static final java.lang.String EXTRA_IS_CALL_PULL = "CallPull";
+ field public static final java.lang.String EXTRA_OI = "oi";
+ field public static final java.lang.String EXTRA_OIR = "oir";
+ field public static final java.lang.String EXTRA_REMOTE_URI = "remote_uri";
+ field public static final java.lang.String EXTRA_USSD = "ussd";
+ field public static final int OIR_DEFAULT = 0; // 0x0
+ field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
+ field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
+ field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
+ field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
+ field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
+ field public static final int SERVICE_TYPE_NONE = 0; // 0x0
+ field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
+ }
+
+ public class ImsCallSessionListener {
+ method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+ method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+ method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
+ method public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
+ method public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
+ method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
+ method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionInitiated(android.telephony.ims.ImsCallProfile);
+ method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionInviteParticipantsRequestDelivered();
+ method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionMayHandover(int, int);
+ method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
+ method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+ method public void callSessionMultipartyStateChanged(boolean);
+ method public void callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile);
+ method public void callSessionRemoveParticipantsRequestDelivered();
+ method public void callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
+ method public void callSessionRttMessageReceived(java.lang.String);
+ method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionRttModifyResponseReceived(int);
+ method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification);
+ method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionTtyModeReceived(int);
+ method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
+ method public void callSessionUssdMessageReceived(int, java.lang.String);
+ }
+
+ public final class ImsConferenceState implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getConnectionStateForStatus(java.lang.String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsConferenceState> CREATOR;
+ field public static final java.lang.String DISPLAY_TEXT = "display-text";
+ field public static final java.lang.String ENDPOINT = "endpoint";
+ field public static final java.lang.String SIP_STATUS_CODE = "sipstatuscode";
+ field public static final java.lang.String STATUS = "status";
+ field public static final java.lang.String STATUS_ALERTING = "alerting";
+ field public static final java.lang.String STATUS_CONNECTED = "connected";
+ field public static final java.lang.String STATUS_CONNECT_FAIL = "connect-fail";
+ field public static final java.lang.String STATUS_DIALING_IN = "dialing-in";
+ field public static final java.lang.String STATUS_DIALING_OUT = "dialing-out";
+ field public static final java.lang.String STATUS_DISCONNECTED = "disconnected";
+ field public static final java.lang.String STATUS_DISCONNECTING = "disconnecting";
+ field public static final java.lang.String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
+ field public static final java.lang.String STATUS_ON_HOLD = "on-hold";
+ field public static final java.lang.String STATUS_PENDING = "pending";
+ field public static final java.lang.String STATUS_SEND_ONLY = "sendonly";
+ field public static final java.lang.String STATUS_SEND_RECV = "sendrecv";
+ field public static final java.lang.String USER = "user";
+ field public final java.util.HashMap<java.lang.String, android.os.Bundle> mParticipants;
+ }
+
+ public final class ImsExternalCallState implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.net.Uri getAddress();
+ method public int getCallId();
+ method public int getCallState();
+ method public int getCallType();
+ method public boolean isCallHeld();
+ method public boolean isCallPullable();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CALL_STATE_CONFIRMED = 1; // 0x1
+ field public static final int CALL_STATE_TERMINATED = 2; // 0x2
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
+ }
+
+ public final class ImsReasonInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCode();
+ method public int getExtraCode();
+ method public java.lang.String getExtraMessage();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
+ field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
+ field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
+ field public static final int CODE_CALL_BARRED = 240; // 0xf0
+ field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
+ field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
+ field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
+ field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
+ field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
+ field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
+ field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
+ field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
+ field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
+ field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
+ field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
+ field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
+ field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
+ field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
+ field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
+ field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
+ field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
+ field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
+ field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
+ field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
+ field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
+ field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
+ field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
+ field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
+ field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
+ field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
+ field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
+ field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
+ field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
+ field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
+ field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
+ field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
+ field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
+ field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
+ field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
+ field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
+ field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
+ field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
+ field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
+ field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
+ field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
+ field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
+ field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
+ field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
+ field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
+ field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
+ field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
+ field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
+ field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
+ field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
+ field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
+ field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
+ field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
+ field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
+ field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
+ field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
+ field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
+ field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
+ field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
+ field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
+ field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
+ field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
+ field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
+ field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
+ field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
+ field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
+ field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
+ field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
+ field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
+ field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
+ field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
+ field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
+ field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
+ field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
+ field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
+ field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
+ field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
+ field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
+ field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
+ field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
+ field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
+ field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
+ field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
+ field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
+ field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
+ field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
+ field public static final int CODE_SIP_BUSY = 338; // 0x152
+ field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
+ field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
+ field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
+ field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
+ field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
+ field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
+ field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
+ field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
+ field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
+ field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
+ field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
+ field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
+ field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
+ field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
+ field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
+ field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
+ field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
+ field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
+ field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
+ field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
+ field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
+ field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
+ field public static final int CODE_UNSPECIFIED = 0; // 0x0
+ field public static final int CODE_USER_DECLINE = 504; // 0x1f8
+ field public static final int CODE_USER_IGNORE = 503; // 0x1f7
+ field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
+ field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
+ field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
+ field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
+ field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
+ field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
+ field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
+ field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
+ field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
+ field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
+ field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
+ field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
+ field public static final int CODE_WIFI_LOST = 1407; // 0x57f
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
+ field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
+ field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
+ field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
+ field public static final java.lang.String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
+ }
+
public class ImsService extends android.app.Service {
ctor public ImsService();
+ method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
+ method public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
+ method public void disableIms(int);
+ method public void enableIms(int);
+ method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+ method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
+ method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
+ method public void readyForFeatureCreation();
+ }
+
+ public final class ImsSsData implements android.os.Parcelable {
+ ctor public ImsSsData();
+ method public int describeContents();
+ method public boolean isTypeBarring();
+ method public boolean isTypeCf();
+ method public boolean isTypeClip();
+ method public boolean isTypeClir();
+ method public boolean isTypeColp();
+ method public boolean isTypeColr();
+ method public boolean isTypeCw();
+ method public boolean isTypeIcb();
+ method public boolean isTypeInterrogation();
+ method public boolean isTypeUnConditional();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsData> CREATOR;
+ field public static final int SS_ACTIVATION = 0; // 0x0
+ field public static final int SS_ALL_BARRING = 18; // 0x12
+ field public static final int SS_ALL_DATA_TELESERVICES = 3; // 0x3
+ field public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; // 0x5
+ field public static final int SS_ALL_TELESEVICES = 1; // 0x1
+ field public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; // 0x0
+ field public static final int SS_BAIC = 16; // 0x10
+ field public static final int SS_BAIC_ROAMING = 17; // 0x11
+ field public static final int SS_BAOC = 13; // 0xd
+ field public static final int SS_BAOIC = 14; // 0xe
+ field public static final int SS_BAOIC_EXC_HOME = 15; // 0xf
+ field public static final int SS_CFU = 0; // 0x0
+ field public static final int SS_CFUT = 6; // 0x6
+ field public static final int SS_CF_ALL = 4; // 0x4
+ field public static final int SS_CF_ALL_CONDITIONAL = 5; // 0x5
+ field public static final int SS_CF_BUSY = 1; // 0x1
+ field public static final int SS_CF_NOT_REACHABLE = 3; // 0x3
+ field public static final int SS_CF_NO_REPLY = 2; // 0x2
+ field public static final int SS_CLIP = 7; // 0x7
+ field public static final int SS_CLIR = 8; // 0x8
+ field public static final int SS_CNAP = 11; // 0xb
+ field public static final int SS_COLP = 9; // 0x9
+ field public static final int SS_COLR = 10; // 0xa
+ field public static final int SS_DEACTIVATION = 1; // 0x1
+ field public static final int SS_ERASURE = 4; // 0x4
+ field public static final int SS_INCOMING_BARRING = 20; // 0x14
+ field public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; // 0x16
+ field public static final int SS_INCOMING_BARRING_DN = 21; // 0x15
+ field public static final int SS_INTERROGATION = 2; // 0x2
+ field public static final int SS_OUTGOING_BARRING = 19; // 0x13
+ field public static final int SS_REGISTRATION = 3; // 0x3
+ field public static final int SS_SMS_SERVICES = 4; // 0x4
+ field public static final int SS_TELEPHONY = 2; // 0x2
+ field public static final int SS_WAIT = 12; // 0xc
+ }
+
+ public final class ImsSsInfo implements android.os.Parcelable {
+ ctor public ImsSsInfo();
+ method public int describeContents();
+ method public java.lang.String getIcbNum();
+ method public int getStatus();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsInfo> CREATOR;
+ field public static final int DISABLED = 0; // 0x0
+ field public static final int ENABLED = 1; // 0x1
+ field public static final int NOT_REGISTERED = -1; // 0xffffffff
+ }
+
+ public final class ImsStreamMediaProfile implements android.os.Parcelable {
+ method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
+ method public int describeContents();
+ method public int getAudioDirection();
+ method public int getAudioQuality();
+ method public int getRttMode();
+ method public int getVideoDirection();
+ method public int getVideoQuality();
+ method public boolean isRttCall();
+ method public void setRttMode(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
+ field public static final int AUDIO_QUALITY_AMR_WB = 2; // 0x2
+ field public static final int AUDIO_QUALITY_EVRC = 4; // 0x4
+ field public static final int AUDIO_QUALITY_EVRC_B = 5; // 0x5
+ field public static final int AUDIO_QUALITY_EVRC_NW = 7; // 0x7
+ field public static final int AUDIO_QUALITY_EVRC_WB = 6; // 0x6
+ field public static final int AUDIO_QUALITY_EVS_FB = 20; // 0x14
+ field public static final int AUDIO_QUALITY_EVS_NB = 17; // 0x11
+ field public static final int AUDIO_QUALITY_EVS_SWB = 19; // 0x13
+ field public static final int AUDIO_QUALITY_EVS_WB = 18; // 0x12
+ field public static final int AUDIO_QUALITY_G711A = 13; // 0xd
+ field public static final int AUDIO_QUALITY_G711AB = 15; // 0xf
+ field public static final int AUDIO_QUALITY_G711U = 11; // 0xb
+ field public static final int AUDIO_QUALITY_G722 = 14; // 0xe
+ field public static final int AUDIO_QUALITY_G723 = 12; // 0xc
+ field public static final int AUDIO_QUALITY_G729 = 16; // 0x10
+ field public static final int AUDIO_QUALITY_GSM_EFR = 8; // 0x8
+ field public static final int AUDIO_QUALITY_GSM_FR = 9; // 0x9
+ field public static final int AUDIO_QUALITY_GSM_HR = 10; // 0xa
+ field public static final int AUDIO_QUALITY_NONE = 0; // 0x0
+ field public static final int AUDIO_QUALITY_QCELP13K = 3; // 0x3
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsStreamMediaProfile> CREATOR;
+ field public static final int DIRECTION_INACTIVE = 0; // 0x0
+ field public static final int DIRECTION_INVALID = -1; // 0xffffffff
+ field public static final int DIRECTION_RECEIVE = 1; // 0x1
+ field public static final int DIRECTION_SEND = 2; // 0x2
+ field public static final int DIRECTION_SEND_RECEIVE = 3; // 0x3
+ field public static final int RTT_MODE_DISABLED = 0; // 0x0
+ field public static final int RTT_MODE_FULL = 1; // 0x1
+ field public static final int VIDEO_QUALITY_NONE = 0; // 0x0
+ field public static final int VIDEO_QUALITY_QCIF = 1; // 0x1
+ field public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = 2; // 0x2
+ field public static final int VIDEO_QUALITY_QVGA_PORTRAIT = 4; // 0x4
+ field public static final int VIDEO_QUALITY_VGA_LANDSCAPE = 8; // 0x8
+ field public static final int VIDEO_QUALITY_VGA_PORTRAIT = 16; // 0x10
+ }
+
+ public final class ImsSuppServiceNotification implements android.os.Parcelable {
+ ctor public ImsSuppServiceNotification(int, int, int, int, java.lang.String, java.lang.String[]);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSuppServiceNotification> CREATOR;
+ field public final int code;
+ field public final java.lang.String[] history;
+ field public final int index;
+ field public final int notificationType;
+ field public final java.lang.String number;
+ field public final int type;
+ }
+
+ public class ImsUtListener {
+ method public void onSupplementaryServiceIndication(android.telephony.ims.ImsSsData);
+ method public void onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]);
+ method public void onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]);
+ method public void onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]);
+ method public void onUtConfigurationQueried(int, android.os.Bundle);
+ method public void onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo);
+ method public void onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo);
+ method public void onUtConfigurationUpdated(int);
+ }
+
+ public abstract class ImsVideoCallProvider {
+ ctor public ImsVideoCallProvider();
+ method public void changeCallDataUsage(long);
+ method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
+ method public void changePeerDimensions(int, int);
+ method public void changeVideoQuality(int);
+ method public void handleCallSessionEvent(int);
+ method public abstract void onRequestCallDataUsage();
+ method public abstract void onRequestCameraCapabilities();
+ method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile);
+ method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
+ method public abstract void onSetCamera(java.lang.String);
+ method public void onSetCamera(java.lang.String, int);
+ method public abstract void onSetDeviceOrientation(int);
+ method public abstract void onSetDisplaySurface(android.view.Surface);
+ method public abstract void onSetPauseImage(android.net.Uri);
+ method public abstract void onSetPreviewSurface(android.view.Surface);
+ method public abstract void onSetZoom(float);
+ method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
+ method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+ }
+
+}
+
+package android.telephony.ims.feature {
+
+ public final class CapabilityChangeRequest implements android.os.Parcelable {
+ method public void addCapabilitiesToDisableForTech(int, int);
+ method public void addCapabilitiesToEnableForTech(int, int);
+ method public int describeContents();
+ method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable();
+ method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.feature.CapabilityChangeRequest> CREATOR;
+ }
+
+ public static class CapabilityChangeRequest.CapabilityPair {
+ ctor public CapabilityChangeRequest.CapabilityPair(int, int);
+ method public int getCapability();
+ method public int getRadioTech();
+ }
+
+ public abstract class ImsFeature {
+ ctor public ImsFeature();
+ method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public abstract void onFeatureReady();
+ method public abstract void onFeatureRemoved();
+ method public final void setFeatureState(int);
+ field public static final int CAPABILITY_ERROR_GENERIC = -1; // 0xffffffff
+ field public static final int CAPABILITY_SUCCESS = 0; // 0x0
+ field public static final int FEATURE_EMERGENCY_MMTEL = 0; // 0x0
+ field public static final int FEATURE_MMTEL = 1; // 0x1
+ field public static final int FEATURE_RCS = 2; // 0x2
+ field public static final int STATE_INITIALIZING = 1; // 0x1
+ field public static final int STATE_READY = 2; // 0x2
+ field public static final int STATE_UNAVAILABLE = 0; // 0x0
+ }
+
+ protected static class ImsFeature.CapabilityCallbackProxy {
+ method public void onChangeCapabilityConfigurationError(int, int, int);
+ }
+
+ public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
+ ctor public MmTelFeature();
+ method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
+ method public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(android.telephony.ims.ImsCallProfile);
+ method public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
+ method public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint();
+ method public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation();
+ method public android.telephony.ims.stub.ImsUtImplBase getUt();
+ method public final void notifyCapabilitiesStatusChanged(android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+ method public final void notifyIncomingCall(android.telephony.ims.stub.ImsCallSessionImplBase, android.os.Bundle);
+ method public final void notifyVoiceMessageCountUpdate(int);
+ method public void onFeatureReady();
+ method public void onFeatureRemoved();
+ method public boolean queryCapabilityConfiguration(int, int);
+ method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
+ method public void setUiTtyMode(int, android.os.Message);
+ method public int shouldProcessCall(java.lang.String[]);
+ field public static final int PROCESS_CALL_CSFB = 1; // 0x1
+ field public static final int PROCESS_CALL_EMERGENCY_CSFB = 2; // 0x2
+ field public static final int PROCESS_CALL_IMS = 0; // 0x0
+ }
+
+ public static class MmTelFeature.MmTelCapabilities {
+ ctor public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
+ ctor public MmTelFeature.MmTelCapabilities(int);
+ method public final void addCapabilities(int);
+ method public final boolean isCapable(int);
+ method public final void removeCapabilities(int);
+ field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
+ field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
+ field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
+ field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
+ }
+
+ public static abstract class MmTelFeature.MmTelCapabilities.MmTelCapability implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class MmTelFeature.ProcessCallResult implements java.lang.annotation.Annotation {
+ }
+
+ public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
+ ctor public RcsFeature();
+ method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public void onFeatureReady();
+ method public void onFeatureRemoved();
+ }
+
+}
+
+package android.telephony.ims.stub {
+
+ public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
+ ctor public ImsCallSessionImplBase();
+ method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
+ method public void close();
+ method public void deflect(java.lang.String);
+ method public void extendToConference(java.lang.String[]);
+ method public java.lang.String getCallId();
+ method public android.telephony.ims.ImsCallProfile getCallProfile();
+ method public android.telephony.ims.ImsVideoCallProvider getImsVideoCallProvider();
+ method public android.telephony.ims.ImsCallProfile getLocalCallProfile();
+ method public java.lang.String getProperty(java.lang.String);
+ method public android.telephony.ims.ImsCallProfile getRemoteCallProfile();
+ method public int getState();
+ method public void hold(android.telephony.ims.ImsStreamMediaProfile);
+ method public void inviteParticipants(java.lang.String[]);
+ method public boolean isInCall();
+ method public boolean isMultiparty();
+ method public void merge();
+ method public void reject(int);
+ method public void removeParticipants(java.lang.String[]);
+ method public void resume(android.telephony.ims.ImsStreamMediaProfile);
+ method public void sendDtmf(char, android.os.Message);
+ method public void sendRttMessage(java.lang.String);
+ method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile);
+ method public void sendRttModifyResponse(boolean);
+ method public void sendUssd(java.lang.String);
+ method public void setListener(android.telephony.ims.ImsCallSessionListener);
+ method public void setMute(boolean);
+ method public void start(java.lang.String, android.telephony.ims.ImsCallProfile);
+ method public void startConference(java.lang.String[], android.telephony.ims.ImsCallProfile);
+ method public void startDtmf(char);
+ method public void stopDtmf();
+ method public void terminate(int);
+ method public void update(int, android.telephony.ims.ImsStreamMediaProfile);
+ field public static final int USSD_MODE_NOTIFY = 0; // 0x0
+ field public static final int USSD_MODE_REQUEST = 1; // 0x1
+ }
+
+ public static class ImsCallSessionImplBase.State {
+ method public static java.lang.String toString(int);
+ field public static final int ESTABLISHED = 4; // 0x4
+ field public static final int ESTABLISHING = 3; // 0x3
+ field public static final int IDLE = 0; // 0x0
+ field public static final int INITIATED = 1; // 0x1
+ field public static final int INVALID = -1; // 0xffffffff
+ field public static final int NEGOTIATING = 2; // 0x2
+ field public static final int REESTABLISHING = 6; // 0x6
+ field public static final int RENEGOTIATING = 5; // 0x5
+ field public static final int TERMINATED = 8; // 0x8
+ field public static final int TERMINATING = 7; // 0x7
+ }
+
+ public class ImsConfigImplBase {
+ ctor public ImsConfigImplBase();
+ method public int getConfigInt(int);
+ method public java.lang.String getConfigString(int);
+ method public final void notifyProvisionedValueChanged(int, int);
+ method public final void notifyProvisionedValueChanged(int, java.lang.String);
+ method public int setConfig(int, int);
+ method public int setConfig(int, java.lang.String);
+ field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
+ field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0
+ field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public class ImsEcbmImplBase {
+ ctor public ImsEcbmImplBase();
+ method public final void enteredEcbm();
+ method public void exitEmergencyCallbackMode();
+ method public final void exitedEcbm();
+ }
+
+ public final class ImsFeatureConfiguration implements android.os.Parcelable {
+ ctor public ImsFeatureConfiguration();
+ method public int describeContents();
+ method public int[] getServiceFeatures();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR;
+ }
+
+ public static class ImsFeatureConfiguration.Builder {
+ ctor public ImsFeatureConfiguration.Builder();
+ method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int);
+ method public android.telephony.ims.stub.ImsFeatureConfiguration build();
+ }
+
+ public class ImsMultiEndpointImplBase {
+ ctor public ImsMultiEndpointImplBase();
+ method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>);
+ method public void requestImsExternalCallStateInfo();
+ }
+
+ public class ImsRegistrationImplBase {
+ ctor public ImsRegistrationImplBase();
+ method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
+ method public final void onRegistered(int);
+ method public final void onRegistering(int);
+ method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
+ method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+ field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
+ field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
+ field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
+ }
+
+ public class ImsSmsImplBase {
+ ctor public ImsSmsImplBase();
+ method public void acknowledgeSms(int, int, int);
+ method public void acknowledgeSmsReport(int, int, int);
+ method public java.lang.String getSmsFormat();
+ method public void onReady();
+ method public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSmsReceived(int, java.lang.String, byte[]) throws java.lang.RuntimeException;
+ method public final void onSmsStatusReportReceived(int, int, java.lang.String, byte[]) throws java.lang.RuntimeException;
+ method public void sendSms(int, int, java.lang.String, java.lang.String, boolean, byte[]);
+ field public static final int DELIVER_STATUS_ERROR = 2; // 0x2
+ field public static final int DELIVER_STATUS_OK = 1; // 0x1
+ field public static final int SEND_STATUS_ERROR = 2; // 0x2
+ field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4
+ field public static final int SEND_STATUS_ERROR_RETRY = 3; // 0x3
+ field public static final int SEND_STATUS_OK = 1; // 0x1
+ field public static final int STATUS_REPORT_STATUS_ERROR = 2; // 0x2
+ field public static final int STATUS_REPORT_STATUS_OK = 1; // 0x1
+ }
+
+ public class ImsUtImplBase {
+ ctor public ImsUtImplBase();
+ method public void close();
+ method public int queryCallBarring(int);
+ method public int queryCallBarringForServiceClass(int, int);
+ method public int queryCallForward(int, java.lang.String);
+ method public int queryCallWaiting();
+ method public int queryClip();
+ method public int queryClir();
+ method public int queryColp();
+ method public int queryColr();
+ method public void setListener(android.telephony.ims.ImsUtListener);
+ method public int transact(android.os.Bundle);
+ method public int updateCallBarring(int, int, java.lang.String[]);
+ method public int updateCallBarringForServiceClass(int, int, java.lang.String[], int);
+ method public int updateCallForward(int, int, java.lang.String, int, int);
+ method public int updateCallWaiting(boolean, int);
+ method public int updateClip(boolean);
+ method public int updateClir(int);
+ method public int updateColp(boolean);
+ method public int updateColr(int);
}
}
package android.telephony.mbms {
- public final class DownloadRequest implements android.os.Parcelable {
- method public byte[] getOpaqueData();
- }
-
public static class DownloadRequest.Builder {
- method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]);
method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
}
@@ -4176,11 +5330,11 @@
method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public void dispose(int) throws android.os.RemoteException;
method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
- method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 75ff437..bf3d0c2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -426,32 +426,6 @@
}
-package android.telecom {
-
- public abstract class Connection extends android.telecom.Conferenceable {
- method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream);
- method public void onStartRtt(android.telecom.Connection.RttTextStream);
- method public void onStopRtt();
- method public final void sendRemoteRttRequest();
- method public final void sendRttInitiationFailure(int);
- method public final void sendRttInitiationSuccess();
- method public final void sendRttSessionRemotelyTerminated();
- field public static final int PROPERTY_IS_RTT = 256; // 0x100
- }
-
- public static final class Connection.RttTextStream {
- method public java.lang.String read() throws java.io.IOException;
- method public java.lang.String readImmediately() throws java.io.IOException;
- method public void write(java.lang.String) throws java.io.IOException;
- }
-
- public final class ConnectionRequest implements android.os.Parcelable {
- method public android.telecom.Connection.RttTextStream getRttTextStream();
- method public boolean isRequestingRtt();
- }
-
-}
-
package android.telephony {
public class MbmsDownloadSession implements java.lang.AutoCloseable {
@@ -495,11 +469,11 @@
method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public void dispose(int) throws android.os.RemoteException;
method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
- method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
@@ -745,8 +719,8 @@
}
public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
- method public final void setActionButton(int);
- method public final void setButtonState(int);
+ method public void setActionButton(int);
+ method public void setButtonState(int);
}
public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index ab075ee..238cb65 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -167,6 +167,8 @@
} else if (opt.equals("--no_window_animation")
|| opt.equals("--no-window-animation")) {
instrument.noWindowAnimation = true;
+ } else if (opt.equals("--no-hidden-api-checks")) {
+ instrument.disableHiddenApiChecks = true;
} else if (opt.equals("--user")) {
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index b69ef1c..432e890 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -52,12 +52,17 @@
public boolean rawMode = false;
public boolean proto = false;
public boolean noWindowAnimation = false;
+ public boolean disableHiddenApiChecks = false;
public String abi = null;
public int userId = UserHandle.USER_CURRENT;
public Bundle args = new Bundle();
// Required
public String componentNameArg;
+ // Disable hidden API checks for the newly started instrumentation.
+ // Must be kept in sync with ActivityManagerService.
+ private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
/**
* Construct the instrument command runner.
*/
@@ -416,7 +421,8 @@
}
// Start the instrumentation
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+ int flags = disableHiddenApiChecks ? INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS : 0;
+ if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
}
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index adbe9d0..34f6d7d 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -38,6 +39,9 @@
+ "\n"
+ "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n"
+ " Set the current usb function and optionally the data lock state.\n\n"
+ + " svc usb setScreenUnlockedFunctions [function]\n"
+ + " Sets the functions which, if the device was charging,"
+ + " become current on screen unlock.\n"
+ " svc usb getFunction\n"
+ " Gets the list of currently enabled functions\n";
}
@@ -62,6 +66,16 @@
} else if ("getFunction".equals(args[1])) {
System.err.println(SystemProperties.get("sys.usb.config"));
return;
+ } else if ("setScreenUnlockedFunctions".equals(args[1])) {
+ IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
+ Context.USB_SERVICE));
+ try {
+ usbMgr.setScreenUnlockedFunctions((args.length >= 3 ? args[2] :
+ UsbManager.USB_FUNCTION_NONE));
+ } catch (RemoteException e) {
+ System.err.println("Error communicating with UsbManager: " + e);
+ }
+ return;
}
}
System.err.println(longHelp());
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
deleted file mode 100644
index 955e58e..0000000
--- a/cmds/webview_zygote/Android.mk
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := webview_zygote
-
-LOCAL_SRC_FILES := webview_zygote.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid_runtime \
- libbinder \
- liblog \
- libcutils \
- libutils
-
-LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
-LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
-
-LOCAL_INIT_RC := webview_zygote32.rc
-
-# Always include the 32-bit version of webview_zygote. If the target is 64-bit,
-# also include the 64-bit webview_zygote.
-ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
- LOCAL_INIT_RC += webview_zygote64.rc
-endif
-
-LOCAL_MULTILIB := both
-
-LOCAL_MODULE_STEM_32 := webview_zygote32
-LOCAL_MODULE_STEM_64 := webview_zygote64
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/webview_zygote/webview_zygote.cpp b/cmds/webview_zygote/webview_zygote.cpp
deleted file mode 100644
index 88fee64..0000000
--- a/cmds/webview_zygote/webview_zygote.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#define LOG_TAG "WebViewZygote"
-
-#include <sys/prctl.h>
-
-#include <android_runtime/AndroidRuntime.h>
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-class WebViewRuntime : public AndroidRuntime {
-public:
- WebViewRuntime(char* argBlockStart, size_t argBlockSize)
- : AndroidRuntime(argBlockStart, argBlockSize) {}
-
- ~WebViewRuntime() override {}
-
- void onStarted() override {
- // Nothing to do since this is a zygote server.
- }
-
- void onVmCreated(JNIEnv*) override {
- // Nothing to do when the VM is created in the zygote.
- }
-
- void onZygoteInit() override {
- // Called after a new process is forked.
- sp<ProcessState> proc = ProcessState::self();
- proc->startThreadPool();
- }
-
- void onExit(int code) override {
- IPCThreadState::self()->stopProcess();
- AndroidRuntime::onExit(code);
- }
-};
-
-} // namespace android
-
-int main(int argc, char* const argv[]) {
- if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
- LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
- return 12;
- }
-
- size_t argBlockSize = 0;
- for (int i = 0; i < argc; ++i) {
- argBlockSize += strlen(argv[i]) + 1;
- }
-
- android::WebViewRuntime runtime(argv[0], argBlockSize);
- runtime.addOption("-Xzygote");
-
- android::Vector<android::String8> args;
- runtime.start("com.android.internal.os.WebViewZygoteInit", args, /*zygote=*/ true);
-}
diff --git a/cmds/webview_zygote/webview_zygote32.rc b/cmds/webview_zygote/webview_zygote32.rc
deleted file mode 100644
index b7decc8..0000000
--- a/cmds/webview_zygote/webview_zygote32.rc
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-service webview_zygote32 /system/bin/webview_zygote32
- user webview_zygote
- socket webview_zygote stream 660 webview_zygote system
-
-on property:init.svc.zygote=stopped
- stop webview_zygote32
diff --git a/cmds/webview_zygote/webview_zygote64.rc b/cmds/webview_zygote/webview_zygote64.rc
deleted file mode 100644
index 2935b28..0000000
--- a/cmds/webview_zygote/webview_zygote64.rc
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-service webview_zygote64 /system/bin/webview_zygote64
- user webview_zygote
- socket webview_zygote stream 660 webview_zygote system
-
-on property:init.svc.zygote=stopped
- stop webview_zygote64
diff --git a/legacy-test/api/legacy-test-removed.txt b/config/hiddenapi-blacklist.txt
similarity index 100%
copy from legacy-test/api/legacy-test-removed.txt
copy to config/hiddenapi-blacklist.txt
diff --git a/legacy-test/api/legacy-test-removed.txt b/config/hiddenapi-dark-greylist.txt
similarity index 100%
copy from legacy-test/api/legacy-test-removed.txt
copy to config/hiddenapi-dark-greylist.txt
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 42b0f6b..fb27f74 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -2,3 +2,8 @@
name: "IKeyAttestationApplicationIdProvider.aidl",
srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
}
+
+filegroup {
+ name: "IDropBoxManagerService.aidl",
+ srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index dbdb81c..025609e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -128,6 +128,8 @@
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
+import dalvik.system.VMRuntime;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -7039,11 +7041,12 @@
mFragments.dispatchStart();
mFragments.reportLoaderStart();
- // This property is set for all builds except final release
- boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
boolean isAppDebuggable =
(mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ // This property is set for all non-user builds except final release
+ boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
+
if (isAppDebuggable || isDlwarningEnabled) {
String dlwarning = getDlWarning();
if (dlwarning != null) {
@@ -7064,6 +7067,31 @@
}
}
+ // This property is set for all non-user builds except final release
+ boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
+
+ if (isAppDebuggable || isApiWarningEnabled) {
+ if (!mMainThread.mHiddenApiWarningShown && VMRuntime.getRuntime().hasUsedHiddenApi()) {
+ // Only show the warning once per process.
+ mMainThread.mHiddenApiWarningShown = true;
+
+ String appName = getApplicationInfo().loadLabel(getPackageManager())
+ .toString();
+ String warning = "Detected problems with API compatibility\n"
+ + "(visit g.co/dev/appcompat for more info)";
+ if (isAppDebuggable) {
+ new AlertDialog.Builder(this)
+ .setTitle(appName)
+ .setMessage(warning)
+ .setPositiveButton(android.R.string.ok, null)
+ .setCancelable(false)
+ .show();
+ } else {
+ Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
mActivityTransitionState.enterReady(this);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c8d9839..f2160e1 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -268,4 +268,11 @@
* @param token The IApplicationToken for the activity
*/
public abstract void setFocusedActivity(IBinder token);
+
+ public interface ScreenObserver {
+ public void onAwakeStateChanged(boolean isAwake);
+ public void onKeyguardStateChanged(boolean isShowing);
+ }
+
+ public abstract void registerScreenObserver(ScreenObserver observer);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 45f7eba..565eaeb 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -266,6 +266,7 @@
boolean mJitEnabled = false;
boolean mSomeActivitiesChanged = false;
boolean mUpdatingSystemConfig = false;
+ /* package */ boolean mHiddenApiWarningShown = false;
// These can be accessed by multiple threads; mResourcesManager is the lock.
// XXX For now we keep around information about all packages we have
@@ -366,7 +367,7 @@
ActivityInfo activityInfo;
CompatibilityInfo compatInfo;
- LoadedApk packageInfo;
+ LoadedApk loadedApk;
List<ResultInfo> pendingResults;
List<ReferrerIntent> pendingIntents;
@@ -542,7 +543,7 @@
}
static final class AppBindData {
- LoadedApk info;
+ LoadedApk loadedApk;
String processName;
ApplicationInfo appInfo;
List<ProviderInfo> providers;
@@ -1584,7 +1585,7 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
- r.packageInfo = getPackageInfoNoCheck(
+ r.loadedApk = getLoadedApkNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -1832,9 +1833,11 @@
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
break;
- case ATTACH_AGENT:
- handleAttachAgent((String) msg.obj);
+ case ATTACH_AGENT: {
+ Application app = getApplication();
+ handleAttachAgent((String) msg.obj, app != null ? app.mLoadedApk : null);
break;
+ }
case APPLICATION_INFO_CHANGED:
mUpdatingSystemConfig = true;
try {
@@ -1971,13 +1974,13 @@
return mH;
}
- public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- int flags) {
- return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
+ public final LoadedApk getLoadedApkForPackageName(String packageName,
+ CompatibilityInfo compatInfo, int flags) {
+ return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId());
}
- public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- int flags, int userId) {
+ public final LoadedApk getLoadedApkForPackageName(String packageName,
+ CompatibilityInfo compatInfo, int flags, int userId) {
final boolean differentUser = (UserHandle.myUserId() != userId);
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
@@ -1990,13 +1993,13 @@
ref = mResourcePackages.get(packageName);
}
- LoadedApk packageInfo = ref != null ? ref.get() : null;
- //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
- //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
- // + ": " + packageInfo.mResources.getAssets().isUpToDate());
- if (packageInfo != null && (packageInfo.mResources == null
- || packageInfo.mResources.getAssets().isUpToDate())) {
- if (packageInfo.isSecurityViolation()
+ LoadedApk loadedApk = ref != null ? ref.get() : null;
+ //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk);
+ //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir
+ // + ": " + loadedApk.mResources.getAssets().isUpToDate());
+ if (loadedApk != null && (loadedApk.mResources == null
+ || loadedApk.mResources.getAssets().isUpToDate())) {
+ if (loadedApk.isSecurityViolation()
&& (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
throw new SecurityException(
"Requesting code from " + packageName
@@ -2004,7 +2007,7 @@
+ mBoundApplication.processName
+ "/" + mBoundApplication.appInfo.uid);
}
- return packageInfo;
+ return loadedApk;
}
}
@@ -2019,13 +2022,13 @@
}
if (ai != null) {
- return getPackageInfo(ai, compatInfo, flags);
+ return getLoadedApk(ai, compatInfo, flags);
}
return null;
}
- public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
+ public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo,
int flags) {
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
@@ -2047,16 +2050,16 @@
throw new SecurityException(msg);
}
}
- return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
+ return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode,
registerPackage);
}
- public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+ public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
- return getPackageInfo(ai, compatInfo, null, false, true, false);
+ return getLoadedApk(ai, compatInfo, null, false, true, false);
}
- public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
+ public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) {
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
@@ -2068,7 +2071,7 @@
}
}
- private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
+ private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
@@ -2083,35 +2086,35 @@
ref = mResourcePackages.get(aInfo.packageName);
}
- LoadedApk packageInfo = ref != null ? ref.get() : null;
- if (packageInfo == null || (packageInfo.mResources != null
- && !packageInfo.mResources.getAssets().isUpToDate())) {
+ LoadedApk loadedApk = ref != null ? ref.get() : null;
+ if (loadedApk == null || (loadedApk.mResources != null
+ && !loadedApk.mResources.getAssets().isUpToDate())) {
if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
? mBoundApplication.processName : null)
+ ")");
- packageInfo =
+ loadedApk =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
if (mSystemThread && "android".equals(aInfo.packageName)) {
- packageInfo.installSystemApplicationInfo(aInfo,
- getSystemContext().mPackageInfo.getClassLoader());
+ loadedApk.installSystemApplicationInfo(aInfo,
+ getSystemContext().mLoadedApk.getClassLoader());
}
if (differentUser) {
// Caching not supported across users
} else if (includeCode) {
mPackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
+ new WeakReference<LoadedApk>(loadedApk));
} else {
mResourcePackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
+ new WeakReference<LoadedApk>(loadedApk));
}
}
- return packageInfo;
+ return loadedApk;
}
}
@@ -2645,8 +2648,8 @@
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
ActivityInfo aInfo = r.activityInfo;
- if (r.packageInfo == null) {
- r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
+ if (r.loadedApk == null) {
+ r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
@@ -2683,15 +2686,15 @@
}
try {
- Application app = r.packageInfo.makeApplication(false, mInstrumentation);
+ Application app = r.loadedApk.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
- + ", pkg=" + r.packageInfo.getPackageName()
+ + ", pkg=" + r.loadedApk.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
- + ", dir=" + r.packageInfo.getAppDir());
+ + ", dir=" + r.loadedApk.getAppDir());
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2809,7 +2812,7 @@
}
ContextImpl appContext = ContextImpl.createActivityContext(
- this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
+ this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig);
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
@@ -2817,7 +2820,7 @@
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
- && r.packageInfo.mPackageName.contains(pkgName)) {
+ && r.loadedApk.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
@@ -3119,11 +3122,23 @@
}
}
- static final void handleAttachAgent(String agent) {
+ private static boolean attemptAttachAgent(String agent, ClassLoader classLoader) {
try {
- VMDebug.attachAgent(agent);
+ VMDebug.attachAgent(agent, classLoader);
+ return true;
} catch (IOException e) {
- Slog.e(TAG, "Attaching agent failed: " + agent);
+ Slog.e(TAG, "Attaching agent with " + classLoader + " failed: " + agent);
+ return false;
+ }
+ }
+
+ static void handleAttachAgent(String agent, LoadedApk loadedApk) {
+ ClassLoader classLoader = loadedApk != null ? loadedApk.getClassLoader() : null;
+ if (attemptAttachAgent(agent, classLoader)) {
+ return;
+ }
+ if (classLoader != null) {
+ attemptAttachAgent(agent, null);
}
}
@@ -3145,7 +3160,7 @@
String component = data.intent.getComponent().getClassName();
- LoadedApk packageInfo = getPackageInfoNoCheck(
+ LoadedApk loadedApk = getLoadedApkNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManager.getService();
@@ -3154,7 +3169,7 @@
BroadcastReceiver receiver;
ContextImpl context;
try {
- app = packageInfo.makeApplication(false, mInstrumentation);
+ app = loadedApk.makeApplication(false, mInstrumentation);
context = (ContextImpl) app.getBaseContext();
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
@@ -3178,9 +3193,9 @@
TAG, "Performing receive of " + data.intent
+ ": app=" + app
+ ", appName=" + app.getPackageName()
- + ", pkg=" + packageInfo.getPackageName()
+ + ", pkg=" + loadedApk.getPackageName()
+ ", comp=" + data.intent.getComponent().toShortString()
- + ", dir=" + packageInfo.getAppDir());
+ + ", dir=" + loadedApk.getAppDir());
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
@@ -3225,8 +3240,8 @@
unscheduleGcIdler();
// instantiate the BackupAgent class named in the manifest
- LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
- String packageName = packageInfo.mPackageName;
+ LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+ String packageName = loadedApk.mPackageName;
if (packageName == null) {
Slog.d(TAG, "Asked to create backup agent for nonexistent package");
return;
@@ -3252,11 +3267,11 @@
try {
if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ java.lang.ClassLoader cl = loadedApk.getClassLoader();
agent = (BackupAgent) cl.loadClass(classname).newInstance();
// set up the agent's context
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+ ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
context.setOuterContext(agent);
agent.attach(context);
@@ -3292,8 +3307,8 @@
private void handleDestroyBackupAgent(CreateBackupAgentData data) {
if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
- LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
- String packageName = packageInfo.mPackageName;
+ LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+ String packageName = loadedApk.mPackageName;
BackupAgent agent = mBackupAgents.get(packageName);
if (agent != null) {
try {
@@ -3313,11 +3328,11 @@
// we are back active so skip it.
unscheduleGcIdler();
- LoadedApk packageInfo = getPackageInfoNoCheck(
+ LoadedApk loadedApk = getLoadedApkNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ java.lang.ClassLoader cl = loadedApk.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
@@ -3330,10 +3345,10 @@
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+ ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
context.setOuterContext(service);
- Application app = packageInfo.makeApplication(false, mInstrumentation);
+ Application app = loadedApk.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
@@ -3943,7 +3958,7 @@
Bundle.dumpStats(pw, persistentState);
if (ex instanceof TransactionTooLargeException
- && activity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+ && activity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) {
Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
return;
}
@@ -4238,11 +4253,11 @@
}
private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
- LoadedApk apk = peekPackageInfo(data.pkg, false);
+ LoadedApk apk = peekLoadedApk(data.pkg, false);
if (apk != null) {
apk.setCompatibilityInfo(data.info);
}
- apk = peekPackageInfo(data.pkg, true);
+ apk = peekLoadedApk(data.pkg, true);
if (apk != null) {
apk.setCompatibilityInfo(data.info);
}
@@ -4739,7 +4754,7 @@
if (a != null) {
Configuration thisConfig = applyConfigCompatMainThread(
mCurDefaultDisplayDpi, newConfig,
- ar.packageInfo.getCompatibilityInfo());
+ ar.loadedApk.getCompatibilityInfo());
if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
// If the activity is currently resumed, its configuration
// needs to change right now.
@@ -5209,7 +5224,7 @@
}
final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
- boolean hasPkgInfo = false;
+ boolean hasLoadedApk = false;
switch (cmd) {
case ApplicationThreadConstants.PACKAGE_REMOVED:
case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
@@ -5220,14 +5235,14 @@
}
synchronized (mResourcesManager) {
for (int i = packages.length - 1; i >= 0; i--) {
- if (!hasPkgInfo) {
+ if (!hasLoadedApk) {
WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
if (ref != null && ref.get() != null) {
- hasPkgInfo = true;
+ hasLoadedApk = true;
} else {
ref = mResourcePackages.get(packages[i]);
if (ref != null && ref.get() != null) {
- hasPkgInfo = true;
+ hasLoadedApk = true;
}
}
}
@@ -5247,21 +5262,21 @@
synchronized (mResourcesManager) {
for (int i = packages.length - 1; i >= 0; i--) {
WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
- LoadedApk pkgInfo = ref != null ? ref.get() : null;
- if (pkgInfo != null) {
- hasPkgInfo = true;
+ LoadedApk loadedApk = ref != null ? ref.get() : null;
+ if (loadedApk != null) {
+ hasLoadedApk = true;
} else {
ref = mResourcePackages.get(packages[i]);
- pkgInfo = ref != null ? ref.get() : null;
- if (pkgInfo != null) {
- hasPkgInfo = true;
+ loadedApk = ref != null ? ref.get() : null;
+ if (loadedApk != null) {
+ hasLoadedApk = true;
}
}
// If the package is being replaced, yet it still has a valid
// LoadedApk object, the package was updated with _DONT_KILL.
// Adjust it's internal references to the application info and
// resources.
- if (pkgInfo != null) {
+ if (loadedApk != null) {
try {
final String packageName = packages[i];
final ApplicationInfo aInfo =
@@ -5275,13 +5290,13 @@
if (ar.activityInfo.applicationInfo.packageName
.equals(packageName)) {
ar.activityInfo.applicationInfo = aInfo;
- ar.packageInfo = pkgInfo;
+ ar.loadedApk = loadedApk;
}
}
}
final List<String> oldPaths =
sPackageManager.getPreviousCodePaths(packageName);
- pkgInfo.updateApplicationInfo(aInfo, oldPaths);
+ loadedApk.updateApplicationInfo(aInfo, oldPaths);
} catch (RemoteException e) {
}
}
@@ -5290,7 +5305,7 @@
break;
}
}
- ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
+ ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
}
final void handleLowMemory() {
@@ -5441,12 +5456,16 @@
mCompatConfiguration = new Configuration(data.config);
mProfiler = new Profiler();
+ String agent = null;
if (data.initProfilerInfo != null) {
mProfiler.profileFile = data.initProfilerInfo.profileFile;
mProfiler.profileFd = data.initProfilerInfo.profileFd;
mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval;
mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler;
mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput;
+ if (data.initProfilerInfo.attachAgentDuringBind) {
+ agent = data.initProfilerInfo.agent;
+ }
}
// send up app name; do this *before* waiting for debugger
@@ -5494,7 +5513,11 @@
applyCompatConfiguration(mCurDefaultDisplayDpi);
}
- data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+ data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+
+ if (agent != null) {
+ handleAttachAgent(agent, data.loadedApk);
+ }
/**
* Switch this process to density compatibility mode if needed.
@@ -5562,7 +5585,7 @@
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
- Slog.w(TAG, "Application " + data.info.getPackageName()
+ Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+ " is waiting for the debugger on port 8100...");
IActivityManager mgr = ActivityManager.getService();
@@ -5581,7 +5604,7 @@
}
} else {
- Slog.w(TAG, "Application " + data.info.getPackageName()
+ Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+ " can be debugged on port 8100...");
}
}
@@ -5629,14 +5652,14 @@
mInstrumentationAppDir = ii.sourceDir;
mInstrumentationSplitAppDirs = ii.splitSourceDirs;
mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
- mInstrumentedAppDir = data.info.getAppDir();
- mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
- mInstrumentedLibDir = data.info.getLibDir();
+ mInstrumentedAppDir = data.loadedApk.getAppDir();
+ mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs();
+ mInstrumentedLibDir = data.loadedApk.getLibDir();
} else {
ii = null;
}
- final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
+ final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk);
updateLocaleListFromAppContext(appContext,
mResourcesManager.getConfiguration().getLocales());
@@ -5666,9 +5689,9 @@
final ApplicationInfo instrApp = new ApplicationInfo();
ii.copyTo(instrApp);
instrApp.initForUser(UserHandle.myUserId());
- final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
+ final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
- final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
+ final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk);
try {
final ClassLoader cl = instrContext.getClassLoader();
@@ -5712,7 +5735,7 @@
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
- app = data.info.makeApplication(data.restrictedBackupMode, null);
+ app = data.loadedApk.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
@@ -5766,7 +5789,7 @@
final int preloadedFontsResource = info.metaData.getInt(
ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
if (preloadedFontsResource != 0) {
- data.info.getResources().preloadFonts(preloadedFontsResource);
+ data.loadedApk.getResources().preloadFonts(preloadedFontsResource);
}
}
} catch (RemoteException e) {
@@ -6361,8 +6384,8 @@
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
- this, getSystemContext().mPackageInfo);
- mInitialApplication = context.mPackageInfo.makeApplication(true, null);
+ this, getSystemContext().mLoadedApk);
+ mInitialApplication = context.mLoadedApk.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b331d84..e8fbbd7 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -252,8 +252,10 @@
public static final int OP_INSTANT_APP_START_FOREGROUND = 68;
/** @hide Answer incoming phone calls */
public static final int OP_ANSWER_PHONE_CALLS = 69;
+ /** @hide Continue handover of a call from another app */
+ public static final int OP_ACCEPT_HANDOVER = 70;
/** @hide */
- public static final int _NUM_OP = 70;
+ public static final int _NUM_OP = 71;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -365,6 +367,12 @@
/** Answer incoming phone calls */
public static final String OPSTR_ANSWER_PHONE_CALLS
= "android:answer_phone_calls";
+ /***
+ * Accept call handover
+ * @hide
+ */
+ public static final String OPSTR_ACCEPT_HANDOVER
+ = "android:accept_handover";
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
@@ -400,6 +408,7 @@
OP_USE_SIP,
OP_PROCESS_OUTGOING_CALLS,
OP_ANSWER_PHONE_CALLS,
+ OP_ACCEPT_HANDOVER,
// Microphone
OP_RECORD_AUDIO,
// Camera
@@ -492,7 +501,8 @@
OP_REQUEST_INSTALL_PACKAGES,
OP_PICTURE_IN_PICTURE,
OP_INSTANT_APP_START_FOREGROUND,
- OP_ANSWER_PHONE_CALLS
+ OP_ANSWER_PHONE_CALLS,
+ OP_ACCEPT_HANDOVER
};
/**
@@ -570,6 +580,7 @@
OPSTR_PICTURE_IN_PICTURE,
OPSTR_INSTANT_APP_START_FOREGROUND,
OPSTR_ANSWER_PHONE_CALLS,
+ OPSTR_ACCEPT_HANDOVER
};
/**
@@ -647,6 +658,7 @@
"PICTURE_IN_PICTURE",
"INSTANT_APP_START_FOREGROUND",
"ANSWER_PHONE_CALLS",
+ "ACCEPT_HANDOVER"
};
/**
@@ -724,6 +736,7 @@
null, // no permission for entering picture-in-picture on hide
Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
Manifest.permission.ANSWER_PHONE_CALLS,
+ Manifest.permission.ACCEPT_HANDOVER
};
/**
@@ -802,6 +815,7 @@
null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
null, // INSTANT_APP_START_FOREGROUND
null, // ANSWER_PHONE_CALLS
+ null, // ACCEPT_HANDOVER
};
/**
@@ -879,6 +893,7 @@
false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
false, // INSTANT_APP_START_FOREGROUND
false, // ANSWER_PHONE_CALLS
+ false, // ACCEPT_HANDOVER
};
/**
@@ -955,6 +970,7 @@
AppOpsManager.MODE_ALLOWED, // OP_PICTURE_IN_PICTURE
AppOpsManager.MODE_DEFAULT, // OP_INSTANT_APP_START_FOREGROUND
AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
+ AppOpsManager.MODE_ALLOWED, // ACCEPT_HANDOVER
};
/**
@@ -1035,6 +1051,7 @@
false, // OP_PICTURE_IN_PICTURE
false,
false, // ANSWER_PHONE_CALLS
+ false, // ACCEPT_HANDOVER
};
/**
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 156df36..5822f5c 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -187,7 +187,7 @@
*/
/* package */ final void attach(Context context) {
attachBaseContext(context);
- mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
+ mLoadedApk = ContextImpl.getImpl(context).mLoadedApk;
}
/* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0eafdec..5bc1e76 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -56,6 +56,7 @@
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
@@ -122,6 +123,8 @@
private UserManager mUserManager;
@GuardedBy("mLock")
private PackageInstaller mInstaller;
+ @GuardedBy("mLock")
+ private ArtManager mArtManager;
@GuardedBy("mDelegates")
private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
@@ -1379,7 +1382,7 @@
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
- mContext.mPackageInfo);
+ mContext.mLoadedApk);
if (r != null) {
return r;
}
@@ -2763,4 +2766,18 @@
throw e.rethrowAsRuntimeException();
}
}
+
+ @Override
+ public ArtManager getArtManager() {
+ synchronized (mLock) {
+ if (mArtManager == null) {
+ try {
+ mArtManager = new ArtManager(mPM.getArtManager());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return mArtManager;
+ }
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5f34322..64ddfbc 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -159,7 +159,7 @@
private ArrayMap<String, File> mSharedPrefsPaths;
final @NonNull ActivityThread mMainThread;
- final @NonNull LoadedApk mPackageInfo;
+ final @NonNull LoadedApk mLoadedApk;
private @Nullable ClassLoader mClassLoader;
private final @Nullable IBinder mActivityToken;
@@ -252,8 +252,8 @@
@Override
public Context getApplicationContext() {
- return (mPackageInfo != null) ?
- mPackageInfo.getApplication() : mMainThread.getApplication();
+ return (mLoadedApk != null) ?
+ mLoadedApk.getApplication() : mMainThread.getApplication();
}
@Override
@@ -297,15 +297,15 @@
@Override
public ClassLoader getClassLoader() {
- return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
+ return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader());
}
@Override
public String getPackageName() {
- if (mPackageInfo != null) {
- return mPackageInfo.getPackageName();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getPackageName();
}
- // No mPackageInfo means this is a Context for the system itself,
+ // No mLoadedApk means this is a Context for the system itself,
// and this here is its name.
return "android";
}
@@ -324,24 +324,24 @@
@Override
public ApplicationInfo getApplicationInfo() {
- if (mPackageInfo != null) {
- return mPackageInfo.getApplicationInfo();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getApplicationInfo();
}
throw new RuntimeException("Not supported in system context");
}
@Override
public String getPackageResourcePath() {
- if (mPackageInfo != null) {
- return mPackageInfo.getResDir();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getResDir();
}
throw new RuntimeException("Not supported in system context");
}
@Override
public String getPackageCodePath() {
- if (mPackageInfo != null) {
- return mPackageInfo.getAppDir();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getAppDir();
}
throw new RuntimeException("Not supported in system context");
}
@@ -351,7 +351,7 @@
// At least one application in the world actually passes in a null
// name. This happened to work because when we generated the file name
// we would stringify it to "null.xml". Nice.
- if (mPackageInfo.getApplicationInfo().targetSdkVersion <
+ if (mLoadedApk.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
@@ -1093,11 +1093,11 @@
warnIfCallingFromSystemProcess();
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1197,11 +1197,11 @@
Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1251,11 +1251,11 @@
warnIfCallingFromSystemProcess();
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1333,11 +1333,11 @@
Bundle initialExtras) {
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1414,11 +1414,11 @@
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
- if (mPackageInfo != null && context != null) {
+ if (mLoadedApk != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
@@ -1445,8 +1445,8 @@
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
- if (mPackageInfo != null) {
- IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
+ if (mLoadedApk != null) {
+ IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher(
getOuterContext(), receiver);
try {
ActivityManager.getService().unregisterReceiver(rd);
@@ -1579,7 +1579,7 @@
@Override
public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
int flags) {
- return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+ return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
/** @hide */
@@ -1601,16 +1601,16 @@
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
- if (mPackageInfo != null) {
- sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+ if (mLoadedApk != null) {
+ sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
IBinder token = getActivityToken();
- if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
- && mPackageInfo.getApplicationInfo().targetSdkVersion
+ if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null
+ && mLoadedApk.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
@@ -1634,8 +1634,8 @@
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
- if (mPackageInfo != null) {
- IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+ if (mLoadedApk != null) {
+ IServiceConnection sd = mLoadedApk.forgetServiceDispatcher(
getOuterContext(), conn);
try {
ActivityManager.getService().unbindService(sd);
@@ -1980,40 +1980,20 @@
}
}
- private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
- int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
- final String[] splitResDirs;
- final ClassLoader classLoader;
- try {
- splitResDirs = pi.getSplitPaths(splitName);
- classLoader = pi.getSplitClassLoader(splitName);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- return ResourcesManager.getInstance().getResources(activityToken,
- pi.getResDir(),
- splitResDirs,
- pi.getOverlayDirs(),
- pi.getApplicationInfo().sharedLibraryFiles,
- displayId,
- overrideConfig,
- compatInfo,
- classLoader);
- }
-
@Override
public Context createApplicationContext(ApplicationInfo application, int flags)
throws NameNotFoundException {
- LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
+ LoadedApk loadedApk = mMainThread.getLoadedApk(application,
+ mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
- if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
+ if (loadedApk != null) {
+ ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken,
new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+ c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -2037,20 +2017,21 @@
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
- return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
+ return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user,
flags, null);
}
- LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
+ LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName,
+ mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
- if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
+ if (loadedApk != null) {
+ ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user,
flags, null);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+ c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -2064,30 +2045,21 @@
@Override
public Context createContextForSplit(String splitName) throws NameNotFoundException {
- if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+ if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
// All Splits are always loaded.
return this;
}
- final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
- final String[] paths = mPackageInfo.getSplitPaths(splitName);
+ final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName);
- final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
+ final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName,
mActivityToken, mUser, mFlags, classLoader);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- context.setResources(ResourcesManager.getInstance().getResources(
- mActivityToken,
- mPackageInfo.getResDir(),
- paths,
- mPackageInfo.getOverlayDirs(),
- mPackageInfo.getApplicationInfo().sharedLibraryFiles,
- displayId,
- null,
- mPackageInfo.getCompatibilityInfo(),
- classLoader));
+ context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName,
+ mActivityToken, displayId));
return context;
}
@@ -2097,11 +2069,11 @@
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+ ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
mActivityToken, mUser, mFlags, mClassLoader);
final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+ context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
return context;
}
@@ -2112,11 +2084,11 @@
throw new IllegalArgumentException("display must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+ ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
mActivityToken, mUser, mFlags, mClassLoader);
final int displayId = display.getDisplayId();
- context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+ context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
context.mDisplay = display;
return context;
@@ -2126,7 +2098,7 @@
public Context createDeviceProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+ return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
flags, mClassLoader);
}
@@ -2134,7 +2106,7 @@
public Context createCredentialProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+ return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
flags, mClassLoader);
}
@@ -2183,14 +2155,14 @@
@Override
public File getDataDir() {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
File res = null;
if (isCredentialProtectedStorage()) {
- res = mPackageInfo.getCredentialProtectedDataDirFile();
+ res = mLoadedApk.getCredentialProtectedDataDirFile();
} else if (isDeviceProtectedStorage()) {
- res = mPackageInfo.getDeviceProtectedDataDirFile();
+ res = mLoadedApk.getDeviceProtectedDataDirFile();
} else {
- res = mPackageInfo.getDataDirFile();
+ res = mLoadedApk.getDataDirFile();
}
if (res != null) {
@@ -2241,10 +2213,10 @@
}
static ContextImpl createSystemContext(ActivityThread mainThread) {
- LoadedApk packageInfo = new LoadedApk(mainThread);
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+ LoadedApk loadedApk = new LoadedApk(mainThread);
+ ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
null);
- context.setResources(packageInfo.getResources());
+ context.setResources(loadedApk.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetrics());
return context;
@@ -2255,35 +2227,35 @@
* Make sure that the created system UI context shares the same LoadedApk as the system context.
*/
static ContextImpl createSystemUiContext(ContextImpl systemContext) {
- final LoadedApk packageInfo = systemContext.mPackageInfo;
- ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
+ final LoadedApk loadedApk = systemContext.mLoadedApk;
+ ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null,
null, null, 0, null);
- context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
- packageInfo.getCompatibilityInfo()));
+ context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null,
+ loadedApk.getCompatibilityInfo()));
return context;
}
- static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
- if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+ static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) {
+ if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
+ ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
null);
- context.setResources(packageInfo.getResources());
+ context.setResources(loadedApk.getResources());
return context;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
- LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
+ LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
- if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+ if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
- String[] splitDirs = packageInfo.getSplitResDirs();
- ClassLoader classLoader = packageInfo.getClassLoader();
+ String[] splitDirs = loadedApk.getSplitResDirs();
+ ClassLoader classLoader = loadedApk.getClassLoader();
- if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+ if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
try {
- classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
- splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
+ classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName);
+ splitDirs = loadedApk.getSplitPaths(activityInfo.splitName);
} catch (NameNotFoundException e) {
// Nothing above us can handle a NameNotFoundException, better crash.
throw new RuntimeException(e);
@@ -2292,14 +2264,14 @@
}
}
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
+ ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName,
activityToken, null, 0, classLoader);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
- ? packageInfo.getCompatibilityInfo()
+ ? loadedApk.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
@@ -2307,10 +2279,10 @@
// Create the base resources for which all configuration contexts for this Activity
// will be rebased upon.
context.setResources(resourcesManager.createBaseActivityResources(activityToken,
- packageInfo.getResDir(),
+ loadedApk.getResDir(),
splitDirs,
- packageInfo.getOverlayDirs(),
- packageInfo.getApplicationInfo().sharedLibraryFiles,
+ loadedApk.getOverlayDirs(),
+ loadedApk.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
@@ -2321,7 +2293,7 @@
}
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
- @NonNull LoadedApk packageInfo, @Nullable String splitName,
+ @NonNull LoadedApk loadedApk, @Nullable String splitName,
@Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
@Nullable ClassLoader classLoader) {
mOuterContext = this;
@@ -2330,10 +2302,10 @@
// location for application.
if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) {
- final File dataDir = packageInfo.getDataDirFile();
- if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) {
+ final File dataDir = loadedApk.getDataDirFile();
+ if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) {
flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) {
+ } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) {
flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
}
}
@@ -2347,7 +2319,7 @@
}
mUser = user;
- mPackageInfo = packageInfo;
+ mLoadedApk = loadedApk;
mSplitName = splitName;
mClassLoader = classLoader;
mResourcesManager = ResourcesManager.getInstance();
@@ -2358,8 +2330,8 @@
setResources(container.mResources);
mDisplay = container.mDisplay;
} else {
- mBasePackageName = packageInfo.mPackageName;
- ApplicationInfo ainfo = packageInfo.getApplicationInfo();
+ mBasePackageName = loadedApk.mPackageName;
+ ApplicationInfo ainfo = loadedApk.getApplicationInfo();
if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
// Special case: system components allow themselves to be loaded in to other
// processes. For purposes of app ops, we must then consider the context as
@@ -2382,7 +2354,7 @@
}
void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
- mPackageInfo.installSystemApplicationInfo(info, classLoader);
+ mLoadedApk.installSystemApplicationInfo(info, classLoader);
}
final void scheduleFinalCleanup(String who, String what) {
@@ -2391,7 +2363,7 @@
final void performFinalCleanup(String who, String what) {
//Log.i(TAG, "Cleanup up context: " + this);
- mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+ mLoadedApk.removeContextRegistrations(getOuterContext(), who, what);
}
final Context getReceiverRestrictedContext() {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index b162cb1..2eabd86 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -291,7 +291,10 @@
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
- mDecor.setVisibility(View.VISIBLE);
+ if (mDecor.getVisibility() != View.VISIBLE) {
+ mDecor.setVisibility(View.VISIBLE);
+ sendShowMessage();
+ }
}
return;
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 1811748..7b1afa5 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -137,6 +137,7 @@
void publishService(in IBinder token, in Intent intent, in IBinder service);
void activityResumed(in IBinder token);
void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
+ void setAgentApp(in String packageName, @nullable String agent);
void setAlwaysFinish(boolean enabled);
boolean startInstrumentation(in ComponentName className, in String profileFile,
int flags, in Bundle arguments, in IInstrumentationWatcher watcher,
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f6d9710..903031e 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -28,13 +28,14 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.dex.ArtManager;
import android.content.pm.split.SplitDependencyLoader;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -730,13 +731,6 @@
}
}
- // Keep in sync with installd (frameworks/native/cmds/installd/commands.cpp).
- private static File getPrimaryProfileFile(String packageName) {
- File profileDir = Environment.getDataProfilesDePackageDirectory(
- UserHandle.myUserId(), packageName);
- return new File(profileDir, "primary.prof");
- }
-
private void setupJitProfileSupport() {
if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
return;
@@ -764,10 +758,12 @@
return;
}
- final File profileFile = getPrimaryProfileFile(mPackageName);
-
- VMRuntime.registerAppInfo(profileFile.getPath(),
- codePaths.toArray(new String[codePaths.size()]));
+ for (int i = codePaths.size() - 1; i >= 0; i--) {
+ String splitName = i == 0 ? null : mApplicationInfo.splitNames[i - 1];
+ String profileFile = ArtManager.getCurrentProfilePath(
+ mPackageName, UserHandle.myUserId(), splitName);
+ VMRuntime.registerAppInfo(profileFile, new String[] {codePaths.get(i)});
+ }
// Register the app data directory with the reporter. It will
// help deciding whether or not a dex file is the primary apk or a
@@ -948,14 +944,78 @@
throw new AssertionError("null split not found");
}
- mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+ mResources = ResourcesManager.getInstance().getResources(
+ null,
+ mResDir,
+ splitPaths,
+ mOverlayDirs,
+ mApplicationInfo.sharedLibraryFiles,
+ Display.DEFAULT_DISPLAY,
+ null,
+ getCompatibilityInfo(),
getClassLoader());
}
return mResources;
}
+ public Resources getOrCreateResourcesForSplit(@NonNull String splitName,
+ @Nullable IBinder activityToken, int displayId) throws NameNotFoundException {
+ return ResourcesManager.getInstance().getResources(
+ activityToken,
+ mResDir,
+ getSplitPaths(splitName),
+ mOverlayDirs,
+ mApplicationInfo.sharedLibraryFiles,
+ displayId,
+ null,
+ getCompatibilityInfo(),
+ getSplitClassLoader(splitName));
+ }
+
+ /**
+ * Creates the top level resources for the given package. Will return an existing
+ * Resources if one has already been created.
+ */
+ public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) {
+ // Request for this app, short circuit
+ if (appInfo.uid == Process.myUid()) {
+ return getResources();
+ }
+
+ // Get resources for a different package
+ return ResourcesManager.getInstance().getResources(
+ null,
+ appInfo.publicSourceDir,
+ appInfo.splitPublicSourceDirs,
+ appInfo.resourceDirs,
+ appInfo.sharedLibraryFiles,
+ Display.DEFAULT_DISPLAY,
+ null,
+ getCompatibilityInfo(),
+ getClassLoader());
+ }
+
+ public Resources createResources(IBinder activityToken, String splitName,
+ int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+ final String[] splitResDirs;
+ final ClassLoader classLoader;
+ try {
+ splitResDirs = getSplitPaths(splitName);
+ classLoader = getSplitClassLoader(splitName);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return ResourcesManager.getInstance().getResources(activityToken,
+ mResDir,
+ splitResDirs,
+ mOverlayDirs,
+ mApplicationInfo.sharedLibraryFiles,
+ displayId,
+ overrideConfig,
+ compatInfo,
+ classLoader);
+ }
+
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index fad4798..8f718df 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -54,14 +54,24 @@
*/
public final String agent;
+ /**
+ * Whether the {@link agent} should be attached early (before bind-application) or during
+ * bind-application. Agents attached prior to binding cannot be loaded from the app's APK
+ * directly and must be given as an absolute path (or available in the default LD_LIBRARY_PATH).
+ * Agents attached during bind-application will miss early setup (e.g., resource initialization
+ * and classloader generation), but are searched in the app's library search path.
+ */
+ public final boolean attachAgentDuringBind;
+
public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop,
- boolean streaming, String agent) {
+ boolean streaming, String agent, boolean attachAgentDuringBind) {
profileFile = filename;
profileFd = fd;
samplingInterval = interval;
autoStopProfiler = autoStop;
streamingOutput = streaming;
this.agent = agent;
+ this.attachAgentDuringBind = attachAgentDuringBind;
}
public ProfilerInfo(ProfilerInfo in) {
@@ -71,6 +81,16 @@
autoStopProfiler = in.autoStopProfiler;
streamingOutput = in.streamingOutput;
agent = in.agent;
+ attachAgentDuringBind = in.attachAgentDuringBind;
+ }
+
+ /**
+ * Return a new ProfilerInfo instance, with fields populated from this object,
+ * and {@link agent} and {@link attachAgentDuringBind} as given.
+ */
+ public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
+ return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
+ this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
}
/**
@@ -109,6 +129,7 @@
out.writeInt(autoStopProfiler ? 1 : 0);
out.writeInt(streamingOutput ? 1 : 0);
out.writeString(agent);
+ out.writeBoolean(attachAgentDuringBind);
}
public static final Parcelable.Creator<ProfilerInfo> CREATOR =
@@ -131,5 +152,6 @@
autoStopProfiler = in.readInt() != 0;
streamingOutput = in.readInt() != 0;
agent = in.readString();
+ attachAgentDuringBind = in.readBoolean();
}
}
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 6dca400..6ac15a5 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -50,11 +50,6 @@
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
final class SharedPreferencesImpl implements SharedPreferences {
private static final String TAG = "SharedPreferencesImpl";
@@ -74,12 +69,18 @@
private final Object mLock = new Object();
private final Object mWritingToDiskLock = new Object();
- private Future<Map<String, Object>> mMap;
+ @GuardedBy("mLock")
+ private Map<String, Object> mMap;
+ @GuardedBy("mLock")
+ private Throwable mThrowable;
@GuardedBy("mLock")
private int mDiskWritesInFlight = 0;
@GuardedBy("mLock")
+ private boolean mLoaded = false;
+
+ @GuardedBy("mLock")
private StructTimespec mStatTimestamp;
@GuardedBy("mLock")
@@ -106,18 +107,28 @@
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
+ mLoaded = false;
mMap = null;
+ mThrowable = null;
startLoadFromDisk();
}
private void startLoadFromDisk() {
- FutureTask<Map<String, Object>> futureTask = new FutureTask<>(() -> loadFromDisk());
- mMap = futureTask;
- new Thread(futureTask, "SharedPreferencesImpl-load").start();
+ synchronized (mLock) {
+ mLoaded = false;
+ }
+ new Thread("SharedPreferencesImpl-load") {
+ public void run() {
+ loadFromDisk();
+ }
+ }.start();
}
- private Map<String, Object> loadFromDisk() {
+ private void loadFromDisk() {
synchronized (mLock) {
+ if (mLoaded) {
+ return;
+ }
if (mBackupFile.exists()) {
mFile.delete();
mBackupFile.renameTo(mFile);
@@ -131,13 +142,14 @@
Map<String, Object> map = null;
StructStat stat = null;
+ Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(
- new FileInputStream(mFile), 16*1024);
+ new FileInputStream(mFile), 16 * 1024);
map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
@@ -146,18 +158,37 @@
}
}
} catch (ErrnoException e) {
- /* ignore */
+ // An errno exception means the stat failed. Treat as empty/non-existing by
+ // ignoring.
+ } catch (Throwable t) {
+ thrown = t;
}
synchronized (mLock) {
- if (map != null) {
- mStatTimestamp = stat.st_mtim;
- mStatSize = stat.st_size;
- } else {
- map = new HashMap<>();
+ mLoaded = true;
+ mThrowable = thrown;
+
+ // It's important that we always signal waiters, even if we'll make
+ // them fail with an exception. The try-finally is pretty wide, but
+ // better safe than sorry.
+ try {
+ if (thrown == null) {
+ if (map != null) {
+ mMap = map;
+ mStatTimestamp = stat.st_mtim;
+ mStatSize = stat.st_size;
+ } else {
+ mMap = new HashMap<>();
+ }
+ }
+ // In case of a thrown exception, we retain the old map. That allows
+ // any open editors to commit and store updates.
+ } catch (Throwable t) {
+ mThrowable = t;
+ } finally {
+ mLock.notifyAll();
}
}
- return map;
}
static File makeBackupFile(File prefsFile) {
@@ -216,42 +247,40 @@
}
}
- private @GuardedBy("mLock") Map<String, Object> getLoaded() {
- // For backwards compatibility, we need to ignore any interrupts. b/70122540.
- for (;;) {
- try {
- return mMap.get();
- } catch (ExecutionException e) {
- throw new IllegalStateException(e);
- } catch (InterruptedException e) {
- // Ignore and try again.
- }
- }
- }
- private @GuardedBy("mLock") Map<String, Object> getLoadedWithBlockGuard() {
- if (!mMap.isDone()) {
+ @GuardedBy("mLock")
+ private void awaitLoadedLocked() {
+ if (!mLoaded) {
// Raise an explicit StrictMode onReadFromDisk for this
// thread, since the real read will be in a different
// thread and otherwise ignored by StrictMode.
BlockGuard.getThreadPolicy().onReadFromDisk();
}
- return getLoaded();
+ while (!mLoaded) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException unused) {
+ }
+ }
+ if (mThrowable != null) {
+ throw new IllegalStateException(mThrowable);
+ }
}
@Override
public Map<String, ?> getAll() {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- return new HashMap<String, Object>(map);
+ awaitLoadedLocked();
+ //noinspection unchecked
+ return new HashMap<String, Object>(mMap);
}
}
@Override
@Nullable
public String getString(String key, @Nullable String defValue) {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- String v = (String) map.get(key);
+ awaitLoadedLocked();
+ String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
@@ -259,65 +288,66 @@
@Override
@Nullable
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- @SuppressWarnings("unchecked")
- Set<String> v = (Set<String>) map.get(key);
+ awaitLoadedLocked();
+ Set<String> v = (Set<String>) mMap.get(key);
return v != null ? v : defValues;
}
}
@Override
public int getInt(String key, int defValue) {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- Integer v = (Integer) map.get(key);
+ awaitLoadedLocked();
+ Integer v = (Integer)mMap.get(key);
return v != null ? v : defValue;
}
}
@Override
public long getLong(String key, long defValue) {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- Long v = (Long) map.get(key);
+ awaitLoadedLocked();
+ Long v = (Long)mMap.get(key);
return v != null ? v : defValue;
}
}
@Override
public float getFloat(String key, float defValue) {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- Float v = (Float) map.get(key);
+ awaitLoadedLocked();
+ Float v = (Float)mMap.get(key);
return v != null ? v : defValue;
}
}
@Override
public boolean getBoolean(String key, boolean defValue) {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- Boolean v = (Boolean) map.get(key);
+ awaitLoadedLocked();
+ Boolean v = (Boolean)mMap.get(key);
return v != null ? v : defValue;
}
}
@Override
public boolean contains(String key) {
- Map<String, Object> map = getLoadedWithBlockGuard();
synchronized (mLock) {
- return map.containsKey(key);
+ awaitLoadedLocked();
+ return mMap.containsKey(key);
}
}
@Override
public Editor edit() {
- // TODO: remove the need to call getLoaded() when
+ // TODO: remove the need to call awaitLoadedLocked() when
// requesting an editor. will require some work on the
// Editor, but then we should be able to do:
//
// context.getSharedPreferences(..).edit().putString(..).apply()
//
// ... all without blocking.
- getLoadedWithBlockGuard();
+ synchronized (mLock) {
+ awaitLoadedLocked();
+ }
return new EditorImpl();
}
@@ -471,43 +501,13 @@
// a memory commit comes in when we're already
// writing to disk.
if (mDiskWritesInFlight > 0) {
- // We can't modify our map as a currently
+ // We can't modify our mMap as a currently
// in-flight write owns it. Clone it before
// modifying it.
// noinspection unchecked
- mMap = new Future<Map<String, Object>>() {
- private Map<String, Object> mCopiedMap =
- new HashMap<String, Object>(getLoaded());
-
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return false;
- }
-
- @Override
- public boolean isCancelled() {
- return false;
- }
-
- @Override
- public boolean isDone() {
- return true;
- }
-
- @Override
- public Map<String, Object> get()
- throws InterruptedException, ExecutionException {
- return mCopiedMap;
- }
-
- @Override
- public Map<String, Object> get(long timeout, TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException {
- return mCopiedMap;
- }
- };
+ mMap = new HashMap<String, Object>(mMap);
}
- mapToWriteToDisk = getLoaded();
+ mapToWriteToDisk = mMap;
mDiskWritesInFlight++;
boolean hasListeners = mListeners.size() > 0;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ab70f0e7..97c6681 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -81,10 +81,10 @@
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
@@ -130,6 +130,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccCardManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
import android.view.ContextThemeWrapper;
@@ -504,6 +505,13 @@
return new EuiccManager(ctx.getOuterContext());
}});
+ registerService(Context.EUICC_CARD_SERVICE, EuiccCardManager.class,
+ new CachedServiceFetcher<EuiccCardManager>() {
+ @Override
+ public EuiccCardManager createService(ContextImpl ctx) {
+ return new EuiccCardManager(ctx.getOuterContext());
+ }});
+
registerService(Context.UI_MODE_SERVICE, UiModeManager.class,
new CachedServiceFetcher<UiModeManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 121b58a..d1500a8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3134,9 +3134,6 @@
/**
* Flag for {@link #wipeData(int)}: also erase the device's eUICC data.
- *
- * TODO(b/35851809): make this public.
- * @hide
*/
public static final int WIPE_EUICC = 0x0004;
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 222e9a0..d33af4f 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -215,6 +215,30 @@
*/
public static final int ROAMING_YES = 0x2;
+ /** @hide */
+ @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+ DEFAULT_NETWORK_ALL,
+ DEFAULT_NETWORK_NO,
+ DEFAULT_NETWORK_YES
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DefaultNetwork {}
+
+ /**
+ * Combined usage for this network regardless of whether it was the active default network.
+ */
+ public static final int DEFAULT_NETWORK_ALL = -1;
+
+ /**
+ * Usage that occurs while this network is not the active default network.
+ */
+ public static final int DEFAULT_NETWORK_NO = 0x1;
+
+ /**
+ * Usage that occurs while this network is the active default network.
+ */
+ public static final int DEFAULT_NETWORK_YES = 0x2;
+
/**
* Special TAG value for total data across all tags
*/
@@ -223,6 +247,7 @@
private int mUid;
private int mTag;
private int mState;
+ private int mDefaultNetwork;
private int mMetered;
private int mRoaming;
private long mBeginTimeStamp;
@@ -274,6 +299,15 @@
return 0;
}
+ private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
+ switch (defaultNetwork) {
+ case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
+ case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
+ case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
+ }
+ return 0;
+ }
+
public Bucket() {
}
@@ -339,6 +373,21 @@
}
/**
+ * Default network state. One of the following values:<p/>
+ * <ul>
+ * <li>{@link #DEFAULT_NETWORK_ALL}</li>
+ * <li>{@link #DEFAULT_NETWORK_NO}</li>
+ * <li>{@link #DEFAULT_NETWORK_YES}</li>
+ * </ul>
+ * <p>Indicates whether the network usage occurred on the system default network for this
+ * type of traffic, or whether the application chose to send this traffic on a network that
+ * was not the one selected by the system.
+ */
+ public @DefaultNetwork int getDefaultNetwork() {
+ return mDefaultNetwork;
+ }
+
+ /**
* Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Start of interval.
@@ -539,6 +588,8 @@
bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+ bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+ mRecycledSummaryEntry.defaultNetwork);
bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
bucketOut.mBeginTimeStamp = mStartTimeStamp;
@@ -588,6 +639,7 @@
bucketOut.mUid = Bucket.convertUid(getUid());
bucketOut.mTag = Bucket.convertTag(mTag);
bucketOut.mState = Bucket.STATE_ALL;
+ bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
bucketOut.mMetered = Bucket.METERED_ALL;
bucketOut.mRoaming = Bucket.ROAMING_ALL;
bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 853b003..5576e86 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -60,10 +60,11 @@
* {@link #queryDetailsForUid} <p />
* {@link #queryDetails} <p />
* These queries do not aggregate over time but do aggregate over state, metered and roaming.
- * Therefore there can be multiple buckets for a particular key but all Bucket's state is going to
- * be {@link NetworkStats.Bucket#STATE_ALL}, all Bucket's metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and all Bucket's roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * Therefore there can be multiple buckets for a particular key. However, all Buckets will have
+ * {@code state} {@link NetworkStats.Bucket#STATE_ALL},
+ * {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * {@code metered } {@link NetworkStats.Bucket#METERED_ALL},
+ * {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p />
* <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
* calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
@@ -130,13 +131,26 @@
}
}
+ /** @hide */
+ public Bucket querySummaryForDevice(NetworkTemplate template,
+ long startTime, long endTime) throws SecurityException, RemoteException {
+ Bucket bucket = null;
+ NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+ bucket = stats.getDeviceSummaryForNetwork();
+
+ stats.close();
+ return bucket;
+ }
+
/**
* Query network usage statistics summaries. Result is summarised data usage for the whole
* device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
* roaming. This means the bucket's start and end timestamp are going to be the same as the
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
- * tag {@link NetworkStats.Bucket#TAG_NONE}, metered {@link NetworkStats.Bucket#METERED_ALL},
+ * tag {@link NetworkStats.Bucket#TAG_NONE},
+ * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered {@link NetworkStats.Bucket#METERED_ALL},
* and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
@@ -160,12 +174,7 @@
return null;
}
- Bucket bucket = null;
- NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
- bucket = stats.getDeviceSummaryForNetwork();
-
- stats.close();
- return bucket;
+ return querySummaryForDevice(template, startTime, endTime);
}
/**
@@ -209,10 +218,10 @@
/**
* Query network usage statistics summaries. Result filtered to include only uids belonging to
* calling user. Result is aggregated over time, hence all buckets will have the same start and
- * end timestamps. Not aggregated over state, uid, metered, or roaming. This means buckets'
- * start and end timestamps are going to be the same as the 'startTime' and 'endTime'
- * parameters. State, uid, metered, and roaming are going to vary, and tag is going to be the
- * same.
+ * end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This
+ * means buckets' start and end timestamps are going to be the same as the 'startTime' and
+ * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
+ * be the same.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,9 +267,10 @@
* belonging to calling user. Result is aggregated over state but not aggregated over time.
* This means buckets' start and end timestamps are going to be between 'startTime' and
* 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the
- * same as the 'uid' parameter and tag the same as 'tag' parameter. metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * same as the 'uid' parameter and tag the same as 'tag' parameter.
+ * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+ * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
@@ -301,9 +311,10 @@
* metered, nor roaming. This means buckets' start and end timestamps are going to be between
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
- * tag {@link NetworkStats.Bucket#TAG_NONE}, metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * tag {@link NetworkStats.Bucket#TAG_NONE},
+ * default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL},
+ * and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
@@ -335,6 +346,37 @@
return result;
}
+ /** @hide */
+ public void registerUsageCallback(NetworkTemplate template, int networkType,
+ long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
+ checkNotNull(callback, "UsageCallback cannot be null");
+
+ final Looper looper;
+ if (handler == null) {
+ looper = Looper.myLooper();
+ } else {
+ looper = handler.getLooper();
+ }
+
+ DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
+ template, thresholdBytes);
+ try {
+ CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
+ template.getSubscriberId(), callback);
+ callback.request = mService.registerUsageCallback(
+ mContext.getOpPackageName(), request, new Messenger(callbackHandler),
+ new Binder());
+ if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
+
+ if (callback.request == null) {
+ Log.e(TAG, "Request from callback is null; should not happen");
+ }
+ } catch (RemoteException e) {
+ if (DBG) Log.d(TAG, "Remote exception when registering callback");
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Registers to receive notifications about data usage on specified networks.
*
@@ -363,15 +405,7 @@
*/
public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
UsageCallback callback, @Nullable Handler handler) {
- checkNotNull(callback, "UsageCallback cannot be null");
-
- final Looper looper;
- if (handler == null) {
- looper = Looper.myLooper();
- } else {
- looper = handler.getLooper();
- }
-
+ NetworkTemplate template = createTemplate(networkType, subscriberId);
if (DBG) {
Log.d(TAG, "registerUsageCallback called with: {"
+ " networkType=" + networkType
@@ -379,25 +413,7 @@
+ " thresholdBytes=" + thresholdBytes
+ " }");
}
-
- NetworkTemplate template = createTemplate(networkType, subscriberId);
- DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
- template, thresholdBytes);
- try {
- CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
- subscriberId, callback);
- callback.request = mService.registerUsageCallback(
- mContext.getOpPackageName(), request, new Messenger(callbackHandler),
- new Binder());
- if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
-
- if (callback.request == null) {
- Log.e(TAG, "Request from callback is null; should not happen");
- }
- } catch (RemoteException e) {
- if (DBG) Log.d(TAG, "Remote exception when registering callback");
- throw e.rethrowFromSystemServer();
- }
+ registerUsageCallback(template, networkType, thresholdBytes, callback, handler);
}
/**
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 7841b83..b255a43 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.Manifest;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -103,6 +104,24 @@
"android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
/**
+ * Intent used to broadcast the selection of a connected device as active.
+ *
+ * <p>This intent will have one extra:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+ * be null if no device is active. </li>
+ * </ul>
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+ "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
+
+ /**
* Intent used to broadcast the change in the Audio Codec state of the
* A2DP Source profile.
*
@@ -281,11 +300,7 @@
}
/**
- * Initiate connection to a profile of the remote bluetooth device.
- *
- * <p> Currently, the system supports only 1 connection to the
- * A2DP profile. The API will automatically disconnect connected
- * devices before connecting.
+ * Initiate connection to a profile of the remote Bluetooth device.
*
* <p> This API returns false in scenarios like the profile on the
* device is already connected or Bluetooth is not turned on.
@@ -425,6 +440,75 @@
}
/**
+ * Select a connected device as active.
+ *
+ * The active device selection is per profile. An active device's
+ * purpose is profile-specific. For example, A2DP audio streaming
+ * is to the active A2DP Sink device. If a remote device is not
+ * connected, it cannot be selected as active.
+ *
+ * <p> This API returns false in scenarios like the profile on the
+ * device is not connected or Bluetooth is not turned on.
+ * When this API returns true, it is guaranteed that the
+ * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
+ * with the active device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device the remote Bluetooth device. Could be null to clear
+ * the active device and stop streaming audio to a Bluetooth device.
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean setActiveDevice(@Nullable BluetoothDevice device) {
+ if (DBG) log("setActiveDevice(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && ((device == null) || isValidDevice(device))) {
+ return mService.setActiveDevice(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the connected device that is active.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ * permission.
+ *
+ * @return the connected device that is active or null if no device
+ * is active
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @Nullable
+ public BluetoothDevice getActiveDevice() {
+ if (VDBG) log("getActiveDevice()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getActiveDevice();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return null;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
* Set priority of the profile
*
* <p> The device should already be paired.
@@ -611,15 +695,17 @@
/**
* Gets the current codec status (configuration and capability).
*
+ * @param device the remote Bluetooth device. If null, use the current
+ * active A2DP Bluetooth device.
* @return the current codec status
* @hide
*/
- public BluetoothCodecStatus getCodecStatus() {
- if (DBG) Log.d(TAG, "getCodecStatus");
+ public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled()) {
- return mService.getCodecStatus();
+ return mService.getCodecStatus(device);
}
if (mService == null) {
Log.w(TAG, "Proxy not attached to service");
@@ -636,15 +722,18 @@
/**
* Sets the codec configuration preference.
*
+ * @param device the remote Bluetooth device. If null, use the current
+ * active A2DP Bluetooth device.
* @param codecConfig the codec configuration preference
* @hide
*/
- public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
- if (DBG) Log.d(TAG, "setCodecConfigPreference");
+ public void setCodecConfigPreference(BluetoothDevice device,
+ BluetoothCodecConfig codecConfig) {
+ if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled()) {
- mService.setCodecConfigPreference(codecConfig);
+ mService.setCodecConfigPreference(device, codecConfig);
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
return;
@@ -659,36 +748,42 @@
/**
* Enables the optional codecs.
*
+ * @param device the remote Bluetooth device. If null, use the currect
+ * active A2DP Bluetooth device.
* @hide
*/
- public void enableOptionalCodecs() {
- if (DBG) Log.d(TAG, "enableOptionalCodecs");
- enableDisableOptionalCodecs(true);
+ public void enableOptionalCodecs(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
+ enableDisableOptionalCodecs(device, true);
}
/**
* Disables the optional codecs.
*
+ * @param device the remote Bluetooth device. If null, use the currect
+ * active A2DP Bluetooth device.
* @hide
*/
- public void disableOptionalCodecs() {
- if (DBG) Log.d(TAG, "disableOptionalCodecs");
- enableDisableOptionalCodecs(false);
+ public void disableOptionalCodecs(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
+ enableDisableOptionalCodecs(device, false);
}
/**
* Enables or disables the optional codecs.
*
+ * @param device the remote Bluetooth device. If null, use the currect
+ * active A2DP Bluetooth device.
* @param enable if true, enable the optional codecs, other disable them
*/
- private void enableDisableOptionalCodecs(boolean enable) {
+ private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
try {
mServiceLock.readLock().lock();
if (mService != null && isEnabled()) {
if (enable) {
- mService.enableOptionalCodecs();
+ mService.enableOptionalCodecs(device);
} else {
- mService.disableOptionalCodecs();
+ mService.disableOptionalCodecs(device);
}
}
if (mService == null) Log.w(TAG, "Proxy not attached to service");
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 158aebb..dc76152 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -79,8 +79,9 @@
* {@link BluetoothDevice} objects representing all paired devices with
* {@link #getBondedDevices()}; start device discovery with
* {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
- * listen for incoming connection requests with
- * {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
+ * listen for incoming RFComm connection requests with {@link
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
+ * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for
* Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
* </p>
* <p>This class is thread safe.</p>
@@ -210,6 +211,14 @@
public static final int STATE_BLE_TURNING_OFF = 16;
/**
+ * UUID of the GATT Read Characteristics for LE_PSM value.
+ *
+ * @hide
+ */
+ public static final UUID LE_PSM_CHARACTERISTIC_UUID =
+ UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
+
+ /**
* Human-readable string helper for AdapterState
*
* @hide
@@ -1671,6 +1680,27 @@
}
/**
+ * Get the maximum number of connected audio devices.
+ *
+ * @return the maximum number of connected audio devices
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getMaxConnectedAudioDevices() {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.getMaxConnectedAudioDevices();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return 1;
+ }
+
+ /**
* Return true if hardware has entries available for matching beacons
*
* @return true if there are hw entries available for matching beacons
@@ -2135,7 +2165,9 @@
min16DigitPin);
int errno = socket.mSocket.bindListen();
if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
- socket.setChannel(socket.mSocket.getPort());
+ int assignedChannel = socket.mSocket.getPort();
+ if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel);
+ socket.setChannel(assignedChannel);
}
if (errno != 0) {
//TODO(BT): Throw the same exception error code
@@ -2176,12 +2208,18 @@
* @hide
*/
public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
+ Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
- false);
+ false);
int errno = socket.mSocket.bindListen();
if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
- socket.setChannel(socket.mSocket.getPort());
+ int assignedChannel = socket.mSocket.getPort();
+ if (DBG) {
+ Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to "
+ + assignedChannel);
+ }
+ socket.setChannel(assignedChannel);
}
if (errno != 0) {
//TODO(BT): Throw the same exception error code
@@ -2265,6 +2303,9 @@
} else if (profile == BluetoothProfile.HID_DEVICE) {
BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
return true;
+ } else if (profile == BluetoothProfile.HEARING_AID) {
+ BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
+ return true;
} else {
return false;
}
@@ -2347,6 +2388,9 @@
BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
hidDevice.close();
break;
+ case BluetoothProfile.HEARING_AID:
+ BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
+ hearingAid.close();
}
}
@@ -2425,6 +2469,8 @@
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
public boolean enableNoAutoConnect() {
if (isEnabled()) {
if (DBG) {
@@ -2738,4 +2784,103 @@
scanner.stopScan(scanCallback);
}
}
+
+ /**
+ * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
+ * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
+ * for incoming connections.
+ * <p>A remote device connecting to this socket will be authenticated and communication on this
+ * socket will be encrypted.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
+ * {@link BluetoothServerSocket}.
+ * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link
+ * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
+ * closed, Bluetooth is turned off, or the application exits unexpectedly.
+ * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
+ * defined and performed by the application.
+ * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
+ * socket from another Android device that is given the PSM value.
+ *
+ * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
+ * @return an L2CAP CoC BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions, or unable to start this CoC
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingL2capCoc(int transport)
+ throws IOException {
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ BluetoothServerSocket socket =
+ new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
+ SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ throw new IOException("Error: " + errno);
+ }
+
+ int assignedPsm = socket.mSocket.getPort();
+ if (assignedPsm == 0) {
+ throw new IOException("Error: Unable to assign PSM value");
+ }
+ if (DBG) {
+ Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
+ + assignedPsm);
+ }
+ socket.setChannel(assignedPsm);
+
+ return socket;
+ }
+
+ /**
+ * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
+ * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
+ * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
+ * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
+ * authenticated communication channel is desired.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
+ * {@link BluetoothServerSocket}.
+ * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
+ * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released
+ * when this server socket is closed, Bluetooth is turned off, or the application exits
+ * unexpectedly.
+ * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
+ * defined and performed by the application.
+ * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
+ * server socket from another Android device that is given the PSM value.
+ *
+ * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
+ * @return an L2CAP CoC BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions, or unable to start this CoC
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+ throws IOException {
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ BluetoothServerSocket socket =
+ new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
+ SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ throw new IOException("Error: " + errno);
+ }
+
+ int assignedPsm = socket.mSocket.getPort();
+ if (assignedPsm == 0) {
+ throw new IOException("Error: Unable to assign PSM value");
+ }
+ if (DBG) {
+ Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
+ + assignedPsm);
+ }
+ socket.setChannel(assignedPsm);
+
+ return socket;
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index d982bb7..ac21395 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -618,6 +618,7 @@
*
* @hide
*/
+ @SystemApi
public static final int ACCESS_UNKNOWN = 0;
/**
@@ -626,6 +627,7 @@
*
* @hide
*/
+ @SystemApi
public static final int ACCESS_ALLOWED = 1;
/**
@@ -634,6 +636,7 @@
*
* @hide
*/
+ @SystemApi
public static final int ACCESS_REJECTED = 2;
/**
@@ -1098,6 +1101,8 @@
* @return true on success, false on error
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
public boolean cancelBondProcess() {
final IBluetooth service = sService;
if (service == null) {
@@ -1125,6 +1130,8 @@
* @return true on success, false on error
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
public boolean removeBond() {
final IBluetooth service = sService;
if (service == null) {
@@ -1174,6 +1181,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH)
public boolean isConnected() {
final IBluetooth service = sService;
if (service == null) {
@@ -1197,6 +1205,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH)
public boolean isEncrypted() {
final IBluetooth service = sService;
if (service == null) {
@@ -1444,6 +1453,8 @@
* @return Whether the value has been successfully set.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setPhonebookAccessPermission(int value) {
final IBluetooth service = sService;
if (service == null) {
@@ -1910,4 +1921,75 @@
}
return null;
}
+
+ /**
+ * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
+ * be used to start a secure outgoing connection to the remote device with the same dynamic
+ * protocol/service multiplexer (PSM) value.
+ * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
+ * peer-peer Bluetooth applications.
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
+ * <p>Application using this API is responsible for obtaining PSM value from remote device.
+ * <p>The remote device will be authenticated and communication on this socket will be
+ * 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)}.
+ *
+ * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
+ * @param psm dynamic PSM value from remote device
+ * @return a CoC #BluetoothSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+ if (!isBluetoothEnabled()) {
+ Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
+ throw new IOException();
+ }
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
+ null);
+ }
+
+ /**
+ * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
+ * be used to start a secure outgoing connection to the remote device with the same dynamic
+ * protocol/service multiplexer (PSM) value.
+ * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
+ * for peer-peer Bluetooth applications.
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
+ * <p>Application using this API is responsible for obtaining PSM value from remote device.
+ * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
+ * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
+ * authenticated communication channel is possible.
+ *
+ * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
+ * @param psm dynamic PSM value from remote device
+ * @return a CoC #BluetoothSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or insufficient
+ * permissions
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
+ if (!isBluetoothEnabled()) {
+ Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
+ throw new IOException();
+ }
+ if (transport != BluetoothDevice.TRANSPORT_LE) {
+ throw new IllegalArgumentException("Unsupported transport: " + transport);
+ }
+ if (DBG) {
+ Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
+ }
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
+ null);
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index a2af342..3df4336 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -1507,6 +1507,38 @@
}
/**
+ * Request an LE connection parameter update.
+ *
+ * <p>This function will send an LE connection parameters update request to the remote device.
+ *
+ * @return true, if the request is send to the Bluetooth stack.
+ * @hide
+ */
+ public boolean requestLeConnectionUpdate(int minConnectionInterval,
+ int maxConnectionInterval,
+ int slaveLatency, int supervisionTimeout) {
+ if (DBG) {
+ Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
+ + ")" + (1.25 * minConnectionInterval)
+ + "msec, max=(" + maxConnectionInterval + ")"
+ + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
+ + ", timeout=" + supervisionTimeout + "msec");
+ }
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
+ minConnectionInterval, maxConnectionInterval,
+ slaveLatency, supervisionTimeout);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
*
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 90ae0e6..a68f485 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,8 +16,11 @@
package android.bluetooth;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
@@ -91,6 +94,23 @@
public static final String ACTION_AUDIO_STATE_CHANGED =
"android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
+ /**
+ * Intent used to broadcast the selection of a connected device as active.
+ *
+ * <p>This intent will have one extra:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+ * be null if no device is active. </li>
+ * </ul>
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+ "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
/**
* Intent used to broadcast that the headset has posted a
@@ -416,6 +436,8 @@
* @return false on immediate error, true otherwise
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -456,6 +478,8 @@
* @return false on immediate error, true otherwise
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -532,8 +556,8 @@
* Set priority of the profile
*
* <p> The device should already be paired.
- * Priority can be one of {@link #PRIORITY_ON} or
- * {@link #PRIORITY_OFF},
+ * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
+ * {@link BluetoothProfile#PRIORITY_OFF},
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
* permission.
@@ -543,6 +567,8 @@
* @return true if priority is set, false on error
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
final IBluetoothHeadset service = mService;
@@ -975,9 +1001,105 @@
}
/**
- * check if in-band ringing is supported for this platform.
+ * Select a connected device as active.
*
- * @return true if in-band ringing is supported false if in-band ringing is not supported
+ * The active device selection is per profile. An active device's
+ * purpose is profile-specific. For example, in HFP and HSP profiles,
+ * it is the device used for phone call audio. If a remote device is not
+ * connected, it cannot be selected as active.
+ *
+ * <p> This API returns false in scenarios like the profile on the
+ * device is not connected or Bluetooth is not turned on.
+ * When this API returns true, it is guaranteed that the
+ * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
+ * with the active device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Remote Bluetooth Device, could be null if phone call audio should not be
+ * streamed to a headset
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ public boolean setActiveDevice(@Nullable BluetoothDevice device) {
+ if (DBG) {
+ Log.d(TAG, "setActiveDevice: " + device);
+ }
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
+ try {
+ return service.setActiveDevice(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return false;
+ }
+
+ /**
+ * Get the connected device that is active.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ * permission.
+ *
+ * @return the connected device that is active or null if no device
+ * is active.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+ public BluetoothDevice getActiveDevice() {
+ if (VDBG) {
+ Log.d(TAG, "getActiveDevice");
+ }
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
+ try {
+ return service.getActiveDevice();
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return null;
+ }
+
+ /**
+ * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an
+ * active connection.
+ *
+ * @return true if in-band ringing is enabled, false if in-band ringing is disabled
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+ public boolean isInbandRingingEnabled() {
+ if (DBG) {
+ log("isInbandRingingEnabled()");
+ }
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
+ try {
+ return service.isInbandRingingEnabled();
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return false;
+ }
+
+ /**
+ * Check if in-band ringing is supported for this platform.
+ *
+ * @return true if in-band ringing is supported, false if in-band ringing is not supported
* @hide
*/
public static boolean isInbandRingingSupported(Context context) {
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index dc00d63..d46b2e3 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -73,17 +73,18 @@
private final boolean mOutgoing;
private final UUID mUUID;
private final long mCreationElapsedMilli;
+ private final boolean mInBandRing;
/**
* Creates BluetoothHeadsetClientCall instance.
*/
public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number,
- boolean multiParty, boolean outgoing) {
- this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing);
+ boolean multiParty, boolean outgoing, boolean inBandRing) {
+ this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing);
}
public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state,
- String number, boolean multiParty, boolean outgoing) {
+ String number, boolean multiParty, boolean outgoing, boolean inBandRing) {
mDevice = device;
mId = id;
mUUID = uuid;
@@ -91,6 +92,7 @@
mNumber = number != null ? number : "";
mMultiParty = multiParty;
mOutgoing = outgoing;
+ mInBandRing = inBandRing;
mCreationElapsedMilli = SystemClock.elapsedRealtime();
}
@@ -200,6 +202,16 @@
return mOutgoing;
}
+ /**
+ * Checks if the ringtone will be generated by the connected phone
+ *
+ * @return <code>true</code> if in band ring is enabled, <code>false</code> otherwise.
+ */
+ public boolean isInBandRing() {
+ return mInBandRing;
+ }
+
+
@Override
public String toString() {
return toString(false);
@@ -253,6 +265,8 @@
builder.append(mMultiParty);
builder.append(", mOutgoing: ");
builder.append(mOutgoing);
+ builder.append(", mInBandRing: ");
+ builder.append(mInBandRing);
builder.append("}");
return builder.toString();
}
@@ -266,7 +280,8 @@
public BluetoothHeadsetClientCall createFromParcel(Parcel in) {
return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null),
in.readInt(), UUID.fromString(in.readString()), in.readInt(),
- in.readString(), in.readInt() == 1, in.readInt() == 1);
+ in.readString(), in.readInt() == 1, in.readInt() == 1,
+ in.readInt() == 1);
}
@Override
@@ -284,6 +299,7 @@
out.writeString(mNumber);
out.writeInt(mMultiParty ? 1 : 0);
out.writeInt(mOutgoing ? 1 : 0);
+ out.writeInt(mInBandRing ? 1 : 0);
}
@Override
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
new file mode 100644
index 0000000..647e0d0
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -0,0 +1,693 @@
+/*
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Hearing Aid
+ * profile.
+ *
+ * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHearingAid proxy object.
+ *
+ * <p> Each method is protected with its appropriate permission.
+ * @hide
+ */
+public final class BluetoothHearingAid implements BluetoothProfile {
+ private static final String TAG = "BluetoothHearingAid";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ /**
+ * Intent used to broadcast the change in connection state of the Hearing Aid
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
+
+ /**
+ * Intent used to broadcast the change in the Playing state of the Hearing Aid
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PLAYING_STATE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED";
+
+ /**
+ * Intent used to broadcast the selection of a connected device as active.
+ *
+ * <p>This intent will have one extra:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+ * be null if no device is active. </li>
+ * </ul>
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
+
+ /**
+ * Hearing Aid device is streaming music. This state can be one of
+ * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+ * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ */
+ public static final int STATE_PLAYING = 10;
+
+ /**
+ * Hearing Aid device is NOT streaming music. This state can be one of
+ * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+ * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ */
+ public static final int STATE_NOT_PLAYING = 11;
+
+ /** This device represents Left Hearing Aid. */
+ public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
+
+ /** This device represents Right Hearing Aid. */
+ public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
+
+ /** This device is Monaural. */
+ public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
+
+ /** This device is Binaural (should receive only left or right audio). */
+ public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
+
+ /** Can't read ClientID for this device */
+ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+ @GuardedBy("mServiceLock")
+ private IBluetoothHearingAid mService;
+ private BluetoothAdapter mAdapter;
+
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (VDBG) Log.d(TAG, "Unbinding service...");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ } else {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ if (VDBG) Log.d(TAG, "Binding service...");
+ doBind();
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothHearingAid proxy object for interacting with the local
+ * Bluetooth Hearing Aid service.
+ */
+ /*package*/ BluetoothHearingAid(Context context, ServiceListener l) {
+ mContext = context;
+ mServiceListener = l;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ doBind();
+ }
+
+ void doBind() {
+ Intent intent = new Intent(IBluetoothHearingAid.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ android.os.Process.myUserHandle())) {
+ Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
+ return;
+ }
+ }
+
+ /*package*/ void close() {
+ mServiceListener = null;
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (Exception e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ try {
+ mServiceLock.writeLock().lock();
+ if (mService != null) {
+ mService = null;
+ mContext.unbindService(mConnection);
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public void finalize() {
+ // The empty finalize needs to be kept or the
+ // cts signature tests would fail.
+ }
+
+ /**
+ * Initiate connection to a profile of the remote bluetooth device.
+ *
+ * <p> This API returns false in scenarios like the profile on the
+ * device is already connected or Bluetooth is not turned on.
+ * When this API returns true, it is guaranteed that
+ * connection state intent for the profile will be broadcasted with
+ * the state. Users can get the connection state of the profile
+ * from this intent.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean connect(BluetoothDevice device) {
+ if (DBG) log("connect(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.connect(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Initiate disconnection from a profile
+ *
+ * <p> This API will return false in scenarios like the profile on the
+ * Bluetooth device is not in connected state etc. When this API returns,
+ * true, it is guaranteed that the connection state change
+ * intent will be broadcasted with the state. Users can get the
+ * disconnection state of the profile from this intent.
+ *
+ * <p> If the disconnection is initiated by a remote device, the state
+ * will transition from {@link #STATE_CONNECTED} to
+ * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+ * host (local) device the state will transition from
+ * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+ * state {@link #STATE_DISCONNECTED}. The transition to
+ * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+ * two scenarios.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean disconnect(BluetoothDevice device) {
+ if (DBG) log("disconnect(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.disconnect(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (VDBG) log("getConnectedDevices()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getConnectedDevices();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (VDBG) log("getDevicesMatchingStates()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getDevicesMatchingConnectionStates(states);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ if (VDBG) log("getState(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getConnectionState(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired.
+ * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
+ * {@link #PRIORITY_OFF},
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Paired bluetooth device
+ * @param priority
+ * @return true if priority is set, false on error
+ * @hide
+ */
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setPriority(" + device + ", " + priority + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF
+ && priority != BluetoothProfile.PRIORITY_ON) {
+ return false;
+ }
+ return mService.setPriority(device, priority);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+ * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) log("getPriority(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getPriority(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.PRIORITY_OFF;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.PRIORITY_OFF;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Helper for converting a state to a string.
+ *
+ * For debug use only - strings are not internationalized.
+ *
+ * @hide
+ */
+ public static String stateToString(int state) {
+ switch (state) {
+ case STATE_DISCONNECTED:
+ return "disconnected";
+ case STATE_CONNECTING:
+ return "connecting";
+ case STATE_CONNECTED:
+ return "connected";
+ case STATE_DISCONNECTING:
+ return "disconnecting";
+ case STATE_PLAYING:
+ return "playing";
+ case STATE_NOT_PLAYING:
+ return "not playing";
+ default:
+ return "<unknown state " + state + ">";
+ }
+ }
+
+ /**
+ * Get the volume of the device.
+ *
+ * <p> The volume is between -128 dB (mute) to 0 dB.
+ *
+ * @return volume of the hearing aid device.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getVolume() {
+ if (VDBG) {
+ log("getVolume()");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getVolume();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return 0;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return 0;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Tells remote device to adjust volume. Uses the following values:
+ * <ul>
+ * <li>{@link AudioManager#ADJUST_LOWER}</li>
+ * <li>{@link AudioManager#ADJUST_RAISE}</li>
+ * <li>{@link AudioManager#ADJUST_MUTE}</li>
+ * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+ * </ul>
+ *
+ * @param direction One of the supported adjust values.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public void adjustVolume(int direction) {
+ if (DBG) log("adjustVolume(" + direction + ")");
+
+ try {
+ mServiceLock.readLock().lock();
+
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return;
+ }
+
+ if (!isEnabled()) return;
+
+ mService.adjustVolume(direction);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Tells remote device to set an absolute volume.
+ *
+ * @param volume Absolute volume to be set on remote
+ * @hide
+ */
+ public void setVolume(int volume) {
+ if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
+
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return;
+ }
+
+ if (!isEnabled()) return;
+
+ mService.setVolume(volume);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the CustomerId of the device.
+ *
+ * @param device Bluetooth device
+ * @return the CustomerId of the device
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public long getHiSyncId(BluetoothDevice device) {
+ if (VDBG) {
+ log("getCustomerId(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return HI_SYNC_ID_INVALID;
+ }
+
+ if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
+
+ return mService.getHiSyncId(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return HI_SYNC_ID_INVALID;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the side of the device.
+ *
+ * @param device Bluetooth device.
+ * @return SIDE_LEFT or SIDE_RIGHT
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getDeviceSide(BluetoothDevice device) {
+ if (VDBG) {
+ log("getDeviceSide(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getDeviceSide(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return SIDE_LEFT;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return SIDE_LEFT;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the mode of the device.
+ *
+ * @param device Bluetooth device
+ * @return MODE_MONAURAL or MODE_BINAURAL
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getDeviceMode(BluetoothDevice device) {
+ if (VDBG) {
+ log("getDeviceMode(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getDeviceMode(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return MODE_MONAURAL;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return MODE_MONAURAL;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID,
+ BluetoothHearingAid.this);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
+ }
+ }
+ };
+
+ private boolean isEnabled() {
+ if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+ return false;
+ }
+
+ private boolean isValidDevice(BluetoothDevice device) {
+ if (device == null) return false;
+
+ if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+ return false;
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index df2028a..656188f 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import java.util.List;
@@ -157,12 +158,26 @@
public static final int HID_DEVICE = 19;
/**
+ * Object Push Profile (OPP)
+ *
+ * @hide
+ */
+ public static final int OPP = 20;
+
+ /**
+ * Hearing Aid Device
+ *
+ * @hide
+ */
+ int HEARING_AID = 21;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- public static final int MAX_PROFILE_ID = 19;
+ int MAX_PROFILE_ID = 21;
/**
* Default priority for devices that we try to auto-connect to and
@@ -178,6 +193,7 @@
*
* @hide
**/
+ @SystemApi
public static final int PRIORITY_ON = 100;
/**
@@ -186,6 +202,7 @@
*
* @hide
**/
+ @SystemApi
public static final int PRIORITY_OFF = 0;
/**
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 58d090d..ebb7f18 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -68,6 +68,7 @@
public final class BluetoothServerSocket implements Closeable {
private static final String TAG = "BluetoothServerSocket";
+ private static final boolean DBG = false;
/*package*/ final BluetoothSocket mSocket;
private Handler mHandler;
private int mMessage;
@@ -169,6 +170,7 @@
* close any {@link BluetoothSocket} received from {@link #accept()}.
*/
public void close() throws IOException {
+ if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel);
synchronized (this) {
if (mHandler != null) {
mHandler.obtainMessage(mMessage).sendToTarget();
@@ -197,6 +199,20 @@
}
/**
+ * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
+ * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
+ * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
+ * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
+ * method is called on non-L2CAP server sockets.
+ *
+ * @return the assigned PSM or LE_PSM value depending on transport
+ * @hide
+ */
+ public int getPsm() {
+ return mChannel;
+ }
+
+ /**
* Sets the channel on which future sockets are bound.
* Currently used only when a channel is auto generated.
*/
@@ -227,6 +243,10 @@
sb.append("TYPE_L2CAP");
break;
}
+ case BluetoothSocket.TYPE_L2CAP_LE: {
+ sb.append("TYPE_L2CAP_LE");
+ break;
+ }
case BluetoothSocket.TYPE_SCO: {
sb.append("TYPE_SCO");
break;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 0569913..09a5b59 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -99,6 +99,16 @@
/** L2CAP socket */
public static final int TYPE_L2CAP = 3;
+ /** L2CAP socket on BR/EDR transport
+ * @hide
+ */
+ public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP;
+
+ /** L2CAP socket on LE transport
+ * @hide
+ */
+ public static final int TYPE_L2CAP_LE = 4;
+
/*package*/ static final int EBADFD = 77;
/*package*/ static final int EADDRINUSE = 98;
@@ -417,6 +427,7 @@
return -1;
}
try {
+ if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType);
mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
mUuid, mPort, getSecurityFlags());
} catch (RemoteException e) {
@@ -451,7 +462,7 @@
mSocketState = SocketState.LISTENING;
}
}
- if (DBG) Log.d(TAG, "channel: " + channel);
+ if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
if (mPort <= -1) {
mPort = channel;
} // else ASSERT(mPort == channel)
@@ -515,7 +526,7 @@
/*package*/ int read(byte[] b, int offset, int length) throws IOException {
int ret = 0;
if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
- if (mType == TYPE_L2CAP) {
+ if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
int bytesToRead = length;
if (VDBG) {
Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
@@ -558,7 +569,7 @@
// Rfcomm uses dynamic allocation, and should not have any bindings
// to the actual message length.
if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
- if (mType == TYPE_L2CAP) {
+ if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
if (length <= mMaxTxPacketSize) {
mSocketOS.write(b, offset, length);
} else {
@@ -665,6 +676,35 @@
mExcludeSdp = excludeSdp;
}
+ /**
+ * Set the LE Transmit Data Length to be the maximum that the BT Controller is capable of. This
+ * parameter is used by the BT Controller to set the maximum transmission packet size on this
+ * connection. This function is currently used for testing only.
+ * @hide
+ */
+ public void requestMaximumTxDataLength() throws IOException {
+ if (mDevice == null) {
+ throw new IOException("requestMaximumTxDataLength is called on null device");
+ }
+
+ try {
+ if (mSocketState == SocketState.CLOSED) {
+ throw new IOException("socket closed");
+ }
+ IBluetooth bluetoothProxy =
+ BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+ if (bluetoothProxy == null) {
+ throw new IOException("Bluetooth is off");
+ }
+
+ if (DBG) Log.d(TAG, "requestMaximumTxDataLength");
+ bluetoothProxy.getSocketManager().requestMaximumTxDataLength(mDevice);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ throw new IOException("unable to send RPC: " + e.getMessage());
+ }
+ }
+
private String convertAddr(final byte[] addr) {
return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
@@ -702,7 +742,7 @@
}
private void createL2capRxBuffer() {
- if (mType == TYPE_L2CAP) {
+ if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
// Allocate the buffer to use for reads.
if (VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 76cb3f5..0a0d214 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -79,6 +79,9 @@
ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid SAP =
ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
+ /* TODO: b/69623109 update this value. It will change to 16bit UUID!! */
+ public static final ParcelUuid HearingAid =
+ ParcelUuid.fromString("7312C48F-22CC-497F-85FD-A0616A3B9E05");
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7e2ac63..d135758 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3583,14 +3583,22 @@
*
* @see #getSystemService
* @see android.telephony.euicc.EuiccManager
- * TODO(b/35851809): Unhide this API.
- * @hide
*/
- public static final String EUICC_SERVICE = "euicc_service";
+ public static final String EUICC_SERVICE = "euicc";
/**
- * Use with {@link #getSystemService} to retrieve a
- * {@link android.text.ClipboardManager} for accessing and modifying
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.telephony.euicc.EuiccCardManager} to access the device eUICC (embedded SIM).
+ *
+ * @see #getSystemService(String)
+ * @see android.telephony.euicc.EuiccCardManager
+ * @hide
+ */
+ @SystemApi
+ public static final String EUICC_CARD_SERVICE = "euicc_card";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
@@ -4052,6 +4060,16 @@
public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.se.omapi.ISecureElementService}
+ * for accessing the SecureElementService.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8ff6699..c865dd7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3511,7 +3511,10 @@
* For more details see TelephonyIntents.ACTION_SIM_STATE_CHANGED. This is here
* because TelephonyIntents is an internal class.
* @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
+ * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
*/
+ @Deprecated
@SystemApi
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 664bcbca..8ea81a4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -35,6 +35,7 @@
import android.util.SparseArray;
import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1458,6 +1459,16 @@
/**
* @hide
*/
+ public boolean isAllowedToUseHiddenApi() {
+ boolean whitelisted =
+ SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+ return isSystemApp() || // TODO get rid of this once the whitelist has been populated
+ (whitelisted && (isSystemApp() || isUpdatedSystemApp()));
+ }
+
+ /**
+ * @hide
+ */
public boolean isForwardLocked() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0e70645..73a403d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -48,6 +48,7 @@
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.IArtManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
@@ -664,4 +665,6 @@
ComponentName getInstantAppInstallerComponent();
String getInstantAppAndroidId(String packageName, int userId);
+
+ IArtManager getArtManager();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 89751da..6f98adc 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -42,6 +42,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.dex.ArtManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
@@ -1316,6 +1317,15 @@
*/
public static final int INSTALL_FAILED_INSTANT_APP_INVALID = -116;
+ /**
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the dex metadata file is invalid or
+ * if there was no matching apk file for a dex metadata file.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -2078,8 +2088,6 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
* supports embedded subscriptions on eUICCs.
- * TODO(b/35851809): Make this public.
- * @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
@@ -5634,6 +5642,8 @@
case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION";
case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS";
case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED";
+ case INSTALL_FAILED_BAD_DEX_METADATA:
+ return "INSTALL_FAILED_BAD_DEX_METADATA";
default: return Integer.toString(status);
}
}
@@ -5678,6 +5688,7 @@
case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return PackageInstaller.STATUS_FAILURE_INVALID;
case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;
case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID;
case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
@@ -5879,4 +5890,14 @@
@SystemApi
public abstract void registerDexModule(String dexModulePath,
@Nullable DexModuleRegisterCallback callback);
+
+ /**
+ * Returns the {@link ArtManager} associated with this package manager.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull ArtManager getArtManager() {
+ throw new UnsupportedOperationException("getArtManager not implemented in subclass");
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index cb9ecf3..a3e3354 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -250,6 +250,9 @@
}
/** @hide */
+ public static final String APK_FILE_EXTENSION = ".apk";
+
+ /** @hide */
public static class NewPermissionInfo {
public final String name;
public final int sdkVersion;
@@ -616,7 +619,7 @@
}
public static boolean isApkPath(String path) {
- return path.endsWith(".apk");
+ return path.endsWith(APK_FILE_EXTENSION);
}
/**
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
new file mode 100644
index 0000000..0753063
--- /dev/null
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -0,0 +1,209 @@
+/**
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Class for retrieving various kinds of information related to the runtime artifacts of
+ * packages that are currently installed on the device.
+ *
+ * @hide
+ */
+@SystemApi
+public class ArtManager {
+ private static final String TAG = "ArtManager";
+
+ /** The snapshot failed because the package was not found. */
+ public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0;
+ /** The snapshot failed because the package code path does not exist. */
+ public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1;
+ /** The snapshot failed because of an internal error (e.g. error during opening profiles). */
+ public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2;
+
+ /** Constant used for applications profiles. */
+ public static final int PROFILE_APPS = 0;
+ /** Constant used for the boot image profile. */
+ public static final int PROFILE_BOOT_IMAGE = 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PROFILE_" }, value = {
+ PROFILE_APPS,
+ PROFILE_BOOT_IMAGE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProfileType {}
+
+
+ private IArtManager mArtManager;
+
+ /**
+ * @hide
+ */
+ public ArtManager(@NonNull IArtManager manager) {
+ mArtManager = manager;
+ }
+
+ /**
+ * Snapshots a runtime profile according to the {@code profileType} parameter.
+ *
+ * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot
+ * the profile for for an apk belonging to the package {@code packageName}.
+ * The apk is identified by {@code codePath}.
+ *
+ * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot
+ * the profile for the boot image. In this case {@code codePath can be null}. The parameters
+ * {@code packageName} and {@code codePath} are ignored.
+ *u
+ * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+ *
+ * The result will be posted on the {@code executor} using the given {@code callback}.
+ * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}.
+ *
+ * This method will throw {@link IllegalStateException} if
+ * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
+ * {@code profileType}.
+ *
+ * @param profileType the type of profile that should be snapshot (boot image or app)
+ * @param packageName the target package name or null if the target is the boot image
+ * @param codePath the code path for which the profile should be retrieved or null if
+ * the target is the boot image
+ * @param callback the callback which should be used for the result
+ * @param executor the executor which should be used to post the result
+ */
+ @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
+ @Nullable String codePath, @NonNull Executor executor,
+ @NonNull SnapshotRuntimeProfileCallback callback) {
+ Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
+
+ SnapshotRuntimeProfileCallbackDelegate delegate =
+ new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
+ try {
+ mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Returns true if runtime profiles are enabled for the given type, false otherwise.
+ *
+ * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+ *
+ * @param profileType can be either {@link ArtManager#PROFILE_APPS}
+ * or {@link ArtManager#PROFILE_BOOT_IMAGE}
+ */
+ @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+ public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
+ try {
+ return mArtManager.isRuntimeProfilingEnabled(profileType);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ return false;
+ }
+
+ /**
+ * Callback used for retrieving runtime profiles.
+ */
+ public abstract static class SnapshotRuntimeProfileCallback {
+ /**
+ * Called when the profile snapshot finished with success.
+ *
+ * @param profileReadFd the file descriptor that can be used to read the profile. Note that
+ * the file might be empty (which is valid profile).
+ */
+ public abstract void onSuccess(ParcelFileDescriptor profileReadFd);
+
+ /**
+ * Called when the profile snapshot finished with an error.
+ *
+ * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND,
+ * SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}.
+ */
+ public abstract void onError(int errCode);
+ }
+
+ private static class SnapshotRuntimeProfileCallbackDelegate
+ extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub {
+ private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
+ private final Executor mExecutor;
+
+ private SnapshotRuntimeProfileCallbackDelegate(
+ ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) {
+ mCallback = callback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onSuccess(final ParcelFileDescriptor profileReadFd) {
+ mExecutor.execute(() -> mCallback.onSuccess(profileReadFd));
+ }
+
+ @Override
+ public void onError(int errCode) {
+ mExecutor.execute(() -> mCallback.onError(errCode));
+ }
+ }
+
+ /**
+ * Return the profile name for the given split. If {@code splitName} is null the
+ * method returns the profile name for the base apk.
+ *
+ * @hide
+ */
+ public static String getProfileName(String splitName) {
+ return splitName == null ? "primary.prof" : splitName + ".split.prof";
+ }
+
+ /**
+ * Return the path to the current profile corresponding to given package and split.
+ *
+ * @hide
+ */
+ public static String getCurrentProfilePath(String packageName, int userId, String splitName) {
+ File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName);
+ return new File(profileDir, getProfileName(splitName)).getAbsolutePath();
+ }
+
+ /**
+ * Return the snapshot profile file for the given package and profile name.
+ *
+ * KEEP in sync with installd dexopt.cpp.
+ * TODO(calin): inject the snapshot profile name from PM to avoid the dependency.
+ *
+ * @hide
+ */
+ public static File getProfileSnapshotFileForName(String packageName, String profileName) {
+ File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName);
+ return new File(profileDir, profileName + ".snapshot");
+ }
+}
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
new file mode 100644
index 0000000..5d10b88
--- /dev/null
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -0,0 +1,230 @@
+/**
+ * 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.content.pm.dex;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
+import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.PackageParser.PackageParserException;
+import android.util.ArrayMap;
+import android.util.jar.StrictJarFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class used to compute and validate the location of dex metadata files.
+ *
+ * @hide
+ */
+public class DexMetadataHelper {
+ private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
+
+ private DexMetadataHelper() {}
+
+ /** Return true if the given file is a dex metadata file. */
+ public static boolean isDexMetadataFile(File file) {
+ return isDexMetadataPath(file.getName());
+ }
+
+ /** Return true if the given path is a dex metadata path. */
+ private static boolean isDexMetadataPath(String path) {
+ return path.endsWith(DEX_METADATA_FILE_EXTENSION);
+ }
+
+ /**
+ * Return the size (in bytes) of all dex metadata files associated with the given package.
+ */
+ public static long getPackageDexMetadataSize(PackageLite pkg) {
+ long sizeBytes = 0;
+ Collection<String> dexMetadataList = DexMetadataHelper.getPackageDexMetadata(pkg).values();
+ for (String dexMetadata : dexMetadataList) {
+ sizeBytes += new File(dexMetadata).length();
+ }
+ return sizeBytes;
+ }
+
+ /**
+ * Search for the dex metadata file associated with the given target file.
+ * If it exists, the method returns the dex metadata file; otherwise it returns null.
+ *
+ * Note that this performs a loose matching suitable to be used in the InstallerSession logic.
+ * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile}
+ * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
+ */
+ public static File findDexMetadataForFile(File targetFile) {
+ String dexMetadataPath = buildDexMetadataPathForFile(targetFile);
+ File dexMetadataFile = new File(dexMetadataPath);
+ return dexMetadataFile.exists() ? dexMetadataFile : null;
+ }
+
+ /**
+ * Return the dex metadata files for the given package as a map
+ * [code path -> dex metadata path].
+ *
+ * NOTE: involves I/O checks.
+ */
+ public static Map<String, String> getPackageDexMetadata(PackageParser.Package pkg) {
+ return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+ }
+
+ /**
+ * Return the dex metadata files for the given package as a map
+ * [code path -> dex metadata path].
+ *
+ * NOTE: involves I/O checks.
+ */
+ private static Map<String, String> getPackageDexMetadata(PackageLite pkg) {
+ return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+ }
+
+ /**
+ * Look up the dex metadata files for the given code paths building the map
+ * [code path -> dex metadata].
+ *
+ * For each code path (.apk) the method checks if a matching dex metadata file (.dm) exists.
+ * If it does it adds the pair to the returned map.
+ *
+ * Note that this method will do a loose
+ * matching based on the extension ('foo.dm' will match 'foo.apk' or 'foo').
+ *
+ * This should only be used for code paths extracted from a package structure after the naming
+ * was enforced in the installer.
+ */
+ private static Map<String, String> buildPackageApkToDexMetadataMap(
+ List<String> codePaths) {
+ ArrayMap<String, String> result = new ArrayMap<>();
+ for (int i = codePaths.size() - 1; i >= 0; i--) {
+ String codePath = codePaths.get(i);
+ String dexMetadataPath = buildDexMetadataPathForFile(new File(codePath));
+
+ if (Files.exists(Paths.get(dexMetadataPath))) {
+ result.put(codePath, dexMetadataPath);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the dex metadata path associated with the given code path.
+ * (replaces '.apk' extension with '.dm')
+ *
+ * @throws IllegalArgumentException if the code path is not an .apk.
+ */
+ public static String buildDexMetadataPathForApk(String codePath) {
+ if (!PackageParser.isApkPath(codePath)) {
+ throw new IllegalStateException(
+ "Corrupted package. Code path is not an apk " + codePath);
+ }
+ return codePath.substring(0, codePath.length() - APK_FILE_EXTENSION.length())
+ + DEX_METADATA_FILE_EXTENSION;
+ }
+
+ /**
+ * Return the dex metadata path corresponding to the given {@code targetFile} using a loose
+ * matching.
+ * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile}
+ * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
+ */
+ private static String buildDexMetadataPathForFile(File targetFile) {
+ return PackageParser.isApkFile(targetFile)
+ ? buildDexMetadataPathForApk(targetFile.getPath())
+ : targetFile.getPath() + DEX_METADATA_FILE_EXTENSION;
+ }
+
+ /**
+ * Validate the dex metadata files installed for the given package.
+ *
+ * @throws PackageParserException in case of errors.
+ */
+ public static void validatePackageDexMetadata(PackageParser.Package pkg)
+ throws PackageParserException {
+ Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
+ for (String dexMetadata : apkToDexMetadataList) {
+ validateDexMetadataFile(dexMetadata);
+ }
+ }
+
+ /**
+ * Validate that the given file is a dex metadata archive.
+ * This is just a sanity validation that the file is a zip archive.
+ *
+ * @throws PackageParserException if the file is not a .dm file.
+ */
+ private static void validateDexMetadataFile(String dmaPath) throws PackageParserException {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(dmaPath, false, false);
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ "Error opening " + dmaPath, e);
+ } finally {
+ if (jarFile != null) {
+ try {
+ jarFile.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates that all dex metadata paths in the given list have a matching apk.
+ * (for any foo.dm there should be either a 'foo' of a 'foo.apk' file).
+ * If that's not the case it throws {@code IllegalStateException}.
+ *
+ * This is used to perform a basic sanity check during adb install commands.
+ * (The installer does not support stand alone .dm files)
+ */
+ public static void validateDexPaths(String[] paths) {
+ ArrayList<String> apks = new ArrayList<>();
+ for (int i = 0; i < paths.length; i++) {
+ if (PackageParser.isApkPath(paths[i])) {
+ apks.add(paths[i]);
+ }
+ }
+ ArrayList<String> unmatchedDmFiles = new ArrayList<>();
+ for (int i = 0; i < paths.length; i++) {
+ String dmPath = paths[i];
+ if (isDexMetadataPath(dmPath)) {
+ boolean valid = false;
+ for (int j = apks.size() - 1; j >= 0; j--) {
+ if (dmPath.equals(buildDexMetadataPathForFile(new File(apks.get(j))))) {
+ valid = true;
+ break;
+ }
+ }
+ if (!valid) {
+ unmatchedDmFiles.add(dmPath);
+ }
+ }
+ }
+ if (!unmatchedDmFiles.isEmpty()) {
+ throw new IllegalStateException("Unmatched .dm files: " + unmatchedDmFiles);
+ }
+ }
+
+}
diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl
new file mode 100644
index 0000000..6abfdba
--- /dev/null
+++ b/core/java/android/content/pm/dex/IArtManager.aidl
@@ -0,0 +1,58 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm.dex;
+
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+
+/**
+ * A system service that provides access to runtime and compiler artifacts.
+ *
+ * @hide
+ */
+interface IArtManager {
+ /**
+ * Snapshots a runtime profile according to the {@code profileType} parameter.
+ *
+ * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot
+ * the profile for for an apk belonging to the package {@code packageName}.
+ * The apk is identified by {@code codePath}.
+ *
+ * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot
+ * the profile for the boot image. In this case {@code codePath can be null}. The parameters
+ * {@code packageName} and {@code codePath} are ignored.
+ *
+ * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+ *
+ * The result will be posted on the {@code executor} using the given {@code callback}.
+ * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}.
+ *
+ * This method will throw {@link IllegalStateException} if
+ * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
+ * {@code profileType}.
+ */
+ oneway void snapshotRuntimeProfile(int profileType, in String packageName,
+ in String codePath, in ISnapshotRuntimeProfileCallback callback);
+
+ /**
+ * Returns true if runtime profiles are enabled for the given type, false otherwise.
+ * The type can be can be either {@code ArtManager.PROFILE_APPS}
+ * or {@code ArtManager.PROFILE_BOOT_IMAGE}.
+ *
+ * @param profileType
+ */
+ boolean isRuntimeProfilingEnabled(int profileType);
+}
diff --git a/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl
new file mode 100644
index 0000000..3b4838f
--- /dev/null
+++ b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm.dex;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Callback used to post the result of a profile-snapshot operation.
+ *
+ * @hide
+ */
+oneway interface ISnapshotRuntimeProfileCallback {
+ void onSuccess(in ParcelFileDescriptor profileReadFd);
+ void onError(int errCode);
+}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 90bf896..a2bc91e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -996,7 +996,12 @@
return;
}
- Integer oldStatus = mDeviceStatus.put(id, status);
+ Integer oldStatus;
+ if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
+ oldStatus = mDeviceStatus.remove(id);
+ } else {
+ oldStatus = mDeviceStatus.put(id, status);
+ }
if (oldStatus != null && oldStatus == status) {
if (DEBUG) {
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 025d46d..4e8c45d 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -96,6 +96,11 @@
*/
void setCurrentFunction(String function, boolean usbDataUnlocked);
+ /* Sets the screen unlocked USB function(s), which will be set automatically
+ * when the screen is unlocked.
+ */
+ void setScreenUnlockedFunctions(String function);
+
/* Allow USB debugging from the attached host. If alwaysAllow is true, add the
* the public key to list of host keys that the user has approved.
*/
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index d73d3d8..48e8d34 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -590,6 +590,32 @@
}
/**
+ * Sets the screen unlocked functions, which are persisted and set as the current functions
+ * whenever the screen is unlocked.
+ * <p>
+ * The allowed values are: {@link #USB_FUNCTION_NONE},
+ * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
+ * or {@link #USB_FUNCTION_RNDIS}.
+ * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions
+ * no longer change on screen unlock.
+ * </p><p>
+ * Note: When the screen is on, this method will apply given functions as current functions,
+ * which is asynchronous and may fail silently without applying the requested changes.
+ * </p>
+ *
+ * @param function function to set as default
+ *
+ * {@hide}
+ */
+ public void setScreenUnlockedFunctions(String function) {
+ try {
+ mService.setScreenUnlockedFunctions(function);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns a list of physical USB ports on the device.
* <p>
* This list is guaranteed to contain all dual-role USB Type C ports but it might
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7a20943..1bafcaec 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1726,9 +1726,9 @@
}
/**
- * Called when the input method window has been shown to the user, after
- * previously not being visible. This is done after all of the UI setup
- * for the window has occurred (creating its views etc).
+ * Called immediately before the input method window is shown to the user.
+ * You could override this to prepare for the window to be shown
+ * (update view structure etc).
*/
public void onWindowShown() {
// Intentionally empty
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8071e8b..3a8a254 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -112,8 +112,14 @@
* <p/>
* For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
* is set to {@code true} if there are no connected networks at all.
+ *
+ * @deprecated apps should use the more versatile {@link #requestNetwork},
+ * {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
+ * functions instead for faster and more detailed updates about the network
+ * changes they care about.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
/**
@@ -1596,8 +1602,12 @@
/** The hardware returned an error. */
public static final int ERROR_HARDWARE_ERROR = -31;
+ /** The NAT-T destination port for IPsec */
public static final int NATT_PORT = 4500;
+ /** The minimum interval in seconds between keepalive packet transmissions */
+ public static final int MIN_INTERVAL = 10;
+
private final Network mNetwork;
private final PacketKeepaliveCallback mCallback;
private final Looper mLooper;
@@ -1794,7 +1804,7 @@
ITelephony it = ITelephony.Stub.asInterface(b);
int subId = SubscriptionManager.getDefaultDataSubscriptionId();
Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
- boolean retVal = it.getDataEnabled(subId);
+ boolean retVal = it.isUserDataEnabled(subId);
Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
+ " retVal=" + retVal);
return retVal;
@@ -2656,7 +2666,7 @@
* A {@code NetworkCallback} is registered by calling
* {@link #requestNetwork(NetworkRequest, NetworkCallback)},
* {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)},
- * or {@link #registerDefaultNetworkCallback(NetworkCallback). A {@code NetworkCallback} is
+ * or {@link #registerDefaultNetworkCallback(NetworkCallback)}. A {@code NetworkCallback} is
* unregistered by calling {@link #unregisterNetworkCallback(NetworkCallback)}.
* A {@code NetworkCallback} should be registered at most once at any time.
* A {@code NetworkCallback} that has been unregistered can be registered again.
@@ -2685,6 +2695,32 @@
* satisfying the request changes.
*
* @param network The {@link Network} of the satisfying network.
+ * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
+ * @param linkProperties The {@link LinkProperties} of the satisfying network.
+ * @hide
+ */
+ public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties) {
+ // Internally only this method is called when a new network is available, and
+ // it calls the callback in the same way and order that older versions used
+ // to call so as not to change the behavior.
+ onAvailable(network);
+ if (!networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
+ onNetworkSuspended(network);
+ }
+ onCapabilitiesChanged(network, networkCapabilities);
+ onLinkPropertiesChanged(network, linkProperties);
+ }
+
+ /**
+ * Called when the framework connects and has declared a new network ready for use.
+ * This callback may be called more than once if the {@link Network} that is
+ * satisfying the request changes. This will always immediately be followed by a
+ * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
+ * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+ *
+ * @param network The {@link Network} of the satisfying network.
*/
public void onAvailable(Network network) {}
@@ -2727,7 +2763,8 @@
* changes capabilities but still satisfies the stated need.
*
* @param network The {@link Network} whose capabilities have changed.
- * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
+ * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+ * network.
*/
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {}
@@ -2743,7 +2780,7 @@
/**
* Called when the network the framework connected to for this request
- * goes into {@link NetworkInfo.DetailedState.SUSPENDED}.
+ * goes into {@link NetworkInfo.State#SUSPENDED}.
* This generally means that while the TCP connections are still live,
* temporarily network data fails to transfer. Specifically this is used
* on cellular networks to mask temporary outages when driving through
@@ -2754,9 +2791,8 @@
/**
* Called when the network the framework connected to for this request
- * returns from a {@link NetworkInfo.DetailedState.SUSPENDED} state.
- * This should always be preceeded by a matching {@code onNetworkSuspended}
- * call.
+ * returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
+ * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
* @hide
*/
public void onNetworkResumed(Network network) {}
@@ -2865,7 +2901,9 @@
break;
}
case CALLBACK_AVAILABLE: {
- callback.onAvailable(network);
+ NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+ LinkProperties lp = getObject(message, LinkProperties.class);
+ callback.onAvailable(network, cap, lp);
break;
}
case CALLBACK_LOSING: {
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 31a3096..ecccda5 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -18,9 +18,6 @@
import android.annotation.SystemService;
import android.content.Context;
-import android.net.IEthernetManager;
-import android.net.IEthernetServiceListener;
-import android.net.IpConfiguration;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
@@ -45,18 +42,18 @@
if (msg.what == MSG_AVAILABILITY_CHANGED) {
boolean isAvailable = (msg.arg1 == 1);
for (Listener listener : mListeners) {
- listener.onAvailabilityChanged(isAvailable);
+ listener.onAvailabilityChanged((String) msg.obj, isAvailable);
}
}
}
};
- private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
+ private final ArrayList<Listener> mListeners = new ArrayList<>();
private final IEthernetServiceListener.Stub mServiceListener =
new IEthernetServiceListener.Stub() {
@Override
- public void onAvailabilityChanged(boolean isAvailable) {
+ public void onAvailabilityChanged(String iface, boolean isAvailable) {
mHandler.obtainMessage(
- MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
+ MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, iface).sendToTarget();
}
};
@@ -66,9 +63,10 @@
public interface Listener {
/**
* Called when Ethernet port's availability is changed.
- * @param isAvailable {@code true} if one or more Ethernet port exists.
+ * @param iface Ethernet interface name
+ * @param isAvailable {@code true} if Ethernet port exists.
*/
- public void onAvailabilityChanged(boolean isAvailable);
+ void onAvailabilityChanged(String iface, boolean isAvailable);
}
/**
@@ -86,9 +84,9 @@
* Get Ethernet configuration.
* @return the Ethernet Configuration, contained in {@link IpConfiguration}.
*/
- public IpConfiguration getConfiguration() {
+ public IpConfiguration getConfiguration(String iface) {
try {
- return mService.getConfiguration();
+ return mService.getConfiguration(iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -97,21 +95,29 @@
/**
* Set Ethernet configuration.
*/
- public void setConfiguration(IpConfiguration config) {
+ public void setConfiguration(String iface, IpConfiguration config) {
try {
- mService.setConfiguration(config);
+ mService.setConfiguration(iface, config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Indicates whether the system currently has one or more
- * Ethernet interfaces.
+ * Indicates whether the system currently has one or more Ethernet interfaces.
*/
public boolean isAvailable() {
+ return getAvailableInterfaces().length > 0;
+ }
+
+ /**
+ * Indicates whether the system has given interface.
+ *
+ * @param iface Ethernet interface name
+ */
+ public boolean isAvailable(String iface) {
try {
- return mService.isAvailable();
+ return mService.isAvailable(iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -137,6 +143,17 @@
}
/**
+ * Returns an array of available Ethernet interface names.
+ */
+ public String[] getAvailableInterfaces() {
+ try {
+ return mService.getAvailableInterfaces();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Removes a listener.
* @param listener A {@link Listener} to remove.
* @throws IllegalArgumentException If the listener is null.
diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl
index 7a92eb9..94960b5 100644
--- a/core/java/android/net/IEthernetManager.aidl
+++ b/core/java/android/net/IEthernetManager.aidl
@@ -26,9 +26,10 @@
/** {@hide} */
interface IEthernetManager
{
- IpConfiguration getConfiguration();
- void setConfiguration(in IpConfiguration config);
- boolean isAvailable();
+ String[] getAvailableInterfaces();
+ IpConfiguration getConfiguration(String iface);
+ void setConfiguration(String iface, in IpConfiguration config);
+ boolean isAvailable(String iface);
void addListener(in IEthernetServiceListener listener);
void removeListener(in IEthernetServiceListener listener);
}
diff --git a/core/java/android/net/IEthernetServiceListener.aidl b/core/java/android/net/IEthernetServiceListener.aidl
index 356690e8..782fa19 100644
--- a/core/java/android/net/IEthernetServiceListener.aidl
+++ b/core/java/android/net/IEthernetServiceListener.aidl
@@ -19,5 +19,5 @@
/** @hide */
oneway interface IEthernetServiceListener
{
- void onAvailabilityChanged(boolean isAvailable);
+ void onAvailabilityChanged(String iface, boolean isAvailable);
}
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index d9b57db..3ce0283 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -21,6 +21,7 @@
import android.net.IpSecUdpEncapResponse;
import android.net.IpSecSpiResponse;
import android.net.IpSecTransformResponse;
+import android.net.IpSecTunnelInterfaceResponse;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -31,7 +32,7 @@
interface IIpSecService
{
IpSecSpiResponse allocateSecurityParameterIndex(
- int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
+ in String destinationAddress, int requestedSpi, in IBinder binder);
void releaseSecurityParameterIndex(int resourceId);
@@ -39,11 +40,29 @@
void closeUdpEncapsulationSocket(int resourceId);
- IpSecTransformResponse createTransportModeTransform(in IpSecConfig c, in IBinder binder);
+ IpSecTunnelInterfaceResponse createTunnelInterface(
+ in String localAddr,
+ in String remoteAddr,
+ in Network underlyingNetwork,
+ in IBinder binder);
- void deleteTransportModeTransform(int transformId);
+ void addAddressToTunnelInterface(
+ int tunnelResourceId,
+ String localAddr);
- void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+ void removeAddressFromTunnelInterface(
+ int tunnelResourceId,
+ String localAddr);
- void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+ void deleteTunnelInterface(int resourceId);
+
+ IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder);
+
+ void deleteTransform(int transformId);
+
+ void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
+
+ void applyTunnelModeTransform(int tunnelResourceId, int direction, int transformResourceId);
+
+ void removeTransportModeTransforms(in ParcelFileDescriptor socket);
}
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 005dd6e..10667ae 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -18,10 +18,9 @@
/** {@hide} */
oneway interface INetworkPolicyListener {
-
void onUidRulesChanged(int uid, int uidRules);
void onMeteredIfacesChanged(in String[] meteredIfaces);
void onRestrictBackgroundChanged(boolean restrictBackground);
void onUidPoliciesChanged(int uid, int uidPolicies);
-
+ void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 95e7f60..90e3ffd 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -18,6 +18,7 @@
import android.net.DataUsageRequest;
import android.net.INetworkStatsSession;
+import android.net.Network;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
@@ -53,7 +54,7 @@
void setUidForeground(int uid, boolean uidForeground);
/** Force update of ifaces. */
- void forceUpdateIfaces();
+ void forceUpdateIfaces(in Network[] defaultNetworks);
/** Force update of statistics. */
void forceUpdate();
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index f82627b..c69a4d4 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -231,13 +231,44 @@
}
}
+ /** @hide */
+ public boolean isAuthentication() {
+ switch (getName()) {
+ // Fallthrough
+ case AUTH_HMAC_MD5:
+ case AUTH_HMAC_SHA1:
+ case AUTH_HMAC_SHA256:
+ case AUTH_HMAC_SHA384:
+ case AUTH_HMAC_SHA512:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** @hide */
+ public boolean isEncryption() {
+ return getName().equals(CRYPT_AES_CBC);
+ }
+
+ /** @hide */
+ public boolean isAead() {
+ return getName().equals(AUTH_CRYPT_AES_GCM);
+ }
+
+ // Because encryption keys are sensitive and userdebug builds are used by large user pools
+ // such as beta testers, we only allow sensitive info such as keys on eng builds.
+ private static boolean isUnsafeBuild() {
+ return Build.IS_DEBUGGABLE && Build.IS_ENG;
+ }
+
@Override
public String toString() {
return new StringBuilder()
.append("{mName=")
.append(mName)
.append(", mKey=")
- .append(Build.IS_DEBUGGABLE ? HexDump.toHexString(mKey) : "<hidden>")
+ .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
.append(", mTruncLenBits=")
.append(mTruncLenBits)
.append("}")
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index e6cd3fc..8599f47 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -32,59 +32,29 @@
// MODE_TRANSPORT or MODE_TUNNEL
private int mMode = IpSecTransform.MODE_TRANSPORT;
- // Needs to be valid only for tunnel mode
// Preventing this from being null simplifies Java->Native binder
- private String mLocalAddress = "";
+ private String mSourceAddress = "";
// Preventing this from being null simplifies Java->Native binder
- private String mRemoteAddress = "";
+ private String mDestinationAddress = "";
// The underlying Network that represents the "gateway" Network
// for outbound packets. It may also be used to select packets.
private Network mNetwork;
- /**
- * This class captures the parameters that specifically apply to inbound or outbound traffic.
- */
- public static class Flow {
- // Minimum requirements for identifying a transform
- // SPI identifying the IPsec flow in packet processing
- // and a remote IP address
- private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
+ // Minimum requirements for identifying a transform
+ // SPI identifying the IPsec SA in packet processing
+ // and a destination IP address
+ private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
- // Encryption Algorithm
- private IpSecAlgorithm mEncryption;
+ // Encryption Algorithm
+ private IpSecAlgorithm mEncryption;
- // Authentication Algorithm
- private IpSecAlgorithm mAuthentication;
+ // Authentication Algorithm
+ private IpSecAlgorithm mAuthentication;
- // Authenticated Encryption Algorithm
- private IpSecAlgorithm mAuthenticatedEncryption;
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{mSpiResourceId=")
- .append(mSpiResourceId)
- .append(", mEncryption=")
- .append(mEncryption)
- .append(", mAuthentication=")
- .append(mAuthentication)
- .append(", mAuthenticatedEncryption=")
- .append(mAuthenticatedEncryption)
- .append("}")
- .toString();
- }
-
- static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) {
- if (lhs == null || rhs == null) return (lhs == rhs);
- return (lhs.mSpiResourceId == rhs.mSpiResourceId
- && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
- && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication));
- }
- }
-
- private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()};
+ // Authenticated Encryption Algorithm
+ private IpSecAlgorithm mAuthenticatedEncryption;
// For tunnel mode IPv4 UDP Encapsulation
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
@@ -95,47 +65,46 @@
// An interval, in seconds between the NattKeepalive packets
private int mNattKeepaliveInterval;
+ // XFRM mark and mask
+ private int mMarkValue;
+ private int mMarkMask;
+
/** Set the mode for this IPsec transform */
public void setMode(int mode) {
mMode = mode;
}
- /** Set the local IP address for Tunnel mode */
- public void setLocalAddress(String localAddress) {
- if (localAddress == null) {
- throw new IllegalArgumentException("localAddress may not be null!");
- }
- mLocalAddress = localAddress;
+ /** Set the source IP addres for this IPsec transform */
+ public void setSourceAddress(String sourceAddress) {
+ mSourceAddress = sourceAddress;
}
- /** Set the remote IP address for this IPsec transform */
- public void setRemoteAddress(String remoteAddress) {
- if (remoteAddress == null) {
- throw new IllegalArgumentException("remoteAddress may not be null!");
- }
- mRemoteAddress = remoteAddress;
+ /** Set the destination IP address for this IPsec transform */
+ public void setDestinationAddress(String destinationAddress) {
+ mDestinationAddress = destinationAddress;
}
- /** Set the SPI for a given direction by resource ID */
- public void setSpiResourceId(int direction, int resourceId) {
- mFlow[direction].mSpiResourceId = resourceId;
+ /** Set the SPI by resource ID */
+ public void setSpiResourceId(int resourceId) {
+ mSpiResourceId = resourceId;
}
- /** Set the encryption algorithm for a given direction */
- public void setEncryption(int direction, IpSecAlgorithm encryption) {
- mFlow[direction].mEncryption = encryption;
+ /** Set the encryption algorithm */
+ public void setEncryption(IpSecAlgorithm encryption) {
+ mEncryption = encryption;
}
- /** Set the authentication algorithm for a given direction */
- public void setAuthentication(int direction, IpSecAlgorithm authentication) {
- mFlow[direction].mAuthentication = authentication;
+ /** Set the authentication algorithm */
+ public void setAuthentication(IpSecAlgorithm authentication) {
+ mAuthentication = authentication;
}
- /** Set the authenticated encryption algorithm for a given direction */
- public void setAuthenticatedEncryption(int direction, IpSecAlgorithm authenticatedEncryption) {
- mFlow[direction].mAuthenticatedEncryption = authenticatedEncryption;
+ /** Set the authenticated encryption algorithm */
+ public void setAuthenticatedEncryption(IpSecAlgorithm authenticatedEncryption) {
+ mAuthenticatedEncryption = authenticatedEncryption;
}
+ /** Set the underlying network that will carry traffic for this transform */
public void setNetwork(Network network) {
mNetwork = network;
}
@@ -156,33 +125,41 @@
mNattKeepaliveInterval = interval;
}
+ public void setMarkValue(int mark) {
+ mMarkValue = mark;
+ }
+
+ public void setMarkMask(int mask) {
+ mMarkMask = mask;
+ }
+
// Transport or Tunnel
public int getMode() {
return mMode;
}
- public String getLocalAddress() {
- return mLocalAddress;
+ public String getSourceAddress() {
+ return mSourceAddress;
}
- public int getSpiResourceId(int direction) {
- return mFlow[direction].mSpiResourceId;
+ public int getSpiResourceId() {
+ return mSpiResourceId;
}
- public String getRemoteAddress() {
- return mRemoteAddress;
+ public String getDestinationAddress() {
+ return mDestinationAddress;
}
- public IpSecAlgorithm getEncryption(int direction) {
- return mFlow[direction].mEncryption;
+ public IpSecAlgorithm getEncryption() {
+ return mEncryption;
}
- public IpSecAlgorithm getAuthentication(int direction) {
- return mFlow[direction].mAuthentication;
+ public IpSecAlgorithm getAuthentication() {
+ return mAuthentication;
}
- public IpSecAlgorithm getAuthenticatedEncryption(int direction) {
- return mFlow[direction].mAuthenticatedEncryption;
+ public IpSecAlgorithm getAuthenticatedEncryption() {
+ return mAuthenticatedEncryption;
}
public Network getNetwork() {
@@ -205,6 +182,14 @@
return mNattKeepaliveInterval;
}
+ public int getMarkValue() {
+ return mMarkValue;
+ }
+
+ public int getMarkMask() {
+ return mMarkMask;
+ }
+
// Parcelable Methods
@Override
@@ -215,49 +200,61 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mMode);
- out.writeString(mLocalAddress);
- out.writeString(mRemoteAddress);
+ out.writeString(mSourceAddress);
+ out.writeString(mDestinationAddress);
out.writeParcelable(mNetwork, flags);
- out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption, flags);
- out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags);
- out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption, flags);
+ out.writeInt(mSpiResourceId);
+ out.writeParcelable(mEncryption, flags);
+ out.writeParcelable(mAuthentication, flags);
+ out.writeParcelable(mAuthenticatedEncryption, flags);
out.writeInt(mEncapType);
out.writeInt(mEncapSocketResourceId);
out.writeInt(mEncapRemotePort);
out.writeInt(mNattKeepaliveInterval);
+ out.writeInt(mMarkValue);
+ out.writeInt(mMarkMask);
}
@VisibleForTesting
public IpSecConfig() {}
+ /** Copy constructor */
+ @VisibleForTesting
+ public IpSecConfig(IpSecConfig c) {
+ mMode = c.mMode;
+ mSourceAddress = c.mSourceAddress;
+ mDestinationAddress = c.mDestinationAddress;
+ mNetwork = c.mNetwork;
+ mSpiResourceId = c.mSpiResourceId;
+ mEncryption = c.mEncryption;
+ mAuthentication = c.mAuthentication;
+ mAuthenticatedEncryption = c.mAuthenticatedEncryption;
+ mEncapType = c.mEncapType;
+ mEncapSocketResourceId = c.mEncapSocketResourceId;
+ mEncapRemotePort = c.mEncapRemotePort;
+ mNattKeepaliveInterval = c.mNattKeepaliveInterval;
+ mMarkValue = c.mMarkValue;
+ mMarkMask = c.mMarkMask;
+ }
+
private IpSecConfig(Parcel in) {
mMode = in.readInt();
- mLocalAddress = in.readString();
- mRemoteAddress = in.readString();
+ mSourceAddress = in.readString();
+ mDestinationAddress = in.readString();
mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt();
- mFlow[IpSecTransform.DIRECTION_IN].mEncryption =
+ mSpiResourceId = in.readInt();
+ mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mAuthentication =
+ mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_IN].mAuthenticatedEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt();
- mFlow[IpSecTransform.DIRECTION_OUT].mEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
- mFlow[IpSecTransform.DIRECTION_OUT].mAuthenticatedEncryption =
+ mAuthenticatedEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
mEncapType = in.readInt();
mEncapSocketResourceId = in.readInt();
mEncapRemotePort = in.readInt();
mNattKeepaliveInterval = in.readInt();
+ mMarkValue = in.readInt();
+ mMarkMask = in.readInt();
}
@Override
@@ -266,10 +263,10 @@
strBuilder
.append("{mMode=")
.append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
- .append(", mLocalAddress=")
- .append(mLocalAddress)
- .append(", mRemoteAddress=")
- .append(mRemoteAddress)
+ .append(", mSourceAddress=")
+ .append(mSourceAddress)
+ .append(", mDestinationAddress=")
+ .append(mDestinationAddress)
.append(", mNetwork=")
.append(mNetwork)
.append(", mEncapType=")
@@ -280,10 +277,18 @@
.append(mEncapRemotePort)
.append(", mNattKeepaliveInterval=")
.append(mNattKeepaliveInterval)
- .append(", mFlow[OUT]=")
- .append(mFlow[IpSecTransform.DIRECTION_OUT])
- .append(", mFlow[IN]=")
- .append(mFlow[IpSecTransform.DIRECTION_IN])
+ .append("{mSpiResourceId=")
+ .append(mSpiResourceId)
+ .append(", mEncryption=")
+ .append(mEncryption)
+ .append(", mAuthentication=")
+ .append(mAuthentication)
+ .append(", mAuthenticatedEncryption=")
+ .append(mAuthenticatedEncryption)
+ .append(", mMarkValue=")
+ .append(mMarkValue)
+ .append(", mMarkMask=")
+ .append(mMarkMask)
.append("}");
return strBuilder.toString();
@@ -305,17 +310,20 @@
public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
if (lhs == null || rhs == null) return (lhs == rhs);
return (lhs.mMode == rhs.mMode
- && lhs.mLocalAddress.equals(rhs.mLocalAddress)
- && lhs.mRemoteAddress.equals(rhs.mRemoteAddress)
+ && lhs.mSourceAddress.equals(rhs.mSourceAddress)
+ && lhs.mDestinationAddress.equals(rhs.mDestinationAddress)
&& ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork))
|| (lhs.mNetwork == rhs.mNetwork))
&& lhs.mEncapType == rhs.mEncapType
&& lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId
&& lhs.mEncapRemotePort == rhs.mEncapRemotePort
&& lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
- && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT],
- rhs.mFlow[IpSecTransform.DIRECTION_OUT])
- && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN],
- rhs.mFlow[IpSecTransform.DIRECTION_IN]));
+ && lhs.mSpiResourceId == rhs.mSpiResourceId
+ && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
+ && IpSecAlgorithm.equals(
+ lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+ && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication)
+ && lhs.mMarkValue == rhs.mMarkValue
+ && lhs.mMarkMask == rhs.mMarkMask);
}
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 6a4b891..b609847 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,7 +17,10 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
@@ -33,6 +36,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
@@ -53,6 +58,23 @@
private static final String TAG = "IpSecManager";
/**
+ * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+ * applies to traffic towards the host.
+ */
+ public static final int DIRECTION_IN = 0;
+
+ /**
+ * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
+ * applies to traffic from the host.
+ */
+ public static final int DIRECTION_OUT = 1;
+
+ /** @hide */
+ @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PolicyDirection {}
+
+ /**
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
*
* <p>No IPsec packet may contain an SPI of 0.
@@ -69,7 +91,7 @@
}
/** @hide */
- public static final int INVALID_RESOURCE_ID = 0;
+ public static final int INVALID_RESOURCE_ID = -1;
/**
* Thrown to indicate that a requested SPI is in use.
@@ -125,10 +147,10 @@
*/
public static final class SecurityParameterIndex implements AutoCloseable {
private final IIpSecService mService;
- private final InetAddress mRemoteAddress;
+ private final InetAddress mDestinationAddress;
private final CloseGuard mCloseGuard = CloseGuard.get();
private int mSpi = INVALID_SECURITY_PARAMETER_INDEX;
- private int mResourceId;
+ private int mResourceId = INVALID_RESOURCE_ID;
/** Get the underlying SPI held by this object. */
public int getSpi() {
@@ -146,6 +168,7 @@
public void close() {
try {
mService.releaseSecurityParameterIndex(mResourceId);
+ mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -163,14 +186,14 @@
}
private SecurityParameterIndex(
- @NonNull IIpSecService service, int direction, InetAddress remoteAddress, int spi)
+ @NonNull IIpSecService service, InetAddress destinationAddress, int spi)
throws ResourceUnavailableException, SpiUnavailableException {
mService = service;
- mRemoteAddress = remoteAddress;
+ mDestinationAddress = destinationAddress;
try {
IpSecSpiResponse result =
mService.allocateSecurityParameterIndex(
- direction, remoteAddress.getHostAddress(), spi, new Binder());
+ destinationAddress.getHostAddress(), spi, new Binder());
if (result == null) {
throw new NullPointerException("Received null response from IpSecService");
@@ -215,25 +238,23 @@
}
/**
- * Reserve a random SPI for traffic bound to or from the specified remote address.
+ * Reserve a random SPI for traffic bound to or from the specified destination address.
*
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
* SecurityParameterIndex#close()}.
*
- * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
- * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+ * @param destinationAddress the destination address for traffic bearing the requested SPI.
+ * For inbound traffic, the destination should be an address currently assigned on-device.
* @return the reserved SecurityParameterIndex
- * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
- * for this user
- * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
+ * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * currently allocated for this user
*/
- public SecurityParameterIndex allocateSecurityParameterIndex(
- int direction, InetAddress remoteAddress) throws ResourceUnavailableException {
+ public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
+ throws ResourceUnavailableException {
try {
return new SecurityParameterIndex(
mService,
- direction,
- remoteAddress,
+ destinationAddress,
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
} catch (SpiUnavailableException unlikely) {
throw new ResourceUnavailableException("No SPIs available");
@@ -241,26 +262,27 @@
}
/**
- * Reserve the requested SPI for traffic bound to or from the specified remote address.
+ * Reserve the requested SPI for traffic bound to or from the specified destination address.
*
* <p>If successful, this SPI is guaranteed available until released by a call to {@link
* SecurityParameterIndex#close()}.
*
- * @param direction {@link IpSecTransform#DIRECTION_IN} or {@link IpSecTransform#DIRECTION_OUT}
- * @param remoteAddress address of the remote. SPIs must be unique for each remoteAddress
+ * @param destinationAddress the destination address for traffic bearing the requested SPI.
+ * For inbound traffic, the destination should be an address currently assigned on-device.
* @param requestedSpi the requested SPI, or '0' to allocate a random SPI
* @return the reserved SecurityParameterIndex
- * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
- * for this user
- * @throws SpiUnavailableException indicating that the requested SPI could not be reserved
+ * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * currently allocated for this user
+ * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
+ * reserved
*/
public SecurityParameterIndex allocateSecurityParameterIndex(
- int direction, InetAddress remoteAddress, int requestedSpi)
+ InetAddress destinationAddress, int requestedSpi)
throws SpiUnavailableException, ResourceUnavailableException {
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
}
- return new SecurityParameterIndex(mService, direction, remoteAddress, requestedSpi);
+ return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
}
/**
@@ -268,14 +290,14 @@
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -286,15 +308,14 @@
* in-flight packets have been received.
*
* @param socket a stream socket
+ * @param direction the policy direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
- * @hide
*/
- public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
+ public void applyTransportModeTransform(
+ Socket socket, int direction, IpSecTransform transform)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
+ applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
/**
@@ -302,14 +323,14 @@
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -320,15 +341,13 @@
* in-flight packets have been received.
*
* @param socket a datagram socket
+ * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
- * @hide
*/
- public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
- throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
+ public void applyTransportModeTransform(
+ DatagramSocket socket, int direction, IpSecTransform transform) throws IOException {
+ applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
}
/**
@@ -336,14 +355,14 @@
*
* <p>This applies transport mode encapsulation to the given socket. Once applied, I/O on the
* socket will be encapsulated according to the parameters of the {@code IpSecTransform}. When
- * the transform is removed from the socket by calling {@link #removeTransportModeTransform},
+ * the transform is removed from the socket by calling {@link #removeTransportModeTransforms},
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
* remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
- * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
+ * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
*
* <h4>Rekey Procedure</h4>
*
@@ -354,24 +373,17 @@
* in-flight packets have been received.
*
* @param socket a socket file descriptor
+ * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
* @param transform a transport mode {@code IpSecTransform}
* @throws IOException indicating that the transform could not be applied
*/
- public void applyTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+ public void applyTransportModeTransform(
+ FileDescriptor socket, int direction, IpSecTransform transform)
throws IOException {
// We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
- // constructor takes control and closes the user's FD when we exit the method
- // This is behaviorally the same as the other versions, but the PFD constructor does not
- // dup() automatically, whereas PFD.fromSocket() and PDF.fromDatagramSocket() do dup().
+ // constructor takes control and closes the user's FD when we exit the method.
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
- applyTransportModeTransform(pfd, transform);
- }
- }
-
- /* Call down to activate a transform */
- private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
- try {
- mService.applyTransportModeTransform(pfd, transform.getResourceId());
+ mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -395,75 +407,56 @@
/**
* Remove an IPsec transform from a stream socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
- * @hide
*/
- public void removeTransportModeTransform(Socket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(Socket socket)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a datagram socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
- * @hide
*/
- public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(DatagramSocket socket)
throws IOException {
- try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
+ removeTransportModeTransforms(socket.getFileDescriptor$());
}
/**
* Remove an IPsec transform from a socket.
*
- * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed
- * regardless of the state of the transform. Removing a transform from a socket allows the
- * socket to be reused for communication in the clear.
+ * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
+ * socket allows the socket to be reused for communication in the clear.
*
* <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling
* {@link IpSecTransform#close()}, then communication on the socket will fail until this method
* is called.
*
* @param socket a socket that previously had a transform applied to it
- * @param transform the IPsec Transform that was previously applied to the given socket
* @throws IOException indicating that the transform could not be removed from the socket
*/
- public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+ public void removeTransportModeTransforms(FileDescriptor socket)
throws IOException {
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
- removeTransportModeTransform(pfd, transform);
- }
- }
-
- /* Call down to remove a transform */
- private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {
- try {
- mService.removeTransportModeTransform(pfd, transform.getResourceId());
+ mService.removeTransportModeTransforms(pfd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -501,7 +494,7 @@
public static final class UdpEncapsulationSocket implements AutoCloseable {
private final ParcelFileDescriptor mPfd;
private final IIpSecService mService;
- private final int mResourceId;
+ private int mResourceId = INVALID_RESOURCE_ID;
private final int mPort;
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -554,6 +547,7 @@
public void close() throws IOException {
try {
mService.closeUdpEncapsulationSocket(mResourceId);
+ mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -633,6 +627,172 @@
}
/**
+ * This class represents an IpSecTunnelInterface
+ *
+ * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
+ * local endpoints for IPsec tunnels.
+ *
+ * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be
+ * applied to provide IPsec security to packets sent through the tunnel. While a tunnel
+ * cannot be used in standalone mode within Android, the higher layers may use the tunnel
+ * to create Network objects which are accessible to the Android system.
+ * @hide
+ */
+ @SystemApi
+ public static final class IpSecTunnelInterface implements AutoCloseable {
+ private final IIpSecService mService;
+ private final InetAddress mRemoteAddress;
+ private final InetAddress mLocalAddress;
+ private final Network mUnderlyingNetwork;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private String mInterfaceName;
+ private int mResourceId = INVALID_RESOURCE_ID;
+
+ /** Get the underlying SPI held by this object. */
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ /**
+ * Add an address to the IpSecTunnelInterface
+ *
+ * <p>Add an address which may be used as the local inner address for
+ * tunneled traffic.
+ *
+ * @param address the local address for traffic inside the tunnel
+ * @throws IOException if the address could not be added
+ * @hide
+ */
+ public void addAddress(LinkAddress address) throws IOException {
+ }
+
+ /**
+ * Remove an address from the IpSecTunnelInterface
+ *
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface
+ *
+ * @param address to be removed
+ * @throws IOException if the address could not be removed
+ * @hide
+ */
+ public void removeAddress(LinkAddress address) throws IOException {
+ }
+
+ private IpSecTunnelInterface(@NonNull IIpSecService service,
+ @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
+ @NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+ mService = service;
+ mLocalAddress = localAddress;
+ mRemoteAddress = remoteAddress;
+ mUnderlyingNetwork = underlyingNetwork;
+
+ try {
+ IpSecTunnelInterfaceResponse result =
+ mService.createTunnelInterface(
+ localAddress.getHostAddress(),
+ remoteAddress.getHostAddress(),
+ underlyingNetwork,
+ new Binder());
+ switch (result.status) {
+ case Status.OK:
+ break;
+ case Status.RESOURCE_UNAVAILABLE:
+ throw new ResourceUnavailableException(
+ "No more tunnel interfaces may be allocated by this requester.");
+ default:
+ throw new RuntimeException(
+ "Unknown status returned by IpSecService: " + result.status);
+ }
+ mResourceId = result.resourceId;
+ mInterfaceName = result.interfaceName;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCloseGuard.open("constructor");
+ }
+
+ /**
+ * Delete an IpSecTunnelInterface
+ *
+ * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
+ * resources. Any packets bound for this interface either inbound or outbound will
+ * all be lost.
+ */
+ @Override
+ public void close() {
+ try {
+ mService.deleteTunnelInterface(mResourceId);
+ mResourceId = INVALID_RESOURCE_ID;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mCloseGuard.close();
+ }
+
+ /** Check that the Interface was closed properly. */
+ @Override
+ protected void finalize() throws Throwable {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public int getResourceId() {
+ return mResourceId;
+ }
+ }
+
+ /**
+ * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+ *
+ * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
+ * underlying network goes away, and the onLost() callback is received.
+ *
+ * @param localAddress The local addres of the tunnel
+ * @param remoteAddress The local addres of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+ * This network should almost certainly be a network such as WiFi with an L2 address.
+ * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the socket could not be opened or bound
+ * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
+ @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+ return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+ }
+
+ /**
+ * Apply a transform to the IpSecTunnelInterface
+ *
+ * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+ * transform.
+ * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * the transform will be used.
+ * @param transform an {@link IpSecTransform} created in tunnel mode
+ * @throws IOException indicating that the transform could not be applied due to a lower
+ * layer failure.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
+ IpSecTransform transform) throws IOException {
+ try {
+ mService.applyTunnelModeTransform(
+ tunnel.getResourceId(), direction, transform.getResourceId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ /**
* Construct an instance of IpSecManager within an application context.
*
* @param context the application context for this manager
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 7cd742b..60e96f9 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -17,11 +17,15 @@
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -38,13 +42,11 @@
import java.net.InetAddress;
/**
- * This class represents an IPsec transform, which comprises security associations in one or both
- * directions.
+ * This class represents a transform, which roughly corresponds to an IPsec Security Association.
*
* <p>Transforms are created using {@link IpSecTransform.Builder}. Each {@code IpSecTransform}
- * object encapsulates the properties and state of an inbound and outbound IPsec security
- * association. That includes, but is not limited to, algorithm choice, key material, and allocated
- * system resources.
+ * object encapsulates the properties and state of an IPsec security association. That includes,
+ * but is not limited to, algorithm choice, key material, and allocated system resources.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
* Internet Protocol</a>
@@ -52,23 +54,6 @@
public final class IpSecTransform implements AutoCloseable {
private static final String TAG = "IpSecTransform";
- /**
- * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
- * applies to traffic towards the host.
- */
- public static final int DIRECTION_IN = 0;
-
- /**
- * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
- * applies to traffic from the host.
- */
- public static final int DIRECTION_OUT = 1;
-
- /** @hide */
- @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface TransformDirection {}
-
/** @hide */
public static final int MODE_TRANSPORT = 0;
@@ -99,9 +84,11 @@
@Retention(RetentionPolicy.SOURCE)
public @interface EncapType {}
- private IpSecTransform(Context context, IpSecConfig config) {
+ /** @hide */
+ @VisibleForTesting
+ public IpSecTransform(Context context, IpSecConfig config) {
mContext = context;
- mConfig = config;
+ mConfig = new IpSecConfig(config);
mResourceId = INVALID_RESOURCE_ID;
}
@@ -143,18 +130,10 @@
synchronized (this) {
try {
IIpSecService svc = getIpSecService();
- IpSecTransformResponse result =
- svc.createTransportModeTransform(mConfig, new Binder());
+ IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
int status = result.status;
checkResultStatus(status);
mResourceId = result.resourceId;
-
- /* Keepalive will silently fail if not needed by the config; but, if needed and
- * it fails to start, we need to bail because a transform will not be reliable
- * to use if keepalive is expected to offload and fails.
- */
- // FIXME: if keepalive fails, we need to fail spectacularly
- startKeepalive(mContext);
Log.d(TAG, "Added Transform with Id " + mResourceId);
mCloseGuard.open("build");
} catch (RemoteException e) {
@@ -166,11 +145,23 @@
}
/**
+ * Equals method used for testing
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) {
+ if (lhs == null || rhs == null) return (lhs == rhs);
+ return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig())
+ && lhs.mResourceId == rhs.mResourceId;
+ }
+
+ /**
* Deactivate this {@code IpSecTransform} and free allocated resources.
*
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
* that socket. Make sure to remove transforms by calling {@link
- * IpSecManager#removeTransportModeTransform}. Note, removing an {@code IpSecTransform} from a
+ * IpSecManager#removeTransportModeTransforms}. Note, removing an {@code IpSecTransform} from a
* socket will not deactivate it (because one transform may be applied to multiple sockets).
*
* <p>It is safe to call this method on a transform that has already been deactivated.
@@ -184,13 +175,9 @@
return;
}
try {
- /* Order matters here because the keepalive is best-effort but could fail in some
- * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
- * still want to clear out the transform.
- */
IIpSecService svc = getIpSecService();
- svc.deleteTransportModeTransform(mResourceId);
- stopKeepalive();
+ svc.deleteTransform(mResourceId);
+ stopNattKeepalive();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
@@ -218,42 +205,35 @@
private final Context mContext;
private final CloseGuard mCloseGuard = CloseGuard.get();
private ConnectivityManager.PacketKeepalive mKeepalive;
- private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
- private Object mKeepaliveSyncLock = new Object();
- private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
+ private Handler mCallbackHandler;
+ private final ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
new ConnectivityManager.PacketKeepaliveCallback() {
@Override
public void onStarted() {
- synchronized (mKeepaliveSyncLock) {
- mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
- mKeepaliveSyncLock.notifyAll();
+ synchronized (this) {
+ mCallbackHandler.post(() -> mUserKeepaliveCallback.onStarted());
}
}
@Override
public void onStopped() {
- synchronized (mKeepaliveSyncLock) {
- mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
- mKeepaliveSyncLock.notifyAll();
+ synchronized (this) {
+ mKeepalive = null;
+ mCallbackHandler.post(() -> mUserKeepaliveCallback.onStopped());
}
}
@Override
public void onError(int error) {
- synchronized (mKeepaliveSyncLock) {
- mKeepaliveStatus = error;
- mKeepaliveSyncLock.notifyAll();
+ synchronized (this) {
+ mKeepalive = null;
+ mCallbackHandler.post(() -> mUserKeepaliveCallback.onError(error));
}
}
};
- /* Package */
- void startKeepalive(Context c) {
- if (mConfig.getNattKeepaliveInterval() != 0) {
- Log.wtf(TAG, "Keepalive not yet supported.");
- }
- }
+ private NattKeepaliveCallback mUserKeepaliveCallback;
/** @hide */
@VisibleForTesting
@@ -261,9 +241,101 @@
return mResourceId;
}
- /* Package */
- void stopKeepalive() {
- return;
+ /**
+ * A callback class to provide status information regarding a NAT-T keepalive session
+ *
+ * <p>Use this callback to receive status information regarding a NAT-T keepalive session
+ * by registering it when calling {@link #startNattKeepalive}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static class NattKeepaliveCallback {
+ /** The specified {@code Network} is not connected. */
+ public static final int ERROR_INVALID_NETWORK = 1;
+ /** The hardware does not support this request. */
+ public static final int ERROR_HARDWARE_UNSUPPORTED = 2;
+ /** The hardware returned an error. */
+ public static final int ERROR_HARDWARE_ERROR = 3;
+
+ /** The requested keepalive was successfully started. */
+ public void onStarted() {}
+ /** The keepalive was successfully stopped. */
+ public void onStopped() {}
+ /** An error occurred. */
+ public void onError(int error) {}
+ }
+
+ /**
+ * Start a NAT-T keepalive session for the current transform.
+ *
+ * For a transform that is using UDP encapsulated IPv4, NAT-T offloading provides
+ * a power efficient mechanism of sending NAT-T packets at a specified interval.
+ *
+ * @param userCallback a {@link #NattKeepaliveCallback} to receive asynchronous status
+ * information about the requested NAT-T keepalive session.
+ * @param intervalSeconds the interval between NAT-T keepalives being sent. The
+ * the allowed range is between 20 and 3600 seconds.
+ * @param handler a handler on which to post callbacks when received.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+ })
+ public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
+ int intervalSeconds, @NonNull Handler handler) throws IOException {
+ checkNotNull(userCallback);
+ if (intervalSeconds < 20 || intervalSeconds > 3600) {
+ throw new IllegalArgumentException("Invalid NAT-T keepalive interval");
+ }
+ checkNotNull(handler);
+ if (mResourceId == INVALID_RESOURCE_ID) {
+ throw new IllegalStateException(
+ "Packet keepalive cannot be started for an inactive transform");
+ }
+
+ synchronized (mKeepaliveCallback) {
+ if (mKeepaliveCallback != null) {
+ throw new IllegalStateException("Keepalive already active");
+ }
+
+ mUserKeepaliveCallback = userCallback;
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ mKeepalive = cm.startNattKeepalive(
+ mConfig.getNetwork(), intervalSeconds, mKeepaliveCallback,
+ NetworkUtils.numericToInetAddress(mConfig.getSourceAddress()),
+ 4500, // FIXME urgently, we need to get the port number from the Encap socket
+ NetworkUtils.numericToInetAddress(mConfig.getDestinationAddress()));
+ mCallbackHandler = handler;
+ }
+ }
+
+ /**
+ * Stop an ongoing NAT-T keepalive session.
+ *
+ * Calling this API will request that an ongoing NAT-T keepalive session be terminated.
+ * If this API is not called when a Transform is closed, the underlying NAT-T session will
+ * be terminated automatically.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+ })
+ public void stopNattKeepalive() {
+ synchronized (mKeepaliveCallback) {
+ if (mKeepalive == null) {
+ Log.e(TAG, "No active keepalive to stop");
+ return;
+ }
+ mKeepalive.stop();
+ }
}
/** This class is used to build {@link IpSecTransform} objects. */
@@ -272,96 +344,49 @@
private IpSecConfig mConfig;
/**
- * Set the encryption algorithm for the given direction.
- *
- * <p>If encryption is set for a direction without also providing an SPI for that direction,
- * creation of an {@code IpSecTransform} will fail when attempting to build the transform.
+ * Set the encryption algorithm.
*
* <p>Encryption is mutually exclusive with authenticated encryption.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
*/
- public IpSecTransform.Builder setEncryption(
- @TransformDirection int direction, IpSecAlgorithm algo) {
+ public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
- mConfig.setEncryption(direction, algo);
+ Preconditions.checkNotNull(algo);
+ mConfig.setEncryption(algo);
return this;
}
/**
- * Set the authentication (integrity) algorithm for the given direction.
- *
- * <p>If authentication is set for a direction without also providing an SPI for that
- * direction, creation of an {@code IpSecTransform} will fail when attempting to build the
- * transform.
+ * Set the authentication (integrity) algorithm.
*
* <p>Authentication is mutually exclusive with authenticated encryption.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
*/
- public IpSecTransform.Builder setAuthentication(
- @TransformDirection int direction, IpSecAlgorithm algo) {
+ public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
- mConfig.setAuthentication(direction, algo);
+ Preconditions.checkNotNull(algo);
+ mConfig.setAuthentication(algo);
return this;
}
/**
- * Set the authenticated encryption algorithm for the given direction.
+ * Set the authenticated encryption algorithm.
*
- * <p>If an authenticated encryption algorithm is set for a given direction without also
- * providing an SPI for that direction, creation of an {@code IpSecTransform} will fail when
- * attempting to build the transform.
- *
- * <p>The Authenticated Encryption (AE) class of algorithms are also known as Authenticated
- * Encryption with Associated Data (AEAD) algorithms, or Combined mode algorithms (as
- * referred to in <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
+ * <p>The Authenticated Encryption (AE) class of algorithms are also known as
+ * Authenticated Encryption with Associated Data (AEAD) algorithms, or Combined mode
+ * algorithms (as referred to in
+ * <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
*
* <p>Authenticated encryption is mutually exclusive with encryption and authentication.
*
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
* @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
* be applied.
*/
- public IpSecTransform.Builder setAuthenticatedEncryption(
- @TransformDirection int direction, IpSecAlgorithm algo) {
- mConfig.setAuthenticatedEncryption(direction, algo);
- return this;
- }
-
- /**
- * Set the SPI for the given direction.
- *
- * <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies
- * packets to a given destination address. To prevent SPI collisions, values should be
- * reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}.
- *
- * <p>If the SPI and algorithms are omitted for one direction, traffic in that direction
- * will not be encrypted or authenticated.
- *
- * @param direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
- * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
- * traffic
- */
- public IpSecTransform.Builder setSpi(
- @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
- mConfig.setSpiResourceId(direction, spi.getResourceId());
- return this;
- }
-
- /**
- * Set the {@link Network} which will carry tunneled traffic.
- *
- * <p>Restricts the transformed traffic to a particular {@link Network}. This is required
- * for tunnel mode, otherwise tunneled traffic would be sent on the default network.
- *
- * @hide
- */
- @SystemApi
- public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
- mConfig.setNetwork(net);
+ public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
+ Preconditions.checkNotNull(algo);
+ mConfig.setAuthenticatedEncryption(algo);
return this;
}
@@ -379,33 +404,17 @@
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
*/
public IpSecTransform.Builder setIpv4Encapsulation(
- IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+ @NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+ Preconditions.checkNotNull(localSocket);
mConfig.setEncapType(ENCAP_ESPINUDP);
+ if (localSocket.getResourceId() == INVALID_RESOURCE_ID) {
+ throw new IllegalArgumentException("Invalid UdpEncapsulationSocket");
+ }
mConfig.setEncapSocketResourceId(localSocket.getResourceId());
mConfig.setEncapRemotePort(remotePort);
return this;
}
- // TODO: Decrease the minimum keepalive to maybe 10?
- // TODO: Probably a better exception to throw for NATTKeepalive failure
- // TODO: Specify the needed NATT keepalive permission.
- /**
- * Set NAT-T keepalives to be sent with a given interval.
- *
- * <p>This will set power-efficient keepalive packets to be sent by the system. If NAT-T
- * keepalive is requested but cannot be activated, then creation of an {@link
- * IpSecTransform} will fail when calling the build method.
- *
- * @param intervalSeconds the maximum number of seconds between keepalive packets. Must be
- * between 20s and 3600s.
- * @hide
- */
- @SystemApi
- public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
- mConfig.setNattKeepaliveInterval(intervalSeconds);
- return this;
- }
-
/**
* Build a transport mode {@link IpSecTransform}.
*
@@ -413,21 +422,33 @@
* will not affect any network traffic until it has been applied to one or more sockets.
*
* @see IpSecManager#applyTransportModeTransform
- * @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use
- * this transform
+ * @param sourceAddress the source {@code InetAddress} of traffic on sockets that will use
+ * this transform; this address must belong to the Network used by all sockets that
+ * utilize this transform; if provided, then only traffic originating from the
+ * specified source address will be processed.
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid
- * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms are
- * active
+ * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+ * are active
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
* collides with an existing transform
* @throws IOException indicating other errors
*/
- public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
+ public IpSecTransform buildTransportModeTransform(
+ @NonNull InetAddress sourceAddress,
+ @NonNull IpSecManager.SecurityParameterIndex spi)
throws IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException, IOException {
+ Preconditions.checkNotNull(sourceAddress);
+ Preconditions.checkNotNull(spi);
+ if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+ throw new IllegalArgumentException("Invalid SecurityParameterIndex");
+ }
mConfig.setMode(MODE_TRANSPORT);
- mConfig.setRemoteAddress(remoteAddress.getHostAddress());
+ mConfig.setSourceAddress(sourceAddress.getHostAddress());
+ mConfig.setSpiResourceId(spi.getResourceId());
// FIXME: modifying a builder after calling build can change the built transform.
return new IpSecTransform(mContext, mConfig).activate();
}
@@ -436,23 +457,36 @@
* Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
* parameters have interdependencies that are checked at build time.
*
- * @param localAddress the {@link InetAddress} that provides the local endpoint for this
+ * @param sourceAddress the {@link InetAddress} that provides the source address for this
* IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
* that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
- * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
- * IPsec tunnel.
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid.
+ * @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
+ * are active
+ * @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
+ * collides with an existing transform
+ * @throws IOException indicating other errors
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public IpSecTransform buildTunnelModeTransform(
- InetAddress localAddress, InetAddress remoteAddress) {
- // FIXME: argument validation here
- // throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
- mConfig.setLocalAddress(localAddress.getHostAddress());
- mConfig.setRemoteAddress(remoteAddress.getHostAddress());
+ @NonNull InetAddress sourceAddress,
+ @NonNull IpSecManager.SecurityParameterIndex spi)
+ throws IpSecManager.ResourceUnavailableException,
+ IpSecManager.SpiUnavailableException, IOException {
+ Preconditions.checkNotNull(sourceAddress);
+ Preconditions.checkNotNull(spi);
+ if (spi.getResourceId() == INVALID_RESOURCE_ID) {
+ throw new IllegalArgumentException("Invalid SecurityParameterIndex");
+ }
mConfig.setMode(MODE_TUNNEL);
- return new IpSecTransform(mContext, mConfig);
+ mConfig.setSourceAddress(sourceAddress.getHostAddress());
+ mConfig.setSpiResourceId(spi.getResourceId());
+ return new IpSecTransform(mContext, mConfig).activate();
}
/**
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/core/java/android/net/IpSecTunnelInterfaceResponse.aidl
similarity index 82%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/net/IpSecTunnelInterfaceResponse.aidl
index d750363..7239221 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/net/IpSecTunnelInterfaceResponse.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-/** @hide */
-package android.telephony.data;
+package android.net;
-parcelable InterfaceAddress;
+/** @hide */
+parcelable IpSecTunnelInterfaceResponse;
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.java b/core/java/android/net/IpSecTunnelInterfaceResponse.java
new file mode 100644
index 0000000..c23d831
--- /dev/null
+++ b/core/java/android/net/IpSecTunnelInterfaceResponse.java
@@ -0,0 +1,78 @@
+/*
+ * 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class is used to return an IpSecTunnelInterface resource Id and and corresponding status
+ * from the IpSecService to an IpSecTunnelInterface object.
+ *
+ * @hide
+ */
+public final class IpSecTunnelInterfaceResponse implements Parcelable {
+ private static final String TAG = "IpSecTunnelInterfaceResponse";
+
+ public final int resourceId;
+ public final String interfaceName;
+ public final int status;
+ // Parcelable Methods
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(status);
+ out.writeInt(resourceId);
+ out.writeString(interfaceName);
+ }
+
+ public IpSecTunnelInterfaceResponse(int inStatus) {
+ if (inStatus == IpSecManager.Status.OK) {
+ throw new IllegalArgumentException("Valid status implies other args must be provided");
+ }
+ status = inStatus;
+ resourceId = IpSecManager.INVALID_RESOURCE_ID;
+ interfaceName = "";
+ }
+
+ public IpSecTunnelInterfaceResponse(int inStatus, int inResourceId, String inInterfaceName) {
+ status = inStatus;
+ resourceId = inResourceId;
+ interfaceName = inInterfaceName;
+ }
+
+ private IpSecTunnelInterfaceResponse(Parcel in) {
+ status = in.readInt();
+ resourceId = in.readInt();
+ interfaceName = in.readString();
+ }
+
+ public static final Parcelable.Creator<IpSecTunnelInterfaceResponse> CREATOR =
+ new Parcelable.Creator<IpSecTunnelInterfaceResponse>() {
+ public IpSecTunnelInterfaceResponse createFromParcel(Parcel in) {
+ return new IpSecTunnelInterfaceResponse(in);
+ }
+
+ public IpSecTunnelInterfaceResponse[] newArray(int size) {
+ return new IpSecTunnelInterfaceResponse[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/ims/ImsSsData.aidl b/core/java/android/net/KeepalivePacketData.aidl
similarity index 83%
copy from telephony/java/com/android/ims/ImsSsData.aidl
copy to core/java/android/net/KeepalivePacketData.aidl
index 33f8306..d456b53 100644
--- a/telephony/java/com/android/ims/ImsSsData.aidl
+++ b/core/java/android/net/KeepalivePacketData.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.net;
-parcelable ImsSsData;
+parcelable KeepalivePacketData;
diff --git a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
similarity index 64%
rename from services/core/java/com/android/server/connectivity/KeepalivePacketData.java
rename to core/java/android/net/KeepalivePacketData.java
index 2ccfdd1..7436ad0 100644
--- a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -14,30 +14,29 @@
* limitations under the License.
*/
-package com.android.server.connectivity;
+package android.net;
-import android.system.OsConstants;
-import android.net.ConnectivityManager;
-import android.net.NetworkUtils;
+import static android.net.ConnectivityManager.PacketKeepalive.*;
+
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.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import static android.net.ConnectivityManager.PacketKeepalive.*;
-
/**
* Represents the actual packets that are sent by the
* {@link android.net.ConnectivityManager.PacketKeepalive} API.
*
* @hide
*/
-public class KeepalivePacketData {
- /** Protocol of the packet to send; one of the OsConstants.ETH_P_* values. */
- public final int protocol;
+public class KeepalivePacketData implements Parcelable {
+ private static final String TAG = "KeepalivePacketData";
/** Source IP address */
public final InetAddress srcAddress;
@@ -51,57 +50,50 @@
/** Destination port */
public final int dstPort;
- /** Destination MAC address. Can change if routing changes. */
- public byte[] dstMac;
-
/** Packet data. A raw byte string of packet data, not including the link-layer header. */
- public final byte[] data;
+ private final byte[] mPacket;
private static final int IPV4_HEADER_LENGTH = 20;
private static final int UDP_HEADER_LENGTH = 8;
+ // This should only be constructed via static factory methods, such as
+ // nattKeepalivePacket
protected KeepalivePacketData(InetAddress srcAddress, int srcPort,
InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException {
this.srcAddress = srcAddress;
this.dstAddress = dstAddress;
this.srcPort = srcPort;
this.dstPort = dstPort;
- this.data = data;
+ this.mPacket = data;
// Check we have two IP addresses of the same family.
- if (srcAddress == null || dstAddress == null ||
- !srcAddress.getClass().getName().equals(dstAddress.getClass().getName())) {
- throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
- }
-
- // Set the protocol.
- if (this.dstAddress instanceof Inet4Address) {
- this.protocol = OsConstants.ETH_P_IP;
- } else if (this.dstAddress instanceof Inet6Address) {
- this.protocol = OsConstants.ETH_P_IPV6;
- } else {
+ if (srcAddress == null || dstAddress == null || !srcAddress.getClass().getName()
+ .equals(dstAddress.getClass().getName())) {
+ Log.e(TAG, "Invalid or mismatched InetAddresses in KeepalivePacketData");
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
}
// Check the ports.
if (!IpUtils.isValidUdpOrTcpPort(srcPort) || !IpUtils.isValidUdpOrTcpPort(dstPort)) {
+ Log.e(TAG, "Invalid ports in KeepalivePacketData");
throw new InvalidPacketException(ERROR_INVALID_PORT);
}
}
public static class InvalidPacketException extends Exception {
- final public int error;
+ public final int error;
public InvalidPacketException(int error) {
this.error = error;
}
}
- /**
- * Creates an IPsec NAT-T keepalive packet with the specified parameters.
- */
+ public byte[] getPacket() {
+ return mPacket.clone();
+ }
+
public static KeepalivePacketData nattKeepalivePacket(
- InetAddress srcAddress, int srcPort,
- InetAddress dstAddress, int dstPort) throws InvalidPacketException {
+ InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
+ throws InvalidPacketException {
if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
@@ -134,4 +126,39 @@
return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
}
+
+ /* Parcelable Implementation */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Write to parcel */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(srcAddress.getHostAddress());
+ out.writeString(dstAddress.getHostAddress());
+ out.writeInt(srcPort);
+ out.writeInt(dstPort);
+ out.writeByteArray(mPacket);
+ }
+
+ private KeepalivePacketData(Parcel in) {
+ srcAddress = NetworkUtils.numericToInetAddress(in.readString());
+ dstAddress = NetworkUtils.numericToInetAddress(in.readString());
+ srcPort = in.readInt();
+ dstPort = in.readInt();
+ mPacket = in.createByteArray();
+ }
+
+ /** Parcelable Creator */
+ public static final Parcelable.Creator<KeepalivePacketData> CREATOR =
+ new Parcelable.Creator<KeepalivePacketData>() {
+ public KeepalivePacketData createFromParcel(Parcel in) {
+ return new KeepalivePacketData(in);
+ }
+
+ public KeepalivePacketData[] newArray(int size) {
+ return new KeepalivePacketData[size];
+ }
+ };
+
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4e474c8..f525b1f 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -50,6 +50,8 @@
private String mIfaceName;
private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
+ private boolean mUsePrivateDns;
+ private String mPrivateDnsServerName;
private String mDomains;
private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
private ProxyInfo mHttpProxy;
@@ -165,6 +167,8 @@
mIfaceName = source.getInterfaceName();
for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
for (InetAddress i : source.getDnsServers()) mDnses.add(i);
+ mUsePrivateDns = source.mUsePrivateDns;
+ mPrivateDnsServerName = source.mPrivateDnsServerName;
mDomains = source.getDomains();
for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
mHttpProxy = (source.getHttpProxy() == null) ?
@@ -391,6 +395,59 @@
}
/**
+ * Set whether private DNS is currently in use on this network.
+ *
+ * @param usePrivateDns The private DNS state.
+ * @hide
+ */
+ public void setUsePrivateDns(boolean usePrivateDns) {
+ mUsePrivateDns = usePrivateDns;
+ }
+
+ /**
+ * Returns whether private DNS is currently in use on this network. When
+ * private DNS is in use, applications must not send unencrypted DNS
+ * queries as doing so could reveal private user information. Furthermore,
+ * if private DNS is in use and {@link #getPrivateDnsServerName} is not
+ * {@code null}, DNS queries must be sent to the specified DNS server.
+ *
+ * @return {@code true} if private DNS is in use, {@code false} otherwise.
+ */
+ public boolean isPrivateDnsActive() {
+ return mUsePrivateDns;
+ }
+
+ /**
+ * Set the name of the private DNS server to which private DNS queries
+ * should be sent when in strict mode. This value should be {@code null}
+ * when private DNS is off or in opportunistic mode.
+ *
+ * @param privateDnsServerName The private DNS server name.
+ * @hide
+ */
+ public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
+ mPrivateDnsServerName = privateDnsServerName;
+ }
+
+ /**
+ * Returns the private DNS server name that is in use. If not {@code null},
+ * private DNS is in strict mode. In this mode, applications should ensure
+ * that all DNS queries are encrypted and sent to this hostname and that
+ * queries are only sent if the hostname's certificate is valid. If
+ * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private
+ * DNS is in opportunistic mode, and applications should ensure that DNS
+ * queries are encrypted and sent to a DNS server returned by
+ * {@link #getDnsServers}. System DNS will handle each of these cases
+ * correctly, but applications implementing their own DNS lookups must make
+ * sure to follow these requirements.
+ *
+ * @return The private DNS server name.
+ */
+ public @Nullable String getPrivateDnsServerName() {
+ return mPrivateDnsServerName;
+ }
+
+ /**
* Sets the DNS domain search path used on this link.
*
* @param domains A {@link String} listing in priority order the comma separated
@@ -622,6 +679,8 @@
mIfaceName = null;
mLinkAddresses.clear();
mDnses.clear();
+ mUsePrivateDns = false;
+ mPrivateDnsServerName = null;
mDomains = null;
mRoutes.clear();
mHttpProxy = null;
@@ -649,6 +708,13 @@
for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
dns += "] ";
+ String usePrivateDns = "UsePrivateDns: " + mUsePrivateDns + " ";
+
+ String privateDnsServerName = "";
+ if (privateDnsServerName != null) {
+ privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
+ }
+
String domainName = "Domains: " + mDomains;
String mtu = " MTU: " + mMtu;
@@ -671,8 +737,9 @@
}
stacked += "] ";
}
- return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu
- + tcpBuffSizes + proxy + stacked + "}";
+ return "{" + ifaceName + linkAddresses + routes + dns + usePrivateDns
+ + privateDnsServerName + domainName + mtu + tcpBuffSizes + proxy
+ + stacked + "}";
}
/**
@@ -896,6 +963,20 @@
}
/**
+ * Compares this {@code LinkProperties} private DNS settings against the
+ * target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalPrivateDns(LinkProperties target) {
+ return (isPrivateDnsActive() == target.isPrivateDnsActive()
+ && TextUtils.equals(getPrivateDnsServerName(),
+ target.getPrivateDnsServerName()));
+ }
+
+ /**
* Compares this {@code LinkProperties} Routes against the target
*
* @param target LinkProperties to compare.
@@ -989,14 +1070,15 @@
* stacked interfaces are not so much a property of the link as a
* description of connections between links.
*/
- return isIdenticalInterfaceName(target) &&
- isIdenticalAddresses(target) &&
- isIdenticalDnses(target) &&
- isIdenticalRoutes(target) &&
- isIdenticalHttpProxy(target) &&
- isIdenticalStackedLinks(target) &&
- isIdenticalMtu(target) &&
- isIdenticalTcpBufferSizes(target);
+ return isIdenticalInterfaceName(target)
+ && isIdenticalAddresses(target)
+ && isIdenticalDnses(target)
+ && isIdenticalPrivateDns(target)
+ && isIdenticalRoutes(target)
+ && isIdenticalHttpProxy(target)
+ && isIdenticalStackedLinks(target)
+ && isIdenticalMtu(target)
+ && isIdenticalTcpBufferSizes(target);
}
/**
@@ -1091,7 +1173,9 @@
+ ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
+ mStackedLinks.hashCode() * 47)
+ mMtu * 51
- + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode());
+ + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+ + (mUsePrivateDns ? 57 : 0)
+ + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
}
/**
@@ -1108,6 +1192,8 @@
for(InetAddress d : mDnses) {
dest.writeByteArray(d.getAddress());
}
+ dest.writeBoolean(mUsePrivateDns);
+ dest.writeString(mPrivateDnsServerName);
dest.writeString(mDomains);
dest.writeInt(mMtu);
dest.writeString(mTcpBufferSizes);
@@ -1148,6 +1234,8 @@
netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
} catch (UnknownHostException e) { }
}
+ netProp.setUsePrivateDns(in.readBoolean());
+ netProp.setPrivateDnsServerName(in.readString());
netProp.setDomains(in.readString());
netProp.setMtu(in.readInt());
netProp.setTcpBufferSizes(in.readString());
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index d6992aa..74d6470 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,6 +26,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Random;
@@ -60,7 +62,7 @@
})
public @interface MacAddressType { }
- /** Indicates a MAC address of unknown type. */
+ /** @hide Indicates a MAC address of unknown type. */
public static final int TYPE_UNKNOWN = 0;
/** Indicates a MAC address is a unicast address. */
public static final int TYPE_UNICAST = 1;
@@ -92,7 +94,7 @@
*
* @return the int constant representing the MAC address type of this MacAddress.
*/
- public @MacAddressType int addressType() {
+ public @MacAddressType int getAddressType() {
if (equals(BROADCAST_ADDRESS)) {
return TYPE_BROADCAST;
}
@@ -120,12 +122,12 @@
/**
* @return a byte array representation of this MacAddress.
*/
- public byte[] toByteArray() {
+ public @NonNull byte[] toByteArray() {
return byteAddrFromLongAddr(mAddr);
}
@Override
- public String toString() {
+ public @NonNull String toString() {
return stringAddrFromLongAddr(mAddr);
}
@@ -133,7 +135,7 @@
* @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal
* numbers in [0,ff] joined by ':' characters.
*/
- public String toOuiString() {
+ public @NonNull String toOuiString() {
return String.format(
"%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff);
}
@@ -197,7 +199,7 @@
if (!isMacAddress(addr)) {
return TYPE_UNKNOWN;
}
- return MacAddress.fromBytes(addr).addressType();
+ return MacAddress.fromBytes(addr).getAddressType();
}
/**
@@ -211,7 +213,7 @@
*
* @hide
*/
- public static byte[] byteAddrFromStringAddr(String addr) {
+ public static @NonNull byte[] byteAddrFromStringAddr(String addr) {
Preconditions.checkNotNull(addr);
String[] parts = addr.split(":");
if (parts.length != ETHER_ADDR_LEN) {
@@ -239,7 +241,7 @@
*
* @hide
*/
- public static String stringAddrFromByteAddr(byte[] addr) {
+ public static @NonNull String stringAddrFromByteAddr(byte[] addr) {
if (!isMacAddress(addr)) {
return null;
}
@@ -291,7 +293,7 @@
// Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
// that avoids the allocation of an intermediary byte[].
- private static String stringAddrFromLongAddr(long addr) {
+ private static @NonNull String stringAddrFromLongAddr(long addr) {
return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
(addr >> 40) & 0xff,
(addr >> 32) & 0xff,
@@ -310,7 +312,7 @@
* @return the MacAddress corresponding to the given String representation.
* @throws IllegalArgumentException if the given String is not a valid representation.
*/
- public static MacAddress fromString(String addr) {
+ public static @NonNull MacAddress fromString(@NonNull String addr) {
return new MacAddress(longAddrFromStringAddr(addr));
}
@@ -322,22 +324,40 @@
* @return the MacAddress corresponding to the given byte array representation.
* @throws IllegalArgumentException if the given byte array is not a valid representation.
*/
- public static MacAddress fromBytes(byte[] addr) {
+ public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) {
return new MacAddress(longAddrFromByteAddr(addr));
}
/**
* Returns a generated MAC address whose 24 least significant bits constituting the
- * NIC part of the address are randomly selected.
+ * NIC part of the address are randomly selected and has Google OUI base.
*
* The locally assigned bit is always set to 1. The multicast bit is always set to 0.
*
- * @return a random locally assigned MacAddress.
+ * @return a random locally assigned, unicast MacAddress with Google OUI.
*
* @hide
*/
- public static MacAddress createRandomUnicastAddress() {
- return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
+ public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
+ return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
+ }
+
+ /**
+ * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
+ * unicast bit, are randomly selected.
+ *
+ * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+ *
+ * @return a random locally assigned, unicast MacAddress.
+ *
+ * @hide
+ */
+ public static @NonNull MacAddress createRandomUnicastAddress() {
+ SecureRandom r = new SecureRandom();
+ long addr = r.nextLong() & VALID_LONG_MASK;
+ addr |= LOCALLY_ASSIGNED_MASK;
+ addr &= ~MULTICAST_MASK;
+ return new MacAddress(addr);
}
/**
@@ -352,10 +372,10 @@
*
* @hide
*/
- public static MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
+ public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
- addr = addr | LOCALLY_ASSIGNED_MASK;
- addr = addr & ~MULTICAST_MASK;
+ addr |= LOCALLY_ASSIGNED_MASK;
+ addr &= ~MULTICAST_MASK;
return new MacAddress(addr);
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 903b602..3683d34 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -356,13 +356,13 @@
// Multiple Provisioning Domains API recommendations, as made by the
// IETF mif working group.
//
- // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+ // The handleMagic value MUST be kept in sync with the corresponding
// value in the native/android/net.c NDK implementation.
if (netId == 0) {
return 0L; // make this zero condition obvious for debugging
}
- final long HANDLE_MAGIC = 0xfacade;
- return (((long) netId) << 32) | HANDLE_MAGIC;
+ final long handleMagic = 0xcafed00dL;
+ return (((long) netId) << 32) | handleMagic;
}
// implement the Parcelable interface
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 2dacf8f..52a2354 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,6 +17,7 @@
package android.net;
import android.content.Context;
+import android.net.ConnectivityManager.PacketKeepalive;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -26,7 +27,6 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
-import android.net.ConnectivityManager.PacketKeepalive;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -101,20 +101,6 @@
public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
/**
- * Sent by the NetworkAgent to ConnectivityService to add new UID ranges
- * to be forced into this Network. For VPNs only.
- * obj = UidRange[] to forward
- */
- public static final int EVENT_UID_RANGES_ADDED = BASE + 5;
-
- /**
- * Sent by the NetworkAgent to ConnectivityService to remove UID ranges
- * from being forced into this Network. For VPNs only.
- * obj = UidRange[] to stop forwarding
- */
- public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
-
- /**
* Sent by ConnectivityService to the NetworkAgent to inform the agent of the
* networks status - whether we could use the network or could not, due to
* either a bad network configuration (no internet link) or captive portal.
@@ -390,22 +376,6 @@
}
/**
- * Called by the VPN code when it wants to add ranges of UIDs to be routed
- * through the VPN network.
- */
- public void addUidRanges(UidRange[] ranges) {
- queueOrSendMessage(EVENT_UID_RANGES_ADDED, ranges);
- }
-
- /**
- * Called by the VPN code when it wants to remove ranges of UIDs from being routed
- * through the VPN network.
- */
- public void removeUidRanges(UidRange[] ranges) {
- queueOrSendMessage(EVENT_UID_RANGES_REMOVED, ranges);
- }
-
- /**
* Called by the bearer to indicate this network was manually selected by the user.
* This should be called before the NetworkInfo is marked CONNECTED so that this
* Network can be given special treatment at that time. If {@code acceptUnvalidated} is
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7c897de..c94ae93 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -20,6 +20,7 @@
import android.net.ConnectivityManager.NetworkCallback;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
@@ -28,6 +29,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.Set;
import java.util.StringJoiner;
/**
@@ -46,6 +48,7 @@
*/
public final class NetworkCapabilities implements Parcelable {
private static final String TAG = "NetworkCapabilities";
+ private static final int INVALID_UID = -1;
/**
* @hide
@@ -63,6 +66,8 @@
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
mSignalStrength = nc.mSignalStrength;
+ mUids = nc.mUids;
+ mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
}
}
@@ -76,6 +81,8 @@
mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
mNetworkSpecifier = null;
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
+ mUids = null;
+ mEstablishingVpnAppUid = INVALID_UID;
}
/**
@@ -107,6 +114,8 @@
NET_CAPABILITY_CAPTIVE_PORTAL,
NET_CAPABILITY_NOT_ROAMING,
NET_CAPABILITY_FOREGROUND,
+ NET_CAPABILITY_NOT_CONGESTED,
+ NET_CAPABILITY_NOT_SUSPENDED,
})
public @interface NetCapability { }
@@ -230,12 +239,32 @@
/**
* Indicates that this network is available for use by apps, and not a network that is being
* kept up in the background to facilitate fast network switching.
- * @hide
*/
public static final int NET_CAPABILITY_FOREGROUND = 19;
+ /**
+ * Indicates that this network is not congested.
+ * <p>
+ * When a network is congested, the device should defer network traffic that
+ * can be done at a later time without breaking developer contracts.
+ * @hide
+ */
+ public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
+
+ /**
+ * Indicates that this network is not currently suspended.
+ * <p>
+ * When a network is suspended, the network's IP addresses and any connections
+ * established on the network remain valid, but the network is temporarily unable
+ * to transfer data. This can happen, for example, if a cellular network experiences
+ * a temporary loss of signal, such as when driving through a tunnel, etc.
+ * A network with this capability is not suspended, so is expected to be able to
+ * transfer data.
+ */
+ public static final int NET_CAPABILITY_NOT_SUSPENDED = 21;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_SUSPENDED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -244,11 +273,13 @@
private static final long MUTABLE_CAPABILITIES =
// TRUSTED can change when user explicitly connects to an untrusted network in Settings.
// http://b/18206275
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_VALIDATED) |
- (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
- (1 << NET_CAPABILITY_NOT_ROAMING) |
- (1 << NET_CAPABILITY_FOREGROUND);
+ (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_VALIDATED)
+ | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+ | (1 << NET_CAPABILITY_NOT_ROAMING)
+ | (1 << NET_CAPABILITY_FOREGROUND)
+ | (1 << NET_CAPABILITY_NOT_CONGESTED)
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -386,12 +417,9 @@
* @hide
*/
public String describeFirstNonRequestableCapability() {
- if (hasCapability(NET_CAPABILITY_VALIDATED)) return "NET_CAPABILITY_VALIDATED";
- if (hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return "NET_CAPABILITY_CAPTIVE_PORTAL";
- if (hasCapability(NET_CAPABILITY_FOREGROUND)) return "NET_CAPABILITY_FOREGROUND";
- // This cannot happen unless the preceding checks are incomplete.
- if ((mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES) != 0) {
- return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities);
+ final long nonRequestable = (mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES);
+ if (nonRequestable != 0) {
+ return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]);
}
if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
if (hasSignalStrength()) return "signalStrength";
@@ -610,6 +638,29 @@
}
/**
+ * UID of the app that manages this network, or INVALID_UID if none/unknown.
+ *
+ * This field keeps track of the UID of the app that created this network and is in charge
+ * of managing it. In the practice, it is used to store the UID of VPN apps so it is named
+ * accordingly, but it may be renamed if other mechanisms are offered for third party apps
+ * to create networks.
+ *
+ * Because this field is only used in the services side (and to avoid apps being able to
+ * set this to whatever they want), this field is not parcelled and will not be conserved
+ * across the IPC boundary.
+ * @hide
+ */
+ private int mEstablishingVpnAppUid = INVALID_UID;
+
+ /**
+ * Set the UID of the managing app.
+ * @hide
+ */
+ public void setEstablishingVpnAppUid(final int uid) {
+ mEstablishingVpnAppUid = uid;
+ }
+
+ /**
* Value indicating that link bandwidth is unspecified.
* @hide
*/
@@ -828,6 +879,174 @@
}
/**
+ * List of UIDs this network applies to. No restriction if null.
+ * <p>
+ * This is typically (and at this time, only) used by VPN. This network is only available to
+ * the UIDs in this list, and it is their default network. Apps in this list that wish to
+ * bypass the VPN can do so iff the VPN app allows them to or if they are privileged. If this
+ * member is null, then the network is not restricted by app UID. If it's an empty list, then
+ * it means nobody can use it.
+ * As a special exception, the app managing this network (as identified by its UID stored in
+ * mEstablishingVpnAppUid) can always see this network. This is embodied by a special check in
+ * satisfiedByUids. That still does not mean the network necessarily <strong>applies</strong>
+ * to the app that manages it as determined by #appliesToUid.
+ * <p>
+ * Please note that in principle a single app can be associated with multiple UIDs because
+ * each app will have a different UID when it's run as a different (macro-)user. A single
+ * macro user can only have a single active VPN app at any given time however.
+ * <p>
+ * Also please be aware this class does not try to enforce any normalization on this. Callers
+ * can only alter the UIDs by setting them wholesale : this class does not provide any utility
+ * to add or remove individual UIDs or ranges. If callers have any normalization needs on
+ * their own (like requiring sortedness or no overlap) they need to enforce it
+ * themselves. Some of the internal methods also assume this is normalized as in no adjacent
+ * or overlapping ranges are present.
+ *
+ * @hide
+ */
+ private ArraySet<UidRange> mUids = null;
+
+ /**
+ * Convenience method to set the UIDs this network applies to to a single UID.
+ * @hide
+ */
+ public NetworkCapabilities setSingleUid(int uid) {
+ final ArraySet<UidRange> identity = new ArraySet<>(1);
+ identity.add(new UidRange(uid, uid));
+ setUids(identity);
+ return this;
+ }
+
+ /**
+ * Set the list of UIDs this network applies to.
+ * This makes a copy of the set so that callers can't modify it after the call.
+ * @hide
+ */
+ public NetworkCapabilities setUids(Set<UidRange> uids) {
+ if (null == uids) {
+ mUids = null;
+ } else {
+ mUids = new ArraySet<>(uids);
+ }
+ return this;
+ }
+
+ /**
+ * Get the list of UIDs this network applies to.
+ * This returns a copy of the set so that callers can't modify the original object.
+ * @hide
+ */
+ public Set<UidRange> getUids() {
+ return null == mUids ? null : new ArraySet<>(mUids);
+ }
+
+ /**
+ * Test whether this network applies to this UID.
+ * @hide
+ */
+ public boolean appliesToUid(int uid) {
+ if (null == mUids) return true;
+ for (UidRange range : mUids) {
+ if (range.contains(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests if the set of UIDs that this network applies to is the same of the passed set of UIDs.
+ * <p>
+ * This test only checks whether equal range objects are in both sets. It will
+ * return false if the ranges are not exactly the same, even if the covered UIDs
+ * are for an equivalent result.
+ * <p>
+ * Note that this method is not very optimized, which is fine as long as it's not used very
+ * often.
+ * <p>
+ * nc is assumed nonnull.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean equalsUids(NetworkCapabilities nc) {
+ Set<UidRange> comparedUids = nc.mUids;
+ if (null == comparedUids) return null == mUids;
+ if (null == mUids) return false;
+ // Make a copy so it can be mutated to check that all ranges in mUids
+ // also are in uids.
+ final Set<UidRange> uids = new ArraySet<>(mUids);
+ for (UidRange range : comparedUids) {
+ if (!uids.contains(range)) {
+ return false;
+ }
+ uids.remove(range);
+ }
+ return uids.isEmpty();
+ }
+
+ /**
+ * Test whether the passed NetworkCapabilities satisfies the UIDs this capabilities require.
+ *
+ * This method is called on the NetworkCapabilities embedded in a request with the
+ * capabilities of an available network. It checks whether all the UIDs from this listen
+ * (representing the UIDs that must have access to the network) are satisfied by the UIDs
+ * in the passed nc (representing the UIDs that this network is available to).
+ * <p>
+ * As a special exception, the UID that created the passed network (as represented by its
+ * mEstablishingVpnAppUid field) always satisfies a NetworkRequest requiring it (of LISTEN
+ * or REQUEST types alike), even if the network does not apply to it. That is so a VPN app
+ * can see its own network when it listens for it.
+ * <p>
+ * nc is assumed nonnull. Else, NPE.
+ * @see #appliesToUid
+ * @hide
+ */
+ public boolean satisfiedByUids(NetworkCapabilities nc) {
+ if (null == nc.mUids) return true; // The network satisfies everything.
+ if (null == mUids) return false; // Not everything allowed but requires everything
+ for (UidRange requiredRange : mUids) {
+ if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true;
+ if (!nc.appliesToUidRange(requiredRange)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether this network applies to the passed ranges.
+ * This assumes that to apply, the passed range has to be entirely contained
+ * within one of the ranges this network applies to. If the ranges are not normalized,
+ * this method may return false even though all required UIDs are covered because no
+ * single range contained them all.
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean appliesToUidRange(UidRange requiredRange) {
+ if (null == mUids) return true;
+ for (UidRange uidRange : mUids) {
+ if (uidRange.containsRange(requiredRange)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Combine the UIDs this network currently applies to with the UIDs the passed
+ * NetworkCapabilities apply to.
+ * nc is assumed nonnull.
+ */
+ private void combineUids(NetworkCapabilities nc) {
+ if (null == nc.mUids || null == mUids) {
+ mUids = null;
+ return;
+ }
+ mUids.addAll(nc.mUids);
+ }
+
+ /**
* Combine a set of Capabilities to this one. Useful for coming up with the complete set
* @hide
*/
@@ -837,6 +1056,7 @@
combineLinkBandwidths(nc);
combineSpecifiers(nc);
combineSignalStrength(nc);
+ combineUids(nc);
}
/**
@@ -849,12 +1069,13 @@
* @hide
*/
private boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
- return (nc != null &&
- satisfiedByNetCapabilities(nc, onlyImmutable) &&
- satisfiedByTransportTypes(nc) &&
- (onlyImmutable || satisfiedByLinkBandwidths(nc)) &&
- satisfiedBySpecifier(nc) &&
- (onlyImmutable || satisfiedBySignalStrength(nc)));
+ return (nc != null
+ && satisfiedByNetCapabilities(nc, onlyImmutable)
+ && satisfiedByTransportTypes(nc)
+ && (onlyImmutable || satisfiedByLinkBandwidths(nc))
+ && satisfiedBySpecifier(nc)
+ && (onlyImmutable || satisfiedBySignalStrength(nc))
+ && (onlyImmutable || satisfiedByUids(nc)));
}
/**
@@ -937,24 +1158,26 @@
@Override
public boolean equals(Object obj) {
if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
- NetworkCapabilities that = (NetworkCapabilities)obj;
- return (equalsNetCapabilities(that) &&
- equalsTransportTypes(that) &&
- equalsLinkBandwidths(that) &&
- equalsSignalStrength(that) &&
- equalsSpecifier(that));
+ NetworkCapabilities that = (NetworkCapabilities) obj;
+ return (equalsNetCapabilities(that)
+ && equalsTransportTypes(that)
+ && equalsLinkBandwidths(that)
+ && equalsSignalStrength(that)
+ && equalsSpecifier(that)
+ && equalsUids(that));
}
@Override
public int hashCode() {
- return ((int)(mNetworkCapabilities & 0xFFFFFFFF) +
- ((int)(mNetworkCapabilities >> 32) * 3) +
- ((int)(mTransportTypes & 0xFFFFFFFF) * 5) +
- ((int)(mTransportTypes >> 32) * 7) +
- (mLinkUpBandwidthKbps * 11) +
- (mLinkDownBandwidthKbps * 13) +
- Objects.hashCode(mNetworkSpecifier) * 17 +
- (mSignalStrength * 19));
+ return ((int) (mNetworkCapabilities & 0xFFFFFFFF)
+ + ((int) (mNetworkCapabilities >> 32) * 3)
+ + ((int) (mTransportTypes & 0xFFFFFFFF) * 5)
+ + ((int) (mTransportTypes >> 32) * 7)
+ + (mLinkUpBandwidthKbps * 11)
+ + (mLinkDownBandwidthKbps * 13)
+ + Objects.hashCode(mNetworkSpecifier) * 17
+ + (mSignalStrength * 19)
+ + Objects.hashCode(mUids) * 23);
}
@Override
@@ -969,6 +1192,7 @@
dest.writeInt(mLinkDownBandwidthKbps);
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeInt(mSignalStrength);
+ dest.writeArraySet(mUids);
}
public static final Creator<NetworkCapabilities> CREATOR =
@@ -983,6 +1207,8 @@
netCap.mLinkDownBandwidthKbps = in.readInt();
netCap.mNetworkSpecifier = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
+ netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
+ null /* ClassLoader, null for default */);
return netCap;
}
@Override
@@ -1015,7 +1241,12 @@
String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : "");
- return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]";
+ String uids = (null != mUids ? " Uids: <" + mUids + ">" : "");
+
+ String establishingAppUid = " EstablishingAppUid: " + mEstablishingVpnAppUid;
+
+ return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength
+ + uids + establishingAppUid + "]";
}
/**
@@ -1056,6 +1287,8 @@
case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
+ case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
+ case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index d3b3599..ce2de85 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -58,21 +58,24 @@
final String mNetworkId;
final boolean mRoaming;
final boolean mMetered;
+ final boolean mDefaultNetwork;
public NetworkIdentity(
int type, int subType, String subscriberId, String networkId, boolean roaming,
- boolean metered) {
+ boolean metered, boolean defaultNetwork) {
mType = type;
mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType;
mSubscriberId = subscriberId;
mNetworkId = networkId;
mRoaming = roaming;
mMetered = metered;
+ mDefaultNetwork = defaultNetwork;
}
@Override
public int hashCode() {
- return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered);
+ return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered,
+ mDefaultNetwork);
}
@Override
@@ -82,7 +85,8 @@
return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
&& Objects.equals(mSubscriberId, ident.mSubscriberId)
&& Objects.equals(mNetworkId, ident.mNetworkId)
- && mMetered == ident.mMetered;
+ && mMetered == ident.mMetered
+ && mDefaultNetwork == ident.mDefaultNetwork;
}
return false;
}
@@ -109,6 +113,7 @@
builder.append(", ROAMING");
}
builder.append(", metered=").append(mMetered);
+ builder.append(", defaultNetwork=").append(mDefaultNetwork);
return builder.append("}").toString();
}
@@ -125,6 +130,7 @@
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
proto.write(NetworkIdentityProto.METERED, mMetered);
+ proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
proto.end(start);
}
@@ -153,6 +159,10 @@
return mMetered;
}
+ public boolean getDefaultNetwork() {
+ return mDefaultNetwork;
+ }
+
/**
* Scrub given IMSI on production builds.
*/
@@ -183,7 +193,8 @@
* Build a {@link NetworkIdentity} from the given {@link NetworkState},
* assuming that any mobile networks are using the current IMSI.
*/
- public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
+ public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
+ boolean defaultNetwork) {
final int type = state.networkInfo.getType();
final int subType = state.networkInfo.getSubtype();
@@ -216,7 +227,8 @@
}
}
- return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered);
+ return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+ defaultNetwork);
}
@Override
@@ -237,6 +249,9 @@
if (res == 0) {
res = Boolean.compare(mMetered, another.mMetered);
}
+ if (res == 0) {
+ res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
+ }
return res;
}
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 81c49a3..763338f 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -114,6 +114,9 @@
*/
public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE";
+ public static final int OVERRIDE_UNMETERED = 1 << 0;
+ public static final int OVERRIDE_CONGESTED = 1 << 1;
+
private final Context mContext;
private INetworkPolicyManager mService;
@@ -348,4 +351,13 @@
public static String resolveNetworkId(String ssid) {
return WifiInfo.removeDoubleQuotes(ssid);
}
+
+ /** {@hide} */
+ public static class Listener extends INetworkPolicyListener.Stub {
+ @Override public void onUidRulesChanged(int uid, int uidRules) { }
+ @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { }
+ @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
+ @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
+ @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue) { }
+ }
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 171adc0..01b2b39 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -82,6 +82,13 @@
/** {@link #roaming} value where roaming data is accounted. */
public static final int ROAMING_YES = 1;
+ /** {@link #onDefaultNetwork} value to account for all default network states. */
+ public static final int DEFAULT_NETWORK_ALL = -1;
+ /** {@link #onDefaultNetwork} value to account for usage while not the default network. */
+ public static final int DEFAULT_NETWORK_NO = 0;
+ /** {@link #onDefaultNetwork} value to account for usage while the default network. */
+ public static final int DEFAULT_NETWORK_YES = 1;
+
/** Denotes a request for stats at the interface level. */
public static final int STATS_PER_IFACE = 0;
/** Denotes a request for stats at the interface and UID level. */
@@ -102,6 +109,7 @@
private int[] tag;
private int[] metered;
private int[] roaming;
+ private int[] defaultNetwork;
private long[] rxBytes;
private long[] rxPackets;
private long[] txBytes;
@@ -125,6 +133,12 @@
* getSummary().
*/
public int roaming;
+ /**
+ * Note that this is only populated w/ the default value when read from /proc or written
+ * to disk. We merge in the correct value when reporting this value to clients of
+ * getSummary().
+ */
+ public int defaultNetwork;
public long rxBytes;
public long rxPackets;
public long txBytes;
@@ -142,18 +156,20 @@
public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) {
- this(iface, uid, set, tag, METERED_NO, ROAMING_NO, rxBytes, rxPackets, txBytes,
- txPackets, operations);
+ this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ rxBytes, rxPackets, txBytes, txPackets, operations);
}
public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
- long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+ int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
+ long operations) {
this.iface = iface;
this.uid = uid;
this.set = set;
this.tag = tag;
this.metered = metered;
this.roaming = roaming;
+ this.defaultNetwork = defaultNetwork;
this.rxBytes = rxBytes;
this.rxPackets = rxPackets;
this.txBytes = txBytes;
@@ -187,6 +203,7 @@
builder.append(" tag=").append(tagToString(tag));
builder.append(" metered=").append(meteredToString(metered));
builder.append(" roaming=").append(roamingToString(roaming));
+ builder.append(" defaultNetwork=").append(defaultNetworkToString(defaultNetwork));
builder.append(" rxBytes=").append(rxBytes);
builder.append(" rxPackets=").append(rxPackets);
builder.append(" txBytes=").append(txBytes);
@@ -200,7 +217,8 @@
if (o instanceof Entry) {
final Entry e = (Entry) o;
return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
- && roaming == e.roaming && rxBytes == e.rxBytes && rxPackets == e.rxPackets
+ && roaming == e.roaming && defaultNetwork == e.defaultNetwork
+ && rxBytes == e.rxBytes && rxPackets == e.rxPackets
&& txBytes == e.txBytes && txPackets == e.txPackets
&& operations == e.operations && iface.equals(e.iface);
}
@@ -209,7 +227,7 @@
@Override
public int hashCode() {
- return Objects.hash(uid, set, tag, metered, roaming, iface);
+ return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface);
}
}
@@ -224,6 +242,7 @@
this.tag = new int[initialSize];
this.metered = new int[initialSize];
this.roaming = new int[initialSize];
+ this.defaultNetwork = new int[initialSize];
this.rxBytes = new long[initialSize];
this.rxPackets = new long[initialSize];
this.txBytes = new long[initialSize];
@@ -238,6 +257,7 @@
this.tag = EmptyArray.INT;
this.metered = EmptyArray.INT;
this.roaming = EmptyArray.INT;
+ this.defaultNetwork = EmptyArray.INT;
this.rxBytes = EmptyArray.LONG;
this.rxPackets = EmptyArray.LONG;
this.txBytes = EmptyArray.LONG;
@@ -256,6 +276,7 @@
tag = parcel.createIntArray();
metered = parcel.createIntArray();
roaming = parcel.createIntArray();
+ defaultNetwork = parcel.createIntArray();
rxBytes = parcel.createLongArray();
rxPackets = parcel.createLongArray();
txBytes = parcel.createLongArray();
@@ -274,6 +295,7 @@
dest.writeIntArray(tag);
dest.writeIntArray(metered);
dest.writeIntArray(roaming);
+ dest.writeIntArray(defaultNetwork);
dest.writeLongArray(rxBytes);
dest.writeLongArray(rxPackets);
dest.writeLongArray(txBytes);
@@ -308,10 +330,11 @@
@VisibleForTesting
public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming,
- long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+ int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
+ long operations) {
return addValues(new Entry(
- iface, uid, set, tag, metered, roaming, rxBytes, rxPackets, txBytes, txPackets,
- operations));
+ iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
+ txBytes, txPackets, operations));
}
/**
@@ -327,6 +350,7 @@
tag = Arrays.copyOf(tag, newLength);
metered = Arrays.copyOf(metered, newLength);
roaming = Arrays.copyOf(roaming, newLength);
+ defaultNetwork = Arrays.copyOf(defaultNetwork, newLength);
rxBytes = Arrays.copyOf(rxBytes, newLength);
rxPackets = Arrays.copyOf(rxPackets, newLength);
txBytes = Arrays.copyOf(txBytes, newLength);
@@ -341,6 +365,7 @@
tag[size] = entry.tag;
metered[size] = entry.metered;
roaming[size] = entry.roaming;
+ defaultNetwork[size] = entry.defaultNetwork;
rxBytes[size] = entry.rxBytes;
rxPackets[size] = entry.rxPackets;
txBytes[size] = entry.txBytes;
@@ -362,6 +387,7 @@
entry.tag = tag[i];
entry.metered = metered[i];
entry.roaming = roaming[i];
+ entry.defaultNetwork = defaultNetwork[i];
entry.rxBytes = rxBytes[i];
entry.rxPackets = rxPackets[i];
entry.txBytes = txBytes[i];
@@ -416,7 +442,7 @@
*/
public NetworkStats combineValues(Entry entry) {
final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered,
- entry.roaming);
+ entry.roaming, entry.defaultNetwork);
if (i == -1) {
// only create new entry when positive contribution
addValues(entry);
@@ -444,10 +470,12 @@
/**
* Find first stats index that matches the requested parameters.
*/
- public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming) {
+ public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming,
+ int defaultNetwork) {
for (int i = 0; i < size; i++) {
if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
&& metered == this.metered[i] && roaming == this.roaming[i]
+ && defaultNetwork == this.defaultNetwork[i]
&& Objects.equals(iface, this.iface[i])) {
return i;
}
@@ -461,7 +489,7 @@
*/
@VisibleForTesting
public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming,
- int hintIndex) {
+ int defaultNetwork, int hintIndex) {
for (int offset = 0; offset < size; offset++) {
final int halfOffset = offset / 2;
@@ -475,6 +503,7 @@
if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
&& metered == this.metered[i] && roaming == this.roaming[i]
+ && defaultNetwork == this.defaultNetwork[i]
&& Objects.equals(iface, this.iface[i])) {
return i;
}
@@ -489,7 +518,8 @@
*/
public void spliceOperationsFrom(NetworkStats stats) {
for (int i = 0; i < size; i++) {
- final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i]);
+ final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i],
+ defaultNetwork[i]);
if (j == -1) {
operations[i] = 0;
} else {
@@ -581,6 +611,7 @@
entry.tag = TAG_NONE;
entry.metered = METERED_ALL;
entry.roaming = ROAMING_ALL;
+ entry.defaultNetwork = DEFAULT_NETWORK_ALL;
entry.rxBytes = 0;
entry.rxPackets = 0;
entry.txBytes = 0;
@@ -677,6 +708,7 @@
entry.tag = left.tag[i];
entry.metered = left.metered[i];
entry.roaming = left.roaming[i];
+ entry.defaultNetwork = left.defaultNetwork[i];
entry.rxBytes = left.rxBytes[i];
entry.rxPackets = left.rxPackets[i];
entry.txBytes = left.txBytes[i];
@@ -685,7 +717,7 @@
// find remote row that matches, and subtract
final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
- entry.metered, entry.roaming, i);
+ entry.metered, entry.roaming, entry.defaultNetwork, i);
if (j != -1) {
// Found matching row, subtract remote value.
entry.rxBytes -= right.rxBytes[j];
@@ -725,6 +757,7 @@
entry.tag = TAG_NONE;
entry.metered = METERED_ALL;
entry.roaming = ROAMING_ALL;
+ entry.defaultNetwork = DEFAULT_NETWORK_ALL;
entry.operations = 0L;
for (int i = 0; i < size; i++) {
@@ -755,6 +788,7 @@
entry.tag = TAG_NONE;
entry.metered = METERED_ALL;
entry.roaming = ROAMING_ALL;
+ entry.defaultNetwork = DEFAULT_NETWORK_ALL;
for (int i = 0; i < size; i++) {
// skip specific tags, since already counted in TAG_NONE
@@ -802,6 +836,7 @@
pw.print(" tag="); pw.print(tagToString(tag[i]));
pw.print(" metered="); pw.print(meteredToString(metered[i]));
pw.print(" roaming="); pw.print(roamingToString(roaming[i]));
+ pw.print(" defaultNetwork="); pw.print(defaultNetworkToString(defaultNetwork[i]));
pw.print(" rxBytes="); pw.print(rxBytes[i]);
pw.print(" rxPackets="); pw.print(rxPackets[i]);
pw.print(" txBytes="); pw.print(txBytes[i]);
@@ -900,6 +935,22 @@
}
}
+ /**
+ * Return text description of {@link #defaultNetwork} value.
+ */
+ public static String defaultNetworkToString(int defaultNetwork) {
+ switch (defaultNetwork) {
+ case DEFAULT_NETWORK_ALL:
+ return "ALL";
+ case DEFAULT_NETWORK_NO:
+ return "NO";
+ case DEFAULT_NETWORK_YES:
+ return "YES";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
@@ -1055,6 +1106,7 @@
tmpEntry.set = set[i];
tmpEntry.metered = metered[i];
tmpEntry.roaming = roaming[i];
+ tmpEntry.defaultNetwork = defaultNetwork[i];
combineValues(tmpEntry);
if (tag[i] == TAG_NONE) {
moved.add(tmpEntry);
@@ -1075,6 +1127,7 @@
moved.iface = underlyingIface;
moved.metered = METERED_ALL;
moved.roaming = ROAMING_ALL;
+ moved.defaultNetwork = DEFAULT_NETWORK_ALL;
combineValues(moved);
// Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
@@ -1085,13 +1138,13 @@
// roaming data after applying these adjustments, by checking the NetworkIdentity of the
// underlying iface.
int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO);
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
if (idxVpnBackground != -1) {
tunSubtract(idxVpnBackground, this, moved);
}
int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
- METERED_NO, ROAMING_NO);
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO);
if (idxVpnForeground != -1) {
tunSubtract(idxVpnForeground, this, moved);
}
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index b307c5d..8efd39a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -24,6 +24,15 @@
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
import static android.net.wifi.WifiInfo.removeDoubleQuotes;
import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
@@ -191,16 +200,30 @@
private final String mNetworkId;
+ // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
+ private final int mMetered;
+ private final int mRoaming;
+ private final int mDefaultNetwork;
+
public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
}
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId) {
+ this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL);
+ }
+
+ public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
+ String networkId, int metered, int roaming, int defaultNetwork) {
mMatchRule = matchRule;
mSubscriberId = subscriberId;
mMatchSubscriberIds = matchSubscriberIds;
mNetworkId = networkId;
+ mMetered = metered;
+ mRoaming = roaming;
+ mDefaultNetwork = defaultNetwork;
if (!isKnownMatchRule(matchRule)) {
Log.e(TAG, "Unknown network template rule " + matchRule
@@ -213,6 +236,9 @@
mSubscriberId = in.readString();
mMatchSubscriberIds = in.createStringArray();
mNetworkId = in.readString();
+ mMetered = in.readInt();
+ mRoaming = in.readInt();
+ mDefaultNetwork = in.readInt();
}
@Override
@@ -221,6 +247,9 @@
dest.writeString(mSubscriberId);
dest.writeStringArray(mMatchSubscriberIds);
dest.writeString(mNetworkId);
+ dest.writeInt(mMetered);
+ dest.writeInt(mRoaming);
+ dest.writeInt(mDefaultNetwork);
}
@Override
@@ -243,12 +272,23 @@
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
}
+ if (mMetered != METERED_ALL) {
+ builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
+ }
+ if (mRoaming != ROAMING_ALL) {
+ builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming));
+ }
+ if (mDefaultNetwork != DEFAULT_NETWORK_ALL) {
+ builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
+ mDefaultNetwork));
+ }
return builder.toString();
}
@Override
public int hashCode() {
- return Objects.hash(mMatchRule, mSubscriberId, mNetworkId);
+ return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
+ mDefaultNetwork);
}
@Override
@@ -257,7 +297,10 @@
final NetworkTemplate other = (NetworkTemplate) obj;
return mMatchRule == other.mMatchRule
&& Objects.equals(mSubscriberId, other.mSubscriberId)
- && Objects.equals(mNetworkId, other.mNetworkId);
+ && Objects.equals(mNetworkId, other.mNetworkId)
+ && mMetered == other.mMetered
+ && mRoaming == other.mRoaming
+ && mDefaultNetwork == other.mDefaultNetwork;
}
return false;
}
@@ -300,6 +343,10 @@
* Test if given {@link NetworkIdentity} matches this template.
*/
public boolean matches(NetworkIdentity ident) {
+ if (!matchesMetered(ident)) return false;
+ if (!matchesRoaming(ident)) return false;
+ if (!matchesDefaultNetwork(ident)) return false;
+
switch (mMatchRule) {
case MATCH_MOBILE_ALL:
return matchesMobile(ident);
@@ -326,6 +373,24 @@
}
}
+ private boolean matchesMetered(NetworkIdentity ident) {
+ return (mMetered == METERED_ALL)
+ || (mMetered == METERED_YES && ident.mMetered)
+ || (mMetered == METERED_NO && !ident.mMetered);
+ }
+
+ private boolean matchesRoaming(NetworkIdentity ident) {
+ return (mRoaming == ROAMING_ALL)
+ || (mRoaming == ROAMING_YES && ident.mRoaming)
+ || (mRoaming == ROAMING_NO && !ident.mRoaming);
+ }
+
+ private boolean matchesDefaultNetwork(NetworkIdentity ident) {
+ return (mDefaultNetwork == DEFAULT_NETWORK_ALL)
+ || (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork)
+ || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
+ }
+
public boolean matchesSubscriberId(String subscriberId) {
return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
}
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 6758d95..3cd37bf 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,5 +1,6 @@
+set noparent
+
ek@google.com
-hugobenichi@google.com
jsharkey@android.com
jchalard@google.com
lorenzo@google.com
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 196a3bc..fa4624e 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -433,6 +433,10 @@
}
}
+ private static long addIfSupported(long stat) {
+ return (stat == UNSUPPORTED) ? 0 : stat;
+ }
+
/**
* Return number of packets transmitted across mobile networks since device
* boot. Counts packets across all mobile network interfaces, and always
@@ -445,7 +449,7 @@
public static long getMobileTxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxPackets(iface);
+ total += addIfSupported(getTxPackets(iface));
}
return total;
}
@@ -462,7 +466,7 @@
public static long getMobileRxPackets() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxPackets(iface);
+ total += addIfSupported(getRxPackets(iface));
}
return total;
}
@@ -479,7 +483,7 @@
public static long getMobileTxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getTxBytes(iface);
+ total += addIfSupported(getTxBytes(iface));
}
return total;
}
@@ -496,7 +500,7 @@
public static long getMobileRxBytes() {
long total = 0;
for (String iface : getMobileIfaces()) {
- total += getRxBytes(iface);
+ total += addIfSupported(getRxBytes(iface));
}
return total;
}
@@ -511,9 +515,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
@@ -528,9 +530,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- if (stat != UNSUPPORTED) {
- total += stat;
- }
+ total += addIfSupported(stat);
}
return total;
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index d5377c7..1bb4adc 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.Nullable;
import android.content.Intent;
import android.os.Environment;
import android.os.Parcel;
@@ -23,6 +24,8 @@
import android.os.StrictMode;
import android.util.Log;
+import libcore.net.UriCodec;
+
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -38,8 +41,6 @@
import java.util.RandomAccess;
import java.util.Set;
-import libcore.net.UriCodec;
-
/**
* Immutable URI reference. A URI reference includes a URI and a fragment, the
* component of the URI following a '#'. Builds and parses URI references
@@ -174,6 +175,7 @@
*
* @return the scheme or null if this is a relative URI
*/
+ @Nullable
public abstract String getScheme();
/**
@@ -208,6 +210,7 @@
*
* @return the authority for this URI or null if not present
*/
+ @Nullable
public abstract String getAuthority();
/**
@@ -219,6 +222,7 @@
*
* @return the authority for this URI or null if not present
*/
+ @Nullable
public abstract String getEncodedAuthority();
/**
@@ -228,6 +232,7 @@
*
* @return the user info for this URI or null if not present
*/
+ @Nullable
public abstract String getUserInfo();
/**
@@ -237,6 +242,7 @@
*
* @return the user info for this URI or null if not present
*/
+ @Nullable
public abstract String getEncodedUserInfo();
/**
@@ -246,6 +252,7 @@
*
* @return the host for this URI or null if not present
*/
+ @Nullable
public abstract String getHost();
/**
@@ -262,6 +269,7 @@
* @return the decoded path, or null if this is not a hierarchical URI
* (like "mailto:nobody@google.com") or the URI is invalid
*/
+ @Nullable
public abstract String getPath();
/**
@@ -270,6 +278,7 @@
* @return the encoded path, or null if this is not a hierarchical URI
* (like "mailto:nobody@google.com") or the URI is invalid
*/
+ @Nullable
public abstract String getEncodedPath();
/**
@@ -280,6 +289,7 @@
*
* @return the decoded query or null if there isn't one
*/
+ @Nullable
public abstract String getQuery();
/**
@@ -290,6 +300,7 @@
*
* @return the encoded query or null if there isn't one
*/
+ @Nullable
public abstract String getEncodedQuery();
/**
@@ -297,6 +308,7 @@
*
* @return the decoded fragment or null if there isn't one
*/
+ @Nullable
public abstract String getFragment();
/**
@@ -304,6 +316,7 @@
*
* @return the encoded fragment or null if there isn't one
*/
+ @Nullable
public abstract String getEncodedFragment();
/**
@@ -318,6 +331,7 @@
*
* @return the decoded last segment or null if the path is empty
*/
+ @Nullable
public abstract String getLastPathSegment();
/**
@@ -1666,6 +1680,7 @@
* @throws NullPointerException if key is null
* @return the decoded value or null if no parameter is found
*/
+ @Nullable
public String getQueryParameter(String key) {
if (isOpaque()) {
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 2b662a0..66d92c4 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -96,6 +96,16 @@
}
}
+ /** Accumulate a single netd sock_diag poll result reported by netd. */
+ public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+ if (pendingSummary == null) {
+ pendingSummary = new Summary(netId, transports);
+ }
+ pendingSummary.tcpLossRate.count(lost, sent);
+ pendingSummary.roundTripTimeUs.count(rttUs);
+ pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
+ }
+
/** Represents running sums for dns and connect average error counts and average latencies. */
public static class Summary {
@@ -109,6 +119,13 @@
public final Metrics connectLatencies = new Metrics();
// Blocking and non blocking connect error rate measured in percentage points.
public final Metrics connectErrorRate = new Metrics();
+ // TCP socket packet loss stats collected from Netlink sock_diag.
+ public final Metrics tcpLossRate = new Metrics();
+ // TCP averaged microsecond round-trip-time stats collected from Netlink sock_diag.
+ public final Metrics roundTripTimeUs = new Metrics();
+ // TCP stats collected from Netlink sock_diag that averages millisecond per-socket
+ // differences between last packet sent timestamp and last ack received timestamp.
+ public final Metrics sentAckTimeDiffenceMs = new Metrics();
public Summary(int netId, long transports) {
this.netId = netId;
@@ -120,6 +137,7 @@
dnsErrorRate.merge(that.dnsErrorRate);
connectLatencies.merge(that.connectLatencies);
connectErrorRate.merge(that.connectErrorRate);
+ tcpLossRate.merge(that.tcpLossRate);
}
@Override
@@ -135,6 +153,10 @@
j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d",
(int) connectLatencies.average(), (int) connectLatencies.max,
100 * connectErrorRate.average(), connectErrorRate.count));
+ j.add(String.format("tcp avg_loss=%.1f%% total_sent=%d total_lost=%d",
+ 100 * tcpLossRate.average(), tcpLossRate.count, (int) tcpLossRate.sum));
+ j.add(String.format("tcp rtt=%dms", (int) (roundTripTimeUs.average() / 1000)));
+ j.add(String.format("tcp sent-ack_diff=%dms", (int) sentAckTimeDiffenceMs.average()));
return j.toString();
}
}
@@ -152,7 +174,11 @@
}
void count(double value) {
- count++;
+ count(value, 1);
+ }
+
+ void count(double value, int subcount) {
+ count += subcount;
sum += value;
max = Math.max(max, value);
}
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 7277ba3..bb36536 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -80,7 +80,7 @@
break;
}
- switch (ev.dstHwAddr.addressType()) {
+ switch (ev.dstHwAddr.getAddressType()) {
case MacAddress.TYPE_UNICAST:
l2UnicastCount++;
break;
diff --git a/services/net/java/android/net/util/IpUtils.java b/core/java/android/net/util/IpUtils.java
similarity index 100%
rename from services/net/java/android/net/util/IpUtils.java
rename to core/java/android/net/util/IpUtils.java
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index debef63..b02d48d 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -147,6 +147,19 @@
public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
/**
+ * Broadcast Action: Intent to notify an application that an transaction event has occurred
+ * on the Secure Element.
+ *
+ * <p>This intent will only be sent if the application has requested permission for
+ * {@link android.Manifest.permission#NFC_TRANSACTION_EVENT} and if the application has the
+ * necessary access to Secure Element which witnessed the particular event.
+ */
+ @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TRANSACTION_DETECTED =
+ "android.nfc.action.TRANSACTION_DETECTED";
+
+ /**
* Broadcast to only the activity that handles ACTION_TAG_DISCOVERED
* @hide
*/
@@ -197,6 +210,23 @@
*/
public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
+ /**
+ * Mandatory byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+ */
+ public static final String EXTRA_AID = "android.nfc.extra.AID";
+
+ /**
+ * Optional byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+ */
+ public static final String EXTRA_DATA = "android.nfc.extra.DATA";
+
+ /**
+ * Mandatory String extra field in {@link #ACTION_TRANSACTION_DETECTED}
+ * Indicates the Secure Element on which the transaction occurred.
+ * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
+ */
+ public static final String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+
public static final int STATE_OFF = 1;
public static final int STATE_TURNING_ON = 2;
public static final int STATE_ON = 3;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 1b707bd..336e1b4 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -35,6 +35,9 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -789,7 +792,7 @@
/**
* Return the total number of pairs in the map.
*/
- int size() {
+ private int size() {
int size = 0;
for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
if (a != null) {
@@ -800,6 +803,24 @@
}
/**
+ * Return the total number of pairs in the map containing values that have
+ * not been cleared. More expensive than the above size function.
+ */
+ private int unclearedSize() {
+ int size = 0;
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> ref : a) {
+ if (ref.get() != null) {
+ ++size;
+ }
+ }
+ }
+ }
+ return size;
+ }
+
+ /**
* Remove ith entry from the hash bucket indicated by hash.
*/
private void remove(int hash, int index) {
@@ -888,17 +909,71 @@
keyArray[size] = key;
}
if (size >= mWarnBucketSize) {
- final int total_size = size();
+ final int totalSize = size();
Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
- + " total = " + total_size);
+ + " total = " + totalSize);
mWarnBucketSize += WARN_INCREMENT;
- if (Build.IS_DEBUGGABLE && total_size > CRASH_AT_SIZE) {
- throw new AssertionError("Binder ProxyMap has too many entries. "
- + "BinderProxy leak?");
+ if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+ // Use the number of uncleared entries to determine whether we should
+ // really report a histogram and crash. We don't want to fundamentally
+ // change behavior for a debuggable process, so we GC only if we are
+ // about to crash.
+ final int totalUnclearedSize = unclearedSize();
+ if (totalUnclearedSize >= CRASH_AT_SIZE) {
+ dumpProxyInterfaceCounts();
+ Runtime.getRuntime().gc();
+ throw new AssertionError("Binder ProxyMap has too many entries: "
+ + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+ + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
+ } else if (totalSize > 3 * totalUnclearedSize / 2) {
+ Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
+ + (totalSize - totalUnclearedSize) + " of " + totalSize
+ + " are cleared");
+ }
}
}
}
+ /**
+ * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
+ */
+ private void dumpProxyInterfaceCounts() {
+ Map<String, Integer> counts = new HashMap<>();
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> weakRef : a) {
+ BinderProxy bp = weakRef.get();
+ String key;
+ if (bp == null) {
+ key = "<cleared weak-ref>";
+ } else {
+ try {
+ key = bp.getInterfaceDescriptor();
+ } catch (Throwable t) {
+ key = "<exception during getDescriptor>";
+ }
+ }
+ Integer i = counts.get(key);
+ if (i == null) {
+ counts.put(key, 1);
+ } else {
+ counts.put(key, i + 1);
+ }
+ }
+ }
+ }
+ Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
+ new Map.Entry[counts.size()]);
+ Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
+ -> b.getValue().compareTo(a.getValue()));
+ Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):");
+ int printLength = Math.min(10, sorted.length);
+ for (int i = 0; i < printLength; i++) {
+ Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
+ + sorted[i].getValue());
+ }
+ }
+
// Corresponding ArrayLists in the following two arrays always have the same size.
// They contain no empty entries. However WeakReferences in the values ArrayLists
// may have been cleared.
diff --git a/core/java/android/os/ChildZygoteProcess.java b/core/java/android/os/ChildZygoteProcess.java
new file mode 100644
index 0000000..337a3e2
--- /dev/null
+++ b/core/java/android/os/ChildZygoteProcess.java
@@ -0,0 +1,44 @@
+/*
+ * 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.os;
+
+import android.net.LocalSocketAddress;
+
+/**
+ * Represents a connection to a child-zygote process. A child-zygote is spawend from another
+ * zygote process using {@link startChildZygote()}.
+ *
+ * {@hide}
+ */
+public class ChildZygoteProcess extends ZygoteProcess {
+ /**
+ * The PID of the child zygote process.
+ */
+ private final int mPid;
+
+ ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) {
+ super(socketAddress, null);
+ mPid = pid;
+ }
+
+ /**
+ * Returns the PID of the child-zygote process.
+ */
+ public int getPid() {
+ return mPid;
+ }
+}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 2acf36f..c3894c4 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1136,7 +1136,7 @@
int intervalUs) {
VMDebug.startMethodTracing(fixTracePath(tracePath), bufferSize, 0, true, intervalUs);
}
-
+
/**
* Formats name of trace log file for method tracing.
*/
@@ -2352,22 +2352,28 @@
}
/**
- * Attach a library as a jvmti agent to the current runtime.
+ * Attach a library as a jvmti agent to the current runtime, with the given classloader
+ * determining the library search path.
+ * <p>
+ * Note: agents may only be attached to debuggable apps. Otherwise, this function will
+ * throw a SecurityException.
*
- * @param library library containing the agent
- * @param options options passed to the agent
+ * @param library the library containing the agent.
+ * @param options the options passed to the agent.
+ * @param classLoader the classloader determining the library search path.
*
- * @throws IOException If the agent could not be attached
+ * @throws IOException if the agent could not be attached.
+ * @throws SecurityException if the app is not debuggable.
*/
- public static void attachJvmtiAgent(@NonNull String library, @Nullable String options)
- throws IOException {
+ public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
+ @Nullable ClassLoader classLoader) throws IOException {
Preconditions.checkNotNull(library);
Preconditions.checkArgument(!library.contains("="));
if (options == null) {
- VMDebug.attachAgent(library);
+ VMDebug.attachAgent(library, classLoader);
} else {
- VMDebug.attachAgent(library + "=" + options);
+ VMDebug.attachAgent(library + "=" + options, classLoader);
}
}
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 5b0e5bbc..d8eae8c 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -291,7 +291,7 @@
}
/** {@hide} */
- public static File getReferenceProfile(String packageName) {
+ public static File getDataRefProfilesDePackageDirectory(String packageName) {
return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
}
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index a080c8d..4d7d931 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -86,6 +86,25 @@
}
/**
+ * Class which can be used to fetch an object out of a lambda. Fetching an object
+ * out of a local scope with HIDL is a common operation (although usually it can
+ * and should be avoided).
+ *
+ * @param <E> Inner object type.
+ */
+ public static final class Mutable<E> {
+ public E value;
+
+ public Mutable() {
+ value = null;
+ }
+
+ public Mutable(E value) {
+ this.value = value;
+ }
+ }
+
+ /**
* Similar to Arrays.deepHashCode, but also take care of lists.
*/
public static int deepHashCode(Object o) {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index a474b47..a5e1934 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -339,7 +339,8 @@
/**
* Configure name servers, search paths, and resolver parameters for the given network.
*/
- void setDnsConfigurationForNetwork(int netId, in String[] servers, String domains);
+ void setDnsConfigurationForNetwork(int netId, in String[] servers, in String[] domains,
+ in int[] params, boolean useTls, String tlsHostname);
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0874d93..5e6f5f5 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -143,7 +143,7 @@
* Defines the UID/GID for the WebView zygote process.
* @hide
*/
- public static final int WEBVIEW_ZYGOTE_UID = 1051;
+ public static final int WEBVIEW_ZYGOTE_UID = 1053;
/**
* Defines the UID used for resource tracking for OTA updates.
@@ -151,6 +151,12 @@
*/
public static final int OTA_UPDATE_UID = 1061;
+ /**
+ * Defines the UID/GID for the Secure Element service process.
+ * @hide
+ */
+ public static final int SE_UID = 1068;
+
/** {@hide} */
public static final int NOBODY_UID = 9999;
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 340f3fb..fb22194 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -76,8 +76,15 @@
/**
* @return a list of VNDK snapshots supported by the framework, as
* specified in framework manifest. For example,
- * [("25.0.5", ["libjpeg.so", "libbase.so"]),
- * ("25.1.3", ["libjpeg.so", "libbase.so"])]
+ * [("27", ["libjpeg.so", "libbase.so"]),
+ * ("28", ["libjpeg.so", "libbase.so"])]
*/
public static native Map<String, String[]> getVndkSnapshots();
+
+ /**
+ * @return target FCM version, a number specified in the device manifest
+ * indicating the FCM version that the device manifest implements. Null if
+ * device manifest doesn't specify this number (for legacy devices).
+ */
+ public static native Long getTargetFrameworkCompatibilityMatrixVersion();
}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 670f794..57418c8 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -33,6 +33,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.UUID;
/*package*/ class ZygoteStartFailedEx extends Exception {
ZygoteStartFailedEx(String s) {
@@ -61,18 +62,27 @@
/**
* The name of the socket used to communicate with the primary zygote.
*/
- private final String mSocket;
+ private final LocalSocketAddress mSocket;
/**
* The name of the secondary (alternate ABI) zygote socket.
*/
- private final String mSecondarySocket;
+ private final LocalSocketAddress mSecondarySocket;
public ZygoteProcess(String primarySocket, String secondarySocket) {
+ this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
+ new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
+ }
+
+ public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
mSocket = primarySocket;
mSecondarySocket = secondarySocket;
}
+ public LocalSocketAddress getPrimarySocketAddress() {
+ return mSocket;
+ }
+
/**
* State for communicating with the zygote process.
*/
@@ -92,14 +102,13 @@
this.abiList = abiList;
}
- public static ZygoteState connect(String socketAddress) throws IOException {
+ public static ZygoteState connect(LocalSocketAddress address) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
- zygoteSocket.connect(new LocalSocketAddress(socketAddress,
- LocalSocketAddress.Namespace.RESERVED));
+ zygoteSocket.connect(address);
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
@@ -115,8 +124,8 @@
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
- Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
- + abiListString);
+ Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
+ + address.getName() + " opened, supported ABIS: " + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
@@ -209,7 +218,8 @@
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
+ zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -325,6 +335,8 @@
* @param abi the ABI the process should use.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
+ * @param startChildZygote Start a sub-zygote. This creates a new zygote process
+ * that has its state cloned from this zygote process.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -340,6 +352,7 @@
String instructionSet,
String appDataDir,
String invokeWith,
+ boolean startChildZygote,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -396,6 +409,10 @@
argsForZygote.add(invokeWith);
}
+ if (startChildZygote) {
+ argsForZygote.add("--start-child-zygote");
+ }
+
argsForZygote.add(processClass);
if (extraArgs != null) {
@@ -410,6 +427,18 @@
}
/**
+ * Closes the connections to the zygote, if they exist.
+ */
+ public void close() {
+ if (primaryZygoteState != null) {
+ primaryZygoteState.close();
+ }
+ if (secondaryZygoteState != null) {
+ secondaryZygoteState.close();
+ }
+ }
+
+ /**
* Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
* and retry if the zygote is unresponsive. This method is a no-op if a connection is
* already open.
@@ -514,9 +543,19 @@
* @param socketName The name of the socket to connect to.
*/
public static void waitForConnectionToZygote(String socketName) {
+ final LocalSocketAddress address =
+ new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
+ waitForConnectionToZygote(address);
+ }
+
+ /**
+ * Try connecting to the Zygote over and over again until we hit a time-out.
+ * @param address The name of the socket to connect to.
+ */
+ public static void waitForConnectionToZygote(LocalSocketAddress address) {
for (int n = 20; n >= 0; n--) {
try {
- final ZygoteState zs = ZygoteState.connect(socketName);
+ final ZygoteState zs = ZygoteState.connect(address);
zs.close();
return;
} catch (IOException ioe) {
@@ -529,6 +568,38 @@
} catch (InterruptedException ie) {
}
}
- Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + socketName);
+ Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
+ }
+
+ /**
+ * Starts a new zygote process as a child of this zygote. This is used to create
+ * secondary zygotes that inherit data from the zygote that this object
+ * communicates with. This returns a new ZygoteProcess representing a connection
+ * to the newly created zygote. Throws an exception if the zygote cannot be started.
+ */
+ public ChildZygoteProcess startChildZygote(final String processClass,
+ final String niceName,
+ int uid, int gid, int[] gids,
+ int runtimeFlags,
+ String seInfo,
+ String abi,
+ String instructionSet) {
+ // Create an unguessable address in the global abstract namespace.
+ final LocalSocketAddress serverAddress = new LocalSocketAddress(
+ processClass + "/" + UUID.randomUUID().toString());
+
+ final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
+
+ Process.ProcessStartResult result;
+ try {
+ result = startViaZygote(processClass, niceName, uid, gid,
+ gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
+ abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
+ true /* startChildZygote */, extraArgs);
+ } catch (ZygoteStartFailedEx ex) {
+ throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
+ }
+
+ return new ChildZygoteProcess(serverAddress, result.pid);
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8533c7e..18f3a14 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1083,12 +1083,14 @@
/** {@hide} */
public static Pair<String, Long> getPrimaryStoragePathAndSize() {
return Pair.create(null,
- FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()));
+ FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()
+ + Environment.getRootDirectory().getTotalSpace()));
}
/** {@hide} */
public long getPrimaryStorageSize() {
- return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace());
+ return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()
+ + Environment.getRootDirectory().getTotalSpace());
}
/** @removed */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ca9544a..36dcca6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -65,6 +65,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.speech.tts.TextToSpeech;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.ArrayMap;
@@ -3664,6 +3665,17 @@
public static final Validator TTY_MODE_VALIDATOR = new InclusiveIntegerRangeValidator(0, 3);
/**
+ * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
+ * calls when supported by the device and carrier. Boolean value.
+ * 0 = OFF
+ * 1 = ON
+ */
+ public static final String RTT_CALLING_MODE = "rtt_calling_mode";
+
+ /** @hide */
+ public static final Validator RTT_CALLING_MODE_VALIDATOR = sBooleanValidator;
+
+ /**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
* boolean (1 or 0).
*/
@@ -3983,6 +3995,7 @@
DTMF_TONE_WHEN_DIALING,
DTMF_TONE_TYPE_WHEN_DIALING,
HEARING_AID,
+ RTT_CALLING_MODE,
TTY_MODE,
MASTER_MONO,
SOUND_EFFECTS_ENABLED,
@@ -4166,6 +4179,7 @@
VALIDATORS.put(DTMF_TONE_TYPE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR);
VALIDATORS.put(HEARING_AID, HEARING_AID_VALIDATOR);
VALIDATORS.put(TTY_MODE, TTY_MODE_VALIDATOR);
+ VALIDATORS.put(RTT_CALLING_MODE, RTT_CALLING_MODE_VALIDATOR);
VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, NOTIFICATION_LIGHT_PULSE_VALIDATOR);
VALIDATORS.put(POINTER_LOCATION, POINTER_LOCATION_VALIDATOR);
VALIDATORS.put(SHOW_TOUCHES, SHOW_TOUCHES_VALIDATOR);
@@ -7837,9 +7851,8 @@
*
* @see android.service.euicc.EuiccService
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
/**
@@ -8645,13 +8658,52 @@
public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
"recommended_network_evaluator_cache_expiry_ms";
- /**
+ /**
* Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
* connectivity.
* @hide
*/
- public static final String BLE_SCAN_ALWAYS_AVAILABLE =
- "ble_scan_always_enabled";
+ public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
+
+ /**
+ * The length in milliseconds of a BLE scan window in a low-power scan mode.
+ * @hide
+ */
+ public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
+
+ /**
+ * The length in milliseconds of a BLE scan window in a balanced scan mode.
+ * @hide
+ */
+ public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
+
+ /**
+ * The length in milliseconds of a BLE scan window in a low-latency scan mode.
+ * @hide
+ */
+ public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS =
+ "ble_scan_low_latency_window_ms";
+
+ /**
+ * The length in milliseconds of a BLE scan interval in a low-power scan mode.
+ * @hide
+ */
+ public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS =
+ "ble_scan_low_power_interval_ms";
+
+ /**
+ * The length in milliseconds of a BLE scan interval in a balanced scan mode.
+ * @hide
+ */
+ public static final String BLE_SCAN_BALANCED_INTERVAL_MS =
+ "ble_scan_balanced_interval_ms";
+
+ /**
+ * The length in milliseconds of a BLE scan interval in a low-latency scan mode.
+ * @hide
+ */
+ public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
+ "ble_scan_low_latency_interval_ms";
/**
* Used to save the Wifi_ON state prior to tethering.
@@ -10077,8 +10129,12 @@
* <p>
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED}
+ * instead.
*/
- public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+ @Deprecated
+ public static final String ENHANCED_4G_MODE_ENABLED =
+ SubscriptionManager.ENHANCED_4G_MODE_ENABLED;
/**
* Whether VT (Video Telephony over IMS) is enabled
@@ -10086,8 +10142,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead.
*/
- public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+ @Deprecated
+ public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED;
/**
* Whether WFC is enabled
@@ -10095,8 +10153,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead.
*/
- public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+ @Deprecated
+ public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED;
/**
* WFC mode on home/non-roaming network.
@@ -10104,8 +10164,10 @@
* Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead.
*/
- public static final String WFC_IMS_MODE = "wfc_ims_mode";
+ @Deprecated
+ public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE;
/**
* WFC mode on roaming network.
@@ -10113,8 +10175,11 @@
* Type: int - see {@link #WFC_IMS_MODE} for values
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE}
+ * instead.
*/
- public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+ @Deprecated
+ public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE;
/**
* Whether WFC roaming is enabled
@@ -10122,8 +10187,12 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED}
+ * instead
*/
- public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+ @Deprecated
+ public static final String WFC_IMS_ROAMING_ENABLED =
+ SubscriptionManager.WFC_IMS_ROAMING_ENABLED;
/**
* Whether user can enable/disable LTE as a preferred network. A carrier might control
@@ -10315,6 +10384,14 @@
"storage_settings_clobber_threshold";
/**
+ * Exemptions to the hidden API blacklist.
+ *
+ * @hide
+ */
+ public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS =
+ "hidden_api_blacklist_exemptions";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
new file mode 100644
index 0000000..65ce67f
--- /dev/null
+++ b/core/java/android/se/omapi/Channel.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent an ISO/IEC 7816-4 channel opened to a
+ * Secure Element. It can be either a logical channel or the basic channel. They
+ * can be used to send APDUs to the secure element. Channels are opened by
+ * calling the Session.openBasicChannel(byte[]) or
+ * Session.openLogicalChannel(byte[]) methods.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Channel {
+
+ private static final String TAG = "OMAPI.Channel";
+ private Session mSession;
+ private final ISecureElementChannel mChannel;
+ private final SEService mService;
+ private final Object mLock = new Object();
+
+ Channel(SEService service, Session session, ISecureElementChannel channel) {
+ if (service == null || session == null || channel == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mService = service;
+ mSession = session;
+ mChannel = channel;
+ }
+
+ /**
+ * Closes this channel to the Secure Element. If the method is called when
+ * the channel is already closed, this method will be ignored. The close()
+ * method shall wait for completion of any pending transmit(byte[] command)
+ * before closing the channel.
+ */
+ public void close() {
+ if (!isClosed()) {
+ synchronized (mLock) {
+ try {
+ mChannel.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Error closing channel", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Tells if this channel is closed.
+ *
+ * @return <code>true</code> if the channel is closed or in case of an error.
+ * <code>false</code> otherwise.
+ */
+ public boolean isClosed() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return true;
+ }
+ try {
+ return mChannel.isClosed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception in isClosed()");
+ return true;
+ }
+ }
+
+ /**
+ * Returns a boolean telling if this channel is the basic channel.
+ *
+ * @return <code>true</code> if this channel is a basic channel. <code>false</code> if
+ * this channel is a logical channel.
+ */
+ public boolean isBasicChannel() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ return mChannel.isBasicChannel();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The
+ * underlying layers generate as many TPDUs as necessary to transport this APDU. The
+ * API shall ensure that all available data returned from Secure Element, including
+ * concatenated responses, are retrieved and made available to the calling application. If a
+ * warning status code is received the API wont check for further response data but will
+ * return all data received so far and the warning status code.<br>
+ * The transport part is invisible from the application. The generated response is the
+ * response of the APDU which means that all protocols related responses are handled
+ * inside the API or the underlying implementation.<br>
+ * The transmit method shall support extended length APDU commands independently of
+ * the coding within the ATR.<br>
+ * For status word '61 XX' the API or underlying implementation shall issue a GET
+ * RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status
+ * word '6C XX', the API or underlying implementation shall reissue the input command
+ * with LE=XX. For other status words, the API (or underlying implementation) shall return
+ * the complete response including data and status word to the device application. The API
+ * (or underlying implementation) shall not handle internally the received status words. The
+ * channel shall not be closed even if the Secure Element answered with an error code.
+ * The system ensures the synchronization between all the concurrent calls to this method,
+ * and that only one APDU will be sent at a time, irrespective of the number of TPDUs that
+ * might be required to transport it to the SE. The entire APDU communication to this SE is
+ * locked to the APDU.<br>
+ * The channel information in the class byte in the APDU will be ignored. The system will
+ * add any required information to ensure the APDU is transported on this channel.
+ * The only restrictions on the set of commands that can be sent is defined below, the API
+ * implementation shall be able to send all other commands: <br>
+ * <ul>
+ * <li>MANAGE_CHANNEL commands are not allowed.</li>
+ * <li>SELECT by DF Name (p1=04) are not allowed.</li>
+ * <li>CLA bytes with channel numbers are de-masked.</li>
+ * </ul>
+ *
+ * @param command the APDU command to be transmitted, as a byte array.
+ *
+ * @return the response received, as a byte array. The returned byte array contains the data
+ * bytes in the following order:
+ * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+ *
+ * @throws IOException if there is a communication problem to the reader or the Secure Element.
+ * @throws IllegalStateException if the channel is used after being closed.
+ * @throws IllegalArgumentException if the command byte array is less than 4 bytes long.
+ * @throws IllegalArgumentException if Lc byte is inconsistent with length of the byte array.
+ * @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff).
+ * @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x).
+ * @throws SecurityException if the command is filtered by the security policy.
+ * @throws NullPointerException if command is NULL.
+ */
+ public @NonNull byte[] transmit(byte[] command) throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ synchronized (mLock) {
+ try {
+ byte[] response = mChannel.transmit(command);
+ if (response == null) {
+ throw new IOException("Error in communicating with Secure Element");
+ }
+ return response;
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Get the session that has opened this channel.
+ *
+ * @return the session object this channel is bound to.
+ */
+ public @NonNull Session getSession() {
+ return mSession;
+ }
+
+ /**
+ * Returns the data as received from the application select command inclusively the status word
+ * received at applet selection.
+ * The returned byte array contains the data bytes in the following order:
+ * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+ * @return The data as returned by the application select command inclusively the status word.
+ * Only the status word if the application select command has no returned data.
+ * Returns null if an application select command has not been performed or the selection
+ * response can not be retrieved by the reader implementation.
+ */
+ public @Nullable byte[] getSelectResponse() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+
+ byte[] response;
+ try {
+ response = mChannel.getSelectResponse();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+
+ if (response != null && response.length == 0) {
+ response = null;
+ }
+ return response;
+ }
+
+ /**
+ * Performs a selection of the next Applet on this channel that matches to the partial AID
+ * specified in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.
+ * This mechanism can be used by a device application to iterate through all Applets
+ * matching to the same partial AID.
+ * If selectNext() returns true a new Applet was successfully selected on this channel.
+ * If no further Applet exists with matches to the partial AID this method returns false
+ * and the already selected Applet stays selected. <br>
+ *
+ * Since the API cannot distinguish between a partial and full AID the API shall rely on the
+ * response of the Secure Element for the return value of this method. <br>
+ * The implementation of the underlying SELECT command within this method shall use
+ * the same values as the corresponding openBasicChannel(byte[] aid) or
+ * openLogicalChannel(byte[] aid) command with the option: <br>
+ * P2='02' (Next occurrence) <br>
+ * The select response stored in the Channel object shall be updated with the APDU
+ * response of the SELECT command.
+
+ * @return <code>true</code> if new Applet was selected on this channel.
+ <code>false</code> he already selected Applet stays selected on this channel.
+ *
+ * @throws IOException if there is a communication problem to the reader or the Secure Element.
+ * @throws IllegalStateException if the channel is used after being closed.
+ * @throws UnsupportedOperationException if this operation is not supported by the card.
+ */
+ public boolean selectNext() throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ synchronized (mLock) {
+ return mChannel.selectNext();
+ }
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+}
diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/core/java/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 0000000..4ae57ab
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+interface ISecureElementChannel {
+
+ /**
+ * Closes the specified connection and frees internal resources.
+ * A logical channel will be closed.
+ */
+ void close();
+
+ /**
+ * Tells if this channel is closed.
+ *
+ * @return <code>true</code> if the channel is closed,
+ * <code>false</code> otherwise.
+ */
+ boolean isClosed();
+
+ /**
+ * Returns a boolean telling if this channel is the basic channel.
+ *
+ * @return <code>true</code> if this channel is a basic channel.
+ * <code>false</code> if this channel is a logical channel.
+ */
+ boolean isBasicChannel();
+
+ /**
+ * Returns the data as received from the application select command
+ * inclusively the status word. The returned byte array contains the data
+ * bytes in the following order:
+ * [<first data byte>, ..., <last data byte>, <sw1>, <sw2>]
+ */
+ byte[] getSelectResponse();
+
+ /**
+ * Transmits the specified command APDU and returns the response APDU.
+ * MANAGE channel commands are not supported.
+ * Selection of applets is not supported in logical channels.
+ */
+ byte[] transmit(in byte[] command);
+
+ /**
+ * Performs a selection of the next Applet on this channel that matches to
+ * the partial AID specified in the openBasicChannel(byte[] aid) or
+ * openLogicalChannel(byte[] aid) method. This mechanism can be used by a
+ * device application to iterate through all Applets matching to the same
+ * partial AID.
+ * If selectNext() returns true a new Applet was successfully selected on
+ * this channel.
+ * If no further Applet exists with matches to the partial AID this method
+ * returns false and the already selected Applet stays selected.
+ *
+ * @return <code>true</code> if new Applet was successfully selected.
+ * <code>false</code> if no further Applet exists which matches the
+ * partial AID.
+ */
+ boolean selectNext();
+}
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/se/omapi/ISecureElementListener.aidl
similarity index 63%
copy from core/java/android/os/Seccomp.java
copy to core/java/android/se/omapi/ISecureElementListener.aidl
index f14e93f..e0c6e04 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,12 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
-package android.os;
+package android.se.omapi;
/**
+ * Interface to receive call-backs when the service is connected.
* @hide
*/
-public final class Seccomp {
- public static final native void setPolicy();
+interface ISecureElementListener {
+ /**
+ * Called by the framework when the service is connected.
+ */
+ void serviceConnected();
}
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 0000000..a312c44
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementSession;
+
+/** @hide */
+interface ISecureElementReader {
+
+ /**
+ * Returns true if a card is present in the specified reader.
+ * Returns false if a card is not present in the specified reader.
+ */
+ boolean isSecureElementPresent();
+
+ /**
+ * Connects to a secure element in this reader. <br>
+ * This method prepares (initialises) the Secure Element for communication
+ * before the Session object is returned (e.g. powers the Secure Element by
+ * ICC ON if its not already on). There might be multiple sessions opened at
+ * the same time on the same reader. The system ensures the interleaving of
+ * APDUs between the respective sessions.
+ *
+ * @return a Session object to be used to create Channels.
+ */
+ ISecureElementSession openSession();
+
+ /**
+ * Close all the sessions opened on this reader. All the channels opened by
+ * all these sessions will be closed.
+ */
+ void closeSessions();
+
+}
diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/core/java/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 0000000..4fa799e
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementReader;
+
+/**
+ * SecureElement service interface.
+ * @hide
+ */
+interface ISecureElementService {
+
+ /**
+ * Returns the friendly names of available Secure Element readers.
+ */
+ String[] getReaders();
+
+ /**
+ * Returns SecureElement Service reader object to the given name.
+ */
+ ISecureElementReader getReader(String reader);
+
+ /**
+ * Checks if the application defined by the package name is allowed to
+ * receive NFC transaction events for the defined AID.
+ */
+ boolean[] isNFCEventAllowed(String reader, in byte[] aid,
+ in String[] packageNames);
+
+}
diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/core/java/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 0000000..8ea599f
--- /dev/null
+++ b/core/java/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.se.omapi.ISecureElementChannel;
+import android.se.omapi.ISecureElementReader;
+import android.se.omapi.ISecureElementListener;
+
+/** @hide */
+interface ISecureElementSession {
+
+ /**
+ * Returns the ATR of the connected card or null if the ATR is not available
+ */
+ byte[] getAtr();
+
+ /**
+ * Close the connection with the Secure Element. This will close any
+ * channels opened by this application with this Secure Element.
+ */
+ void close();
+
+ /**
+ * Close any channel opened on this session.
+ */
+ void closeChannels();
+
+
+ /**
+ * Tells if this session is closed.
+ *
+ * @return <code>true</code> if the session is closed, false otherwise.
+ */
+ boolean isClosed();
+
+ /**
+ * Opens a connection using the basic channel of the card in the
+ * specified reader and returns a channel handle. Selects the specified
+ * applet if aid != null.
+ * Logical channels cannot be opened with this connection.
+ * Use interface method openLogicalChannel() to open a logical channel.
+ */
+ ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2,
+ ISecureElementListener listener);
+
+ /**
+ * Opens a connection using the next free logical channel of the card in the
+ * specified reader. Selects the specified applet.
+ * Selection of other applets with this connection is not supported.
+ */
+ ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2,
+ ISecureElementListener listener);
+}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
new file mode 100644
index 0000000..3dec976
--- /dev/null
+++ b/core/java/android/se/omapi/Reader.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent Secure Element Readers supported to this
+ * device. These Readers can be physical devices or virtual devices. They can be
+ * removable or not. They can contain Secure Element that can or cannot be
+ * removed.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Reader {
+
+ private static final String TAG = "OMAPI.Reader";
+ private final String mName;
+ private final SEService mService;
+ private ISecureElementReader mReader;
+ private final Object mLock = new Object();
+
+
+ Reader(SEService service, String name, ISecureElementReader reader) {
+ if (reader == null || service == null || name == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mName = name;
+ mService = service;
+ mReader = reader;
+ }
+
+ /**
+ * Return the name of this reader.
+ * <ul>
+ * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+ * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+ * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+ * </ul>
+ * Slot is a decimal number without leading zeros. The Numbering must start with 1
+ * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
+ * The slot number “1” for a reader is optional
+ * (SIM and SIM1 are both valid for the first SIM-reader,
+ * but if there are two readers then the second reader must be named SIM2).
+ * This applies also for other SD or SE readers.
+ *
+ * @return the reader name, as a String.
+ */
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ /**
+ * Connects to a Secure Element in this reader. <br>
+ * This method prepares (initialises) the Secure Element for communication
+ * before the Session object is returned (e.g. powers the Secure Element by
+ * ICC ON if its not already on). There might be multiple sessions opened at
+ * the same time on the same reader. The system ensures the interleaving of
+ * APDUs between the respective sessions.
+ *
+ * @throws IOException if something went wrong with the communicating to the
+ * Secure Element or the reader.
+ * @return a Session object to be used to create Channels.
+ */
+ public @NonNull Session openSession() throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service is not connected");
+ }
+
+ synchronized (mLock) {
+ ISecureElementSession session;
+ try {
+ session = mReader.openSession();
+ } catch (ServiceSpecificException e) {
+ throw new IOException(e.getMessage());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ if (session == null) {
+ throw new IOException("service session is null.");
+ }
+ return new Session(mService, session, this);
+ }
+ }
+
+ /**
+ * Check if a Secure Element is present in this reader.
+ *
+ * @throws IllegalStateException if the service is not connected
+ * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
+ */
+ public boolean isSecureElementPresent() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service is not connected");
+ }
+
+ try {
+ return mReader.isSecureElementPresent();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Error in isSecureElementPresent()");
+ }
+ }
+
+ /**
+ * Return the Secure Element service this reader is bound to.
+ *
+ * @return the SEService object.
+ */
+ public @NonNull SEService getSEService() {
+ return mService;
+ }
+
+ /**
+ * Close all the sessions opened on this reader.
+ * All the channels opened by all these sessions will be closed.
+ */
+ public void closeSessions() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service is not connected");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ mReader.closeSessions();
+ } catch (RemoteException ignore) { }
+ }
+ }
+}
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
new file mode 100644
index 0000000..d59e86a
--- /dev/null
+++ b/core/java/android/se/omapi/SEService.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * The SEService realises the communication to available Secure Elements on the
+ * device. This is the entry point of this API. It is used to connect to the
+ * infrastructure and get access to a list of Secure Element Readers.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a>
+ */
+public class SEService {
+
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if there was an error communicating with the Secure Element.
+ *
+ * @hide
+ */
+ public static final int IO_ERROR = 1;
+
+ /**
+ * Error code used with ServiceSpecificException.
+ * Thrown if AID cannot be selected or is not available when opening
+ * a logical channel.
+ *
+ * @hide
+ */
+ public static final int NO_SUCH_ELEMENT_ERROR = 2;
+
+ /**
+ * Interface to send call-backs to the application when the service is connected.
+ */
+ public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ /**
+ * Called by the framework when the service is connected.
+ */
+ public void serviceConnected() {};
+ }
+
+ private static final String TAG = "OMAPI.SEService";
+
+ private final Object mLock = new Object();
+
+ /** The client context (e.g. activity). */
+ private final Context mContext;
+
+ /** The backend system. */
+ private volatile ISecureElementService mSecureElementService;
+
+ /**
+ * Class for interacting with the main interface of the backend.
+ */
+ private ServiceConnection mConnection;
+
+ /**
+ * Collection of available readers
+ */
+ private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
+
+ /**
+ * Listener object that allows the notification of the caller if this
+ * SEService could be bound to the backend.
+ */
+ private ISecureElementListener mSEListener;
+
+ /**
+ * Establishes a new connection that can be used to connect to all the
+ * Secure Elements available in the system. The connection process can be
+ * quite long, so it happens in an asynchronous way. It is usable only if
+ * the specified listener is called or if isConnected() returns
+ * <code>true</code>. <br>
+ * The call-back object passed as a parameter will have its
+ * serviceConnected() method called when the connection actually happen.
+ *
+ * @param context
+ * the context of the calling application. Cannot be
+ * <code>null</code>.
+ * @param listener
+ * a SecureElementListener object. Can be <code>null</code>.
+ */
+ public SEService(Context context, SecureElementListener listener) {
+
+ if (context == null) {
+ throw new NullPointerException("context must not be null");
+ }
+
+ mContext = context;
+ mSEListener = listener;
+
+ mConnection = new ServiceConnection() {
+
+ public synchronized void onServiceConnected(
+ ComponentName className, IBinder service) {
+
+ mSecureElementService = ISecureElementService.Stub.asInterface(service);
+ if (mSEListener != null) {
+ try {
+ mSEListener.serviceConnected();
+ } catch (RemoteException ignore) { }
+ }
+ Log.i(TAG, "Service onServiceConnected");
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mSecureElementService = null;
+ Log.i(TAG, "Service onServiceDisconnected");
+ }
+ };
+
+ Intent intent = new Intent(ISecureElementService.class.getName());
+ intent.setClassName("com.android.se",
+ "com.android.se.SecureElementService");
+ boolean bindingSuccessful =
+ mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ if (bindingSuccessful) {
+ Log.i(TAG, "bindService successful");
+ }
+ }
+
+ /**
+ * Tells whether or not the service is connected.
+ *
+ * @return <code>true</code> if the service is connected.
+ */
+ public boolean isConnected() {
+ return mSecureElementService != null;
+ }
+
+ /**
+ * Returns the list of available Secure Element readers.
+ * There must be no duplicated objects in the returned list.
+ * All available readers shall be listed even if no card is inserted.
+ *
+ * @return The readers list, as an array of Readers. If there are no
+ * readers the returned array is of length 0.
+ */
+ public @NonNull Reader[] getReaders() {
+ if (mSecureElementService == null) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ String[] readerNames;
+ try {
+ readerNames = mSecureElementService.getReaders();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
+ Reader[] readers = new Reader[readerNames.length];
+ int i = 0;
+ for (String readerName : readerNames) {
+ if (mReaders.get(readerName) == null) {
+ try {
+ mReaders.put(readerName, new Reader(this, readerName,
+ getReader(readerName)));
+ readers[i++] = mReaders.get(readerName);
+ } catch (Exception e) {
+ Log.e(TAG, "Error adding Reader: " + readerName, e);
+ }
+ } else {
+ readers[i++] = mReaders.get(readerName);
+ }
+ }
+ return readers;
+ }
+
+ /**
+ * Releases all Secure Elements resources allocated by this SEService
+ * (including any binding to an underlying service).
+ * As a result isConnected() will return false after shutdown() was called.
+ * After this method call, the SEService object is not connected.
+ * It is recommended to call this method in the termination method of the calling application
+ * (or part of this application) which is bound to this SEService.
+ */
+ public void shutdown() {
+ synchronized (mLock) {
+ if (mSecureElementService != null) {
+ for (Reader reader : mReaders.values()) {
+ try {
+ reader.closeSessions();
+ } catch (Exception ignore) { }
+ }
+ }
+ try {
+ mContext.unbindService(mConnection);
+ } catch (IllegalArgumentException e) {
+ // Do nothing and fail silently since an error here indicates
+ // that binding never succeeded in the first place.
+ }
+ mSecureElementService = null;
+ }
+ }
+
+ /**
+ * Returns the version of the OpenMobile API specification this
+ * implementation is based on.
+ *
+ * @return String containing the OpenMobile API version (e.g. "3.0").
+ */
+ public String getVersion() {
+ return "3.2";
+ }
+
+ @NonNull ISecureElementListener getListener() {
+ return mSEListener;
+ }
+
+ /**
+ * Obtain a Reader instance from the SecureElementService
+ */
+ private @NonNull ISecureElementReader getReader(String name) {
+ try {
+ return mSecureElementService.getReader(name);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+}
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
new file mode 100644
index 0000000..19a018e
--- /dev/null
+++ b/core/java/android/se/omapi/Session.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (c) 2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.NoSuchElementException;
+
+/**
+ * Instances of this class represent a connection session to one of the Secure
+ * Elements available on the device. These objects can be used to get a
+ * communication channel with an Applet in the Secure Element.
+ * This channel can be the basic channel or a logical channel.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API v3.0</a>
+ */
+public class Session {
+
+ private final Object mLock = new Object();
+ private final SEService mService;
+ private final Reader mReader;
+ private final ISecureElementSession mSession;
+ private static final String TAG = "OMAPI.Session";
+
+ Session(SEService service, ISecureElementSession session, Reader reader) {
+ if (service == null || reader == null || session == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+ mService = service;
+ mReader = reader;
+ mSession = session;
+ }
+
+ /**
+ * Get the reader that provides this session.
+ *
+ * @return The Reader object.
+ */
+ public @NonNull Reader getReader() {
+ return mReader;
+ }
+
+ /**
+ * Get the Answer to Reset of this Secure Element. <br>
+ * The returned byte array can be null if the ATR for this Secure Element is
+ * not available.
+ *
+ * @throws IllegalStateException if there was an error connecting to SE or
+ * if the service was not connected.
+ * @return the ATR as a byte array or null.
+ */
+ public @Nullable byte[] getATR() {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ try {
+ return mSession.getAtr();
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Close the connection with the Secure Element. This will close any
+ * channels opened by this application with this Secure Element.
+ */
+ public void close() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ mSession.close();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error closing session", e);
+ }
+ }
+ }
+
+ /**
+ * Tells if this session is closed.
+ *
+ * @return <code>true</code> if the session is closed, false otherwise.
+ */
+ public boolean isClosed() {
+ try {
+ return mSession.isClosed();
+ } catch (RemoteException e) {
+ // If there was an error here, then the session is considered close
+ return true;
+ }
+ }
+
+ /**
+ * Close any channel opened on this session.
+ */
+ public void closeChannels() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service not connected to system");
+ return;
+ }
+
+ synchronized (mLock) {
+ try {
+ mSession.closeChannels();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error closing channels", e);
+ }
+ }
+ }
+
+ /**
+ * Get an access to the basic channel, as defined in the ISO/IEC 7816-4 specification (the
+ * one that has number 0). The obtained object is an instance of the Channel class.
+ * If the AID is null, it means no Applet is to be selected on this channel and the default
+ * Applet is used. If the AID is defined then the corresponding Applet is selected.
+ * Once this channel has been opened by a device application, it is considered as "locked"
+ * by this device application, and other calls to this method will return null, until the
+ * channel is closed. Some Secure Elements (like the UICC) might always keep the basic channel
+ * locked (i.e. return null to applications), to prevent access to the basic channel, while
+ * some other might return a channel object implementing some kind of filtering on the
+ * commands, restricting the set of accepted command to a smaller set.
+ * It is recommended for the UICC to reject the opening of the basic channel to a specific
+ * applet, by always answering null to such a request.
+ * For other Secure Elements, the recommendation is to accept opening the basic channel
+ * on the default applet until another applet is selected on the basic channel. As there is no
+ * other way than a reset to select again the default applet, the implementation of the
+ * transport API should guarantee that the openBasicChannel(null) command will return
+ * null until a reset occurs.
+ * With previous release (V2.05) it was not possible to set P2 value, this value was always
+ * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+ * recommended that the device allows all values for P2, however only the following values
+ * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+ * The implementation of the underlying SELECT command within this method shall be
+ * based on ISO 7816-4 with following options:
+ * <ul>
+ * <li>CLA = '00'</li>
+ * <li>INS = 'A4'</li>
+ * <li>P1 = '04' (Select by DF name/application identifier)</li>
+ * </ul>
+ *
+ * The select response data can be retrieved with byte[] getSelectResponse().
+ * The API shall handle received status word as follow. If the status word indicates that the
+ * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+ * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+ * channel opened and the next getSelectResponse() shall return the received status
+ * word.
+ * Other received status codes indicating that the Secure Element was able not to open a
+ * channel shall be considered as an error and the corresponding channel shall not be
+ * opened.
+ * The function without P2 as parameter is provided for backwards compatibility and will
+ * fall back to a select command with P2='00'.
+ *
+ * @param aid the AID of the Applet to be selected on this channel, as a
+ * byte array, or null if no Applet is to be selected.
+ * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+ * @throws IOException if there is a communication problem to the reader or
+ * the Secure Element.
+ * @throws IllegalStateException if the Secure Element session is used after
+ * being closed.
+ * @throws IllegalArgumentException if the aid's length is not within 5 to
+ * 16 (inclusive).
+ * @throws SecurityException if the calling application cannot be granted
+ * access to this AID or the default Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+ * selected.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device
+ * @return an instance of Channel if available or null.
+ */
+ public @Nullable Channel openBasicChannel(byte[] aid, byte p2) throws IOException {
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+
+ synchronized (mLock) {
+ try {
+ ISecureElementChannel channel = mSession.openBasicChannel(aid, p2,
+ mReader.getSEService().getListener());
+ if (channel == null) {
+ return null;
+ }
+ return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * This method is provided to ease the development of mobile application and for compliancy
+ * with existing applications.
+ * This method is equivalent to openBasicChannel(aid, P2=0x00)
+ *
+ * @param aid the AID of the Applet to be selected on this channel, as a
+ * byte array, or null if no Applet is to be selected.
+ * @throws IOException if there is a communication problem to the reader or
+ * the Secure Element.
+ * @throws IllegalStateException if the Secure Element session is used after
+ * being closed.
+ * @throws IllegalArgumentException if the aid's length is not within 5 to
+ * 16 (inclusive).
+ * @throws SecurityException if the calling application cannot be granted
+ * access to this AID or the default Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+ * selected.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device
+ * @return an instance of Channel if available or null.
+ */
+ public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
+ return openBasicChannel(aid, (byte) 0x00);
+ }
+
+ /**
+ * Open a logical channel with the Secure Element, selecting the Applet represented by
+ * the given AID. If the AID is null, which means no Applet is to be selected on this
+ * channel, the default Applet is used. It's up to the Secure Element to choose which
+ * logical channel will be used.
+ * With previous release (V2.05) it was not possible to set P2 value, this value was always
+ * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+ * recommended that the device allows all values for P2, however only the following values
+ * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+ * The implementation of the underlying SELECT command within this method shall be
+ * based on ISO 7816-4 with following options:
+ *
+ * <ul>
+ * <li>CLA = '01' to '03', '40 to 4F'</li>
+ * <li>INS = 'A4'</li>
+ * <li>P1 = '04' (Select by DF name/application identifier)</li>
+ * </ul>
+ *
+ * The select response data can be retrieved with byte[] getSelectResponse().
+ * The API shall handle received status word as follow. If the status word indicates that the
+ * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+ * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+ * channel opened and the next getSelectResponse() shall return the received status
+ * word.
+ * Other received status codes indicating that the Secure Element was able not to open a
+ * channel shall be considered as an error and the corresponding channel shall not be
+ * opened.
+ * In case of UICC it is recommended for the API to reject the opening of the logical
+ * channel without a specific AID, by always answering null to such a request.
+ * The function without P2 as parameter is provided for backwards compatibility and will
+ * fall back to a select command with P2=00.
+ *
+ * @param aid the AID of the Applet to be selected on this channel, as a
+ * byte array.
+ * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+ * @throws IOException if there is a communication problem to the reader or
+ * the Secure Element.
+ * @throws IllegalStateException if the Secure Element is used after being
+ * closed.
+ * @throws IllegalArgumentException if the aid's length is not within 5 to
+ * 16 (inclusive).
+ * @throws SecurityException if the calling application cannot be granted
+ * access to this AID or the default Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not
+ * available or cannot be selected or a logical channel is already
+ * open to a non-multiselectable Applet.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device.
+ * @return an instance of Channel. Null if the Secure Element is unable to
+ * provide a new logical channel.
+ */
+ public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException {
+
+ if ((mReader.getName().startsWith("SIM")) && (aid == null)) {
+ Log.e(TAG, "NULL AID not supported on " + mReader.getName());
+ return null;
+ }
+
+ if (!mService.isConnected()) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ synchronized (mLock) {
+ try {
+ ISecureElementChannel channel = mSession.openLogicalChannel(
+ aid,
+ p2,
+ mReader.getSEService().getListener());
+ if (channel == null) {
+ return null;
+ }
+ return new Channel(mService, this, channel);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == SEService.IO_ERROR) {
+ throw new IOException(e.getMessage());
+ } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+ throw new NoSuchElementException(e.getMessage());
+ } else {
+ throw new IllegalStateException(e.getMessage());
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * This method is provided to ease the development of mobile application and for compliancy
+ * with existing applications.
+ * This method is equivalent to openLogicalChannel(aid, P2=0x00)
+ *
+ * @param aid the AID of the Applet to be selected on this channel, as a
+ * byte array.
+ * @throws IOException if there is a communication problem to the reader or
+ * the Secure Element.
+ * @throws IllegalStateException if the Secure Element is used after being
+ * closed.
+ * @throws IllegalArgumentException if the aid's length is not within 5 to
+ * 16 (inclusive).
+ * @throws SecurityException if the calling application cannot be granted
+ * access to this AID or the default Applet on this
+ * session.
+ * @throws NoSuchElementException if the AID on the Secure Element is not
+ * available or cannot be selected or a logical channel is already
+ * open to a non-multiselectable Applet.
+ * @throws UnsupportedOperationException if the given P2 parameter is not
+ * supported by the device.
+ * @return an instance of Channel. Null if the Secure Element is unable to
+ * provide a new logical channel.
+ */
+ public @Nullable Channel openLogicalChannel(byte[] aid) throws IOException {
+ return openLogicalChannel(aid, (byte) 0x00);
+ }
+}
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index b47e872..09bba4b 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -16,9 +16,14 @@
package android.service.carrier;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.util.Objects;
+
/**
* Used to pass info to CarrierConfigService implementations so they can decide what values to
* return.
@@ -40,13 +45,13 @@
private String mMcc;
private String mMnc;
- private String mSpn;
- private String mImsi;
- private String mGid1;
- private String mGid2;
+ private @Nullable String mSpn;
+ private @Nullable String mImsi;
+ private @Nullable String mGid1;
+ private @Nullable String mGid2;
- public CarrierIdentifier(String mcc, String mnc, String spn, String imsi, String gid1,
- String gid2) {
+ public CarrierIdentifier(String mcc, String mnc, @Nullable String spn, @Nullable String imsi,
+ @Nullable String gid1, @Nullable String gid2) {
mMcc = mcc;
mMnc = mnc;
mSpn = spn;
@@ -55,6 +60,32 @@
mGid2 = gid2;
}
+ /**
+ * Creates a carrier identifier instance.
+ *
+ * @param mccMnc A 3-byte array as defined by 3GPP TS 24.008.
+ * @param gid1 The group identifier level 1.
+ * @param gid2 The group identifier level 2.
+ * @throws IllegalArgumentException If the length of {@code mccMnc} is not 3.
+ */
+ public CarrierIdentifier(byte[] mccMnc, @Nullable String gid1, @Nullable String gid2) {
+ if (mccMnc.length != 3) {
+ throw new IllegalArgumentException(
+ "MCC & MNC must be set by a 3-byte array: byte[" + mccMnc.length + "]");
+ }
+ String hex = IccUtils.bytesToHexString(mccMnc);
+ mMcc = new String(new char[] {hex.charAt(1), hex.charAt(0), hex.charAt(3)});
+ if (hex.charAt(2) == 'F') {
+ mMnc = new String(new char[] {hex.charAt(5), hex.charAt(4)});
+ } else {
+ mMnc = new String(new char[] {hex.charAt(5), hex.charAt(4), hex.charAt(2)});
+ }
+ mGid1 = gid1;
+ mGid2 = gid2;
+ mSpn = null;
+ mImsi = null;
+ }
+
/** @hide */
public CarrierIdentifier(Parcel parcel) {
readFromParcel(parcel);
@@ -71,26 +102,60 @@
}
/** Get the service provider name. */
+ @Nullable
public String getSpn() {
return mSpn;
}
/** Get the international mobile subscriber identity. */
+ @Nullable
public String getImsi() {
return mImsi;
}
/** Get the group identifier level 1. */
+ @Nullable
public String getGid1() {
return mGid1;
}
/** Get the group identifier level 2. */
+ @Nullable
public String getGid2() {
return mGid2;
}
@Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ CarrierIdentifier that = (CarrierIdentifier) obj;
+ return Objects.equals(mMcc, that.mMcc)
+ && Objects.equals(mMnc, that.mMnc)
+ && Objects.equals(mSpn, that.mSpn)
+ && Objects.equals(mImsi, that.mImsi)
+ && Objects.equals(mGid1, that.mGid1)
+ && Objects.equals(mGid2, that.mGid2);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + Objects.hashCode(mMcc);
+ result = 31 * result + Objects.hashCode(mMnc);
+ result = 31 * result + Objects.hashCode(mSpn);
+ result = 31 * result + Objects.hashCode(mImsi);
+ result = 31 * result + Objects.hashCode(mGid1);
+ result = 31 * result + Objects.hashCode(mGid2);
+ return result;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/core/java/android/service/euicc/EuiccProfileInfo.aidl
similarity index 82%
copy from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
copy to core/java/android/service/euicc/EuiccProfileInfo.aidl
index d648a35..321021b 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/core/java/android/service/euicc/EuiccProfileInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.service.euicc;
-package com.android.ims;
-
-parcelable ImsStreamMediaProfile;
+parcelable EuiccProfileInfo;
diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/core/java/android/service/euicc/EuiccProfileInfo.java
index ba6c9a2..cb4f104 100644
--- a/core/java/android/service/euicc/EuiccProfileInfo.java
+++ b/core/java/android/service/euicc/EuiccProfileInfo.java
@@ -15,32 +15,115 @@
*/
package android.service.euicc;
+import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
import android.telephony.UiccAccessRule;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
/**
* Information about an embedded profile (subscription) on an eUICC.
*
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class EuiccProfileInfo implements Parcelable {
+ /** Profile policy rules (bit mask) */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "POLICY_RULE_" }, value = {
+ POLICY_RULE_DO_NOT_DISABLE,
+ POLICY_RULE_DO_NOT_DELETE,
+ POLICY_RULE_DELETE_AFTER_DISABLING
+ })
+ /** @hide */
+ public @interface PolicyRule {}
+ /** Once this profile is enabled, it cannot be disabled. */
+ public static final int POLICY_RULE_DO_NOT_DISABLE = 1;
+ /** This profile cannot be deleted. */
+ public static final int POLICY_RULE_DO_NOT_DELETE = 1 << 1;
+ /** This profile should be deleted after being disabled. */
+ public static final int POLICY_RULE_DELETE_AFTER_DISABLING = 1 << 2;
+
+ /** Class of the profile */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "PROFILE_CLASS_" }, value = {
+ PROFILE_CLASS_TESTING,
+ PROFILE_CLASS_PROVISIONING,
+ PROFILE_CLASS_OPERATIONAL,
+ PROFILE_CLASS_UNSET
+ })
+ /** @hide */
+ public @interface ProfileClass {}
+ /** Testing profiles */
+ public static final int PROFILE_CLASS_TESTING = 0;
+ /** Provisioning profiles which are pre-loaded on eUICC */
+ public static final int PROFILE_CLASS_PROVISIONING = 1;
+ /** Operational profiles which can be pre-loaded or downloaded */
+ public static final int PROFILE_CLASS_OPERATIONAL = 2;
+ /**
+ * Profile class not set.
+ * @hide
+ */
+ public static final int PROFILE_CLASS_UNSET = -1;
+
+ /** State of the profile */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "PROFILE_STATE_" }, value = {
+ PROFILE_STATE_DISABLED,
+ PROFILE_STATE_ENABLED,
+ PROFILE_STATE_UNSET
+ })
+ /** @hide */
+ public @interface ProfileState {}
+ /** Disabled profiles */
+ public static final int PROFILE_STATE_DISABLED = 0;
+ /** Enabled profile */
+ public static final int PROFILE_STATE_ENABLED = 1;
+ /**
+ * Profile state not set.
+ * @hide
+ */
+ public static final int PROFILE_STATE_UNSET = -1;
+
/** The iccid of the subscription. */
- public final String iccid;
+ private final String mIccid;
+
+ /** An optional nickname for the subscription. */
+ private final @Nullable String mNickname;
+
+ /** The service provider name for the subscription. */
+ private final String mServiceProviderName;
+
+ /** The profile name for the subscription. */
+ private final String mProfileName;
+
+ /** Profile class for the subscription. */
+ @ProfileClass private final int mProfileClass;
+
+ /** The profile state of the subscription. */
+ @ProfileState private final int mState;
+
+ /** The operator Id of the subscription. */
+ private final CarrierIdentifier mCarrierIdentifier;
+
+ /** The policy rules of the subscription. */
+ @PolicyRule private final int mPolicyRules;
/**
* Optional access rules defining which apps can manage this subscription. If unset, only the
* platform can manage it.
*/
- public final @Nullable UiccAccessRule[] accessRules;
-
- /** An optional nickname for the subscription. */
- public final @Nullable String nickname;
+ private final @Nullable UiccAccessRule[] mAccessRules;
public static final Creator<EuiccProfileInfo> CREATOR = new Creator<EuiccProfileInfo>() {
@Override
@@ -54,31 +137,315 @@
}
};
+ // TODO(b/70292228): Remove this method when LPA can be updated.
+ /**
+ * @hide
+ * @deprecated - Do not use.
+ */
+ @Deprecated
public EuiccProfileInfo(String iccid, @Nullable UiccAccessRule[] accessRules,
@Nullable String nickname) {
if (!TextUtils.isDigitsOnly(iccid)) {
throw new IllegalArgumentException("iccid contains invalid characters: " + iccid);
}
- this.iccid = iccid;
- this.accessRules = accessRules;
- this.nickname = nickname;
+ this.mIccid = iccid;
+ this.mAccessRules = accessRules;
+ this.mNickname = nickname;
+
+ this.mServiceProviderName = null;
+ this.mProfileName = null;
+ this.mProfileClass = PROFILE_CLASS_UNSET;
+ this.mState = PROFILE_STATE_UNSET;
+ this.mCarrierIdentifier = null;
+ this.mPolicyRules = 0;
}
private EuiccProfileInfo(Parcel in) {
- iccid = in.readString();
- accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
- nickname = in.readString();
+ mIccid = in.readString();
+ mNickname = in.readString();
+ mServiceProviderName = in.readString();
+ mProfileName = in.readString();
+ mProfileClass = in.readInt();
+ mState = in.readInt();
+ byte exist = in.readByte();
+ if (exist == (byte) 1) {
+ mCarrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in);
+ } else {
+ mCarrierIdentifier = null;
+ }
+ mPolicyRules = in.readInt();
+ mAccessRules = in.createTypedArray(UiccAccessRule.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(iccid);
- dest.writeTypedArray(accessRules, flags);
- dest.writeString(nickname);
+ dest.writeString(mIccid);
+ dest.writeString(mNickname);
+ dest.writeString(mServiceProviderName);
+ dest.writeString(mProfileName);
+ dest.writeInt(mProfileClass);
+ dest.writeInt(mState);
+ if (mCarrierIdentifier != null) {
+ dest.writeByte((byte) 1);
+ mCarrierIdentifier.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ dest.writeInt(mPolicyRules);
+ dest.writeTypedArray(mAccessRules, flags);
}
@Override
public int describeContents() {
return 0;
}
+
+ /** The builder to build a new {@link EuiccProfileInfo} instance. */
+ public static final class Builder {
+ private String mIccid;
+ private List<UiccAccessRule> mAccessRules;
+ private String mNickname;
+ private String mServiceProviderName;
+ private String mProfileName;
+ @ProfileClass private int mProfileClass;
+ @ProfileState private int mState;
+ private CarrierIdentifier mCarrierIdentifier;
+ @PolicyRule private int mPolicyRules;
+
+ public Builder(String value) {
+ if (!TextUtils.isDigitsOnly(value)) {
+ throw new IllegalArgumentException("iccid contains invalid characters: " + value);
+ }
+ mIccid = value;
+ }
+
+ public Builder(EuiccProfileInfo baseProfile) {
+ mIccid = baseProfile.mIccid;
+ mNickname = baseProfile.mNickname;
+ mServiceProviderName = baseProfile.mServiceProviderName;
+ mProfileName = baseProfile.mProfileName;
+ mProfileClass = baseProfile.mProfileClass;
+ mState = baseProfile.mState;
+ mCarrierIdentifier = baseProfile.mCarrierIdentifier;
+ mPolicyRules = baseProfile.mPolicyRules;
+ mAccessRules = Arrays.asList(baseProfile.mAccessRules);
+ }
+
+ /** Builds the profile instance. */
+ public EuiccProfileInfo build() {
+ if (mIccid == null) {
+ throw new IllegalStateException("ICCID must be set for a profile.");
+ }
+ return new EuiccProfileInfo(
+ mIccid,
+ mNickname,
+ mServiceProviderName,
+ mProfileName,
+ mProfileClass,
+ mState,
+ mCarrierIdentifier,
+ mPolicyRules,
+ mAccessRules);
+ }
+
+ /** Sets the iccId of the subscription. */
+ public Builder setIccid(String value) {
+ if (!TextUtils.isDigitsOnly(value)) {
+ throw new IllegalArgumentException("iccid contains invalid characters: " + value);
+ }
+ mIccid = value;
+ return this;
+ }
+
+ /** Sets the nickname of the subscription. */
+ public Builder setNickname(String value) {
+ mNickname = value;
+ return this;
+ }
+
+ /** Sets the service provider name of the subscription. */
+ public Builder setServiceProviderName(String value) {
+ mServiceProviderName = value;
+ return this;
+ }
+
+ /** Sets the profile name of the subscription. */
+ public Builder setProfileName(String value) {
+ mProfileName = value;
+ return this;
+ }
+
+ /** Sets the profile class of the subscription. */
+ public Builder setProfileClass(@ProfileClass int value) {
+ mProfileClass = value;
+ return this;
+ }
+
+ /** Sets the state of the subscription. */
+ public Builder setState(@ProfileState int value) {
+ mState = value;
+ return this;
+ }
+
+ /** Sets the carrier identifier of the subscription. */
+ public Builder setCarrierIdentifier(CarrierIdentifier value) {
+ mCarrierIdentifier = value;
+ return this;
+ }
+
+ /** Sets the policy rules of the subscription. */
+ public Builder setPolicyRules(@PolicyRule int value) {
+ mPolicyRules = value;
+ return this;
+ }
+
+ /** Sets the access rules of the subscription. */
+ public Builder setUiccAccessRule(@Nullable List<UiccAccessRule> value) {
+ mAccessRules = value;
+ return this;
+ }
+ }
+
+ private EuiccProfileInfo(
+ String iccid,
+ @Nullable String nickname,
+ String serviceProviderName,
+ String profileName,
+ @ProfileClass int profileClass,
+ @ProfileState int state,
+ CarrierIdentifier carrierIdentifier,
+ @PolicyRule int policyRules,
+ @Nullable List<UiccAccessRule> accessRules) {
+ this.mIccid = iccid;
+ this.mNickname = nickname;
+ this.mServiceProviderName = serviceProviderName;
+ this.mProfileName = profileName;
+ this.mProfileClass = profileClass;
+ this.mState = state;
+ this.mCarrierIdentifier = carrierIdentifier;
+ this.mPolicyRules = policyRules;
+ if (accessRules != null && accessRules.size() > 0) {
+ this.mAccessRules = accessRules.toArray(new UiccAccessRule[accessRules.size()]);
+ } else {
+ this.mAccessRules = null;
+ }
+ }
+
+ /** Gets the ICCID string. */
+ public String getIccid() {
+ return mIccid;
+ }
+
+ /** Gets the access rules. */
+ @Nullable
+ public List<UiccAccessRule> getUiccAccessRules() {
+ if (mAccessRules == null) return null;
+ return Arrays.asList(mAccessRules);
+ }
+
+ /** Gets the nickname. */
+ @Nullable
+ public String getNickname() {
+ return mNickname;
+ }
+
+ /** Gets the service provider name. */
+ public String getServiceProviderName() {
+ return mServiceProviderName;
+ }
+
+ /** Gets the profile name. */
+ public String getProfileName() {
+ return mProfileName;
+ }
+
+ /** Gets the profile class. */
+ @ProfileClass
+ public int getProfileClass() {
+ return mProfileClass;
+ }
+
+ /** Gets the state of the subscription. */
+ @ProfileState
+ public int getState() {
+ return mState;
+ }
+
+ /** Gets the carrier identifier. */
+ public CarrierIdentifier getCarrierIdentifier() {
+ return mCarrierIdentifier;
+ }
+
+ /** Gets the policy rules. */
+ @PolicyRule
+ public int getPolicyRules() {
+ return mPolicyRules;
+ }
+
+ /** Returns whether any policy rule exists. */
+ public boolean hasPolicyRules() {
+ return mPolicyRules != 0;
+ }
+
+ /** Checks whether a certain policy rule exists. */
+ public boolean hasPolicyRule(@PolicyRule int policy) {
+ return (mPolicyRules & policy) != 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ EuiccProfileInfo that = (EuiccProfileInfo) obj;
+ return Objects.equals(mIccid, that.mIccid)
+ && Objects.equals(mNickname, that.mNickname)
+ && Objects.equals(mServiceProviderName, that.mServiceProviderName)
+ && Objects.equals(mProfileName, that.mProfileName)
+ && mProfileClass == that.mProfileClass
+ && mState == that.mState
+ && Objects.equals(mCarrierIdentifier, that.mCarrierIdentifier)
+ && mPolicyRules == that.mPolicyRules
+ && Arrays.equals(mAccessRules, that.mAccessRules);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + Objects.hashCode(mIccid);
+ result = 31 * result + Objects.hashCode(mNickname);
+ result = 31 * result + Objects.hashCode(mServiceProviderName);
+ result = 31 * result + Objects.hashCode(mProfileName);
+ result = 31 * result + mProfileClass;
+ result = 31 * result + mState;
+ result = 31 * result + Objects.hashCode(mCarrierIdentifier);
+ result = 31 * result + mPolicyRules;
+ result = 31 * result + Arrays.hashCode(mAccessRules);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "EuiccProfileInfo (nickname="
+ + mNickname
+ + ", serviceProviderName="
+ + mServiceProviderName
+ + ", profileName="
+ + mProfileName
+ + ", profileClass="
+ + mProfileClass
+ + ", state="
+ + mState
+ + ", CarrierIdentifier="
+ + mCarrierIdentifier.toString()
+ + ", policyRules="
+ + mPolicyRules
+ + ", accessRules="
+ + Arrays.toString(mAccessRules)
+ + ")";
+ }
}
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index df0842f..b87faef 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -17,12 +17,14 @@
import android.annotation.CallSuper;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
+import android.telephony.euicc.EuiccManager.OtaStatus;
import android.util.ArraySet;
import java.util.concurrent.LinkedBlockingQueue;
@@ -40,8 +42,11 @@
* <p>To implement the LPA backend, you must extend this class and declare this service in your
* manifest file. The service must require the
* {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter
- * with the {@link #EUICC_SERVICE_INTERFACE} action. The priority of the intent filter must be set
- * to a non-zero value in case multiple implementations are present on the device. For example:
+ * with the {@link #EUICC_SERVICE_INTERFACE} action. It's suggested that the priority of the intent
+ * filter to be set to a non-zero value in case multiple implementations are present on the device.
+ * See the below example. Note that there will be problem if two LPAs are present and they have the
+ * same priority.
+ * Example:
*
* <pre>{@code
* <service android:name=".MyEuiccService"
@@ -64,9 +69,9 @@
* filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
* priority.
*
- * TODO(b/35851809): Make this a SystemApi.
* @hide
*/
+@SystemApi
public abstract class EuiccService extends Service {
/** Action which must be included in this service's intent filter. */
public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
@@ -76,7 +81,10 @@
// LUI actions. These are passthroughs of the corresponding EuiccManager actions.
- /** @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS */
+ /**
+ * @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS
+ * The difference is this one is used by system to bring up the LUI.
+ */
public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
"android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
/** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
@@ -87,7 +95,10 @@
// require user interaction.
// TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
// more scoped out.
- /** Alert the user that this action will result in an active SIM being deactivated. */
+ /**
+ * Alert the user that this action will result in an active SIM being deactivated.
+ * To implement the LUI triggered by the system, you need to define this in AndroidManifest.xml.
+ */
public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
"android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
/**
@@ -101,7 +112,11 @@
public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
"android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
- /** Intent extra set for resolution requests containing the package name of the calling app. */
+ /**
+ * Intent extra set for resolution requests containing the package name of the calling app.
+ * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM,
+ * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE.
+ */
public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
"android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
@@ -135,10 +150,18 @@
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
}
- /** Boolean extra for resolution actions indicating whether the user granted consent. */
- public static final String RESOLUTION_EXTRA_CONSENT = "consent";
- /** String extra for resolution actions indicating the carrier confirmation code. */
- public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code";
+ /**
+ * Boolean extra for resolution actions indicating whether the user granted consent.
+ * This is used and set by the implementation and used in {@code EuiccOperation}.
+ */
+ public static final String EXTRA_RESOLUTION_CONSENT =
+ "android.service.euicc.extra.RESOLUTION_CONSENT";
+ /**
+ * String extra for resolution actions indicating the carrier confirmation code.
+ * This is used and set by the implementation and used in {@code EuiccOperation}.
+ */
+ public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE =
+ "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
private final IEuiccService.Stub mStubWrapper;
@@ -192,6 +215,18 @@
}
/**
+ * Callback class for {@link #onStartOtaIfNecessary(int, OtaStatusChangedCallback)}
+ *
+ * The status of OTA which can be {@code android.telephony.euicc.EuiccManager#EUICC_OTA_}
+ *
+ * @see IEuiccService#startOtaIfNecessary
+ */
+ public abstract static class OtaStatusChangedCallback {
+ /** Called when OTA status is changed. */
+ public abstract void onOtaStatusChanged(int status);
+ }
+
+ /**
* Return the EID of the eUICC.
*
* @param slotId ID of the SIM slot being queried. This is currently not populated but is here
@@ -203,10 +238,29 @@
public abstract String onGetEid(int slotId);
/**
- * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription.
+ * Return the status of OTA update.
*
* @param slotId ID of the SIM slot to use for the operation. This is currently not populated
* but is here to future-proof the APIs.
+ * @return The status of Euicc OTA update.
+ * @see android.telephony.euicc.EuiccManager#getOtaStatus
+ */
+ public abstract @OtaStatus int onGetOtaStatus(int slotId);
+
+ /**
+ * Perform OTA if current OS is not the latest one.
+ *
+ * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
+ * but is here to future-proof the APIs.
+ * @param statusChangedCallback Function called when OTA status changed.
+ */
+ public abstract void onStartOtaIfNecessary(
+ int slotId, OtaStatusChangedCallback statusChangedCallback);
+
+ /**
+ * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription.
+ *
+ * @param slotId ID of the SIM slot to use for the operation.
* @param subscription A subscription whose metadata needs to be populated.
* @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
* eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
@@ -234,8 +288,7 @@
/**
* Download the given subscription.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param subscription The subscription to download.
* @param switchAfterDownload If true, the subscription should be enabled upon successful
* download.
@@ -253,8 +306,7 @@
/**
* Return a list of all @link EuiccProfileInfo}s.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @return The result of the operation.
* @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
* @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
@@ -264,8 +316,7 @@
/**
* Return info about the eUICC chip/device.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @return the {@link EuiccInfo} for the eUICC chip/device.
* @see android.telephony.euicc.EuiccManager#getEuiccInfo
*/
@@ -277,8 +328,7 @@
* <p>If the subscription is currently active, it should be deactivated first (equivalent to a
* physical SIM being ejected).
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param iccid the ICCID of the subscription to delete.
* @return the result of the delete operation. May be one of the predefined {@code RESULT_}
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
@@ -289,8 +339,7 @@
/**
* Switch to the given subscription.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param iccid the ICCID of the subscription to enable. May be null, in which case the current
* profile should be deactivated and no profile should be activated to replace it - this is
* equivalent to a physical SIM being ejected.
@@ -307,8 +356,7 @@
/**
* Update the nickname of the given subscription.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param iccid the ICCID of the subscription to update.
* @param nickname the new nickname to apply.
* @return the result of the update operation. May be one of the predefined {@code RESULT_}
@@ -385,6 +433,41 @@
}
@Override
+ public void startOtaIfNecessary(
+ int slotId, IOtaStatusChangedCallback statusChangedCallback) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ EuiccService.this.onStartOtaIfNecessary(slotId, new OtaStatusChangedCallback() {
+ @Override
+ public void onOtaStatusChanged(int status) {
+ try {
+ statusChangedCallback.onOtaStatusChanged(status);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void getOtaStatus(int slotId, IGetOtaStatusCallback callback) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int status = EuiccService.this.onGetOtaStatus(slotId);
+ try {
+ callback.onSuccess(status);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
+ }
+
+ @Override
public void getDownloadableSubscriptionMetadata(int slotId,
DownloadableSubscription subscription,
boolean forceDeactivateSim,
diff --git a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
index 5a24492..e2171ae 100644
--- a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
+++ b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
@@ -16,16 +16,19 @@
package android.service.euicc;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.euicc.DownloadableSubscription;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Result of a {@link EuiccService#onGetDefaultDownloadableSubscriptionList} operation.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class GetDefaultDownloadableSubscriptionListResult implements Parcelable {
public static final Creator<GetDefaultDownloadableSubscriptionListResult> CREATOR =
@@ -42,20 +45,35 @@
};
/**
- * Result of the operation.
+ * @hide
+ * @deprecated - Do no use. Use getResult() instead.
+ */
+ @Deprecated
+ public final int result;
+
+ @Nullable
+ private final DownloadableSubscription[] mSubscriptions;
+
+ /**
+ * Gets the result of the operation.
*
* <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
* implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
*/
- public final int result;
+ public int getResult() {
+ return result;
+ }
/**
- * The available {@link DownloadableSubscription}s (with filled-in metadata).
+ * Gets the available {@link DownloadableSubscription}s (with filled-in metadata).
*
* <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}.
*/
@Nullable
- public final DownloadableSubscription[] subscriptions;
+ public List<DownloadableSubscription> getDownloadableSubscriptions() {
+ if (mSubscriptions == null) return null;
+ return Arrays.asList(mSubscriptions);
+ }
/**
* Construct a new {@link GetDefaultDownloadableSubscriptionListResult}.
@@ -70,25 +88,25 @@
@Nullable DownloadableSubscription[] subscriptions) {
this.result = result;
if (this.result == EuiccService.RESULT_OK) {
- this.subscriptions = subscriptions;
+ this.mSubscriptions = subscriptions;
} else {
if (subscriptions != null) {
throw new IllegalArgumentException(
"Error result with non-null subscriptions: " + result);
}
- this.subscriptions = null;
+ this.mSubscriptions = null;
}
}
private GetDefaultDownloadableSubscriptionListResult(Parcel in) {
this.result = in.readInt();
- this.subscriptions = in.createTypedArray(DownloadableSubscription.CREATOR);
+ this.mSubscriptions = in.createTypedArray(DownloadableSubscription.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
- dest.writeTypedArray(subscriptions, flags);
+ dest.writeTypedArray(mSubscriptions, flags);
}
@Override
diff --git a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
index de8a307..1edb539 100644
--- a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
+++ b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
@@ -16,6 +16,7 @@
package android.service.euicc;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.euicc.DownloadableSubscription;
@@ -23,9 +24,8 @@
/**
* Result of a {@link EuiccService#onGetDownloadableSubscriptionMetadata} operation.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class GetDownloadableSubscriptionMetadataResult implements Parcelable {
public static final Creator<GetDownloadableSubscriptionMetadataResult> CREATOR =
@@ -42,20 +42,34 @@
};
/**
- * Result of the operation.
+ * @hide
+ * @deprecated - Do no use. Use getResult() instead.
+ */
+ @Deprecated
+ public final int result;
+
+ @Nullable
+ private final DownloadableSubscription mSubscription;
+
+ /**
+ * Gets the result of the operation.
*
* <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
* implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
*/
- public final int result;
+ public int getResult() {
+ return result;
+ }
/**
- * The {@link DownloadableSubscription} with filled-in metadata.
+ * Gets the {@link DownloadableSubscription} with filled-in metadata.
*
* <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}.
*/
@Nullable
- public final DownloadableSubscription subscription;
+ public DownloadableSubscription getDownloadableSubscription() {
+ return mSubscription;
+ }
/**
* Construct a new {@link GetDownloadableSubscriptionMetadataResult}.
@@ -70,25 +84,25 @@
@Nullable DownloadableSubscription subscription) {
this.result = result;
if (this.result == EuiccService.RESULT_OK) {
- this.subscription = subscription;
+ this.mSubscription = subscription;
} else {
if (subscription != null) {
throw new IllegalArgumentException(
"Error result with non-null subscription: " + result);
}
- this.subscription = null;
+ this.mSubscription = null;
}
}
private GetDownloadableSubscriptionMetadataResult(Parcel in) {
this.result = in.readInt();
- this.subscription = in.readTypedObject(DownloadableSubscription.CREATOR);
+ this.mSubscription = in.readTypedObject(DownloadableSubscription.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
- dest.writeTypedObject(this.subscription, flags);
+ dest.writeTypedObject(this.mSubscription, flags);
}
@Override
diff --git a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java
index 7ad8488..464d136 100644
--- a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java
+++ b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java
@@ -16,15 +16,18 @@
package android.service.euicc;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Result of a {@link EuiccService#onGetEuiccProfileInfoList} operation.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class GetEuiccProfileInfoListResult implements Parcelable {
public static final Creator<GetEuiccProfileInfoListResult> CREATOR =
@@ -41,19 +44,38 @@
};
/**
- * Result of the operation.
+ * @hide
+ * @deprecated - Do no use. Use getResult() instead.
+ */
+ @Deprecated
+ public final int result;
+
+ @Nullable
+ private final EuiccProfileInfo[] mProfiles;
+
+ private final boolean mIsRemovable;
+
+ /**
+ * Gets the result of the operation.
*
* <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
* implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
*/
- public final int result;
+ public int getResult() {
+ return result;
+ }
- /** The profile list (only upon success). */
+ /** Gets the profile list (only upon success). */
@Nullable
- public final EuiccProfileInfo[] profiles;
+ public List<EuiccProfileInfo> getProfiles() {
+ if (mProfiles == null) return null;
+ return Arrays.asList(mProfiles);
+ }
- /** Whether the eUICC is removable. */
- public final boolean isRemovable;
+ /** Gets whether the eUICC is removable. */
+ public boolean getIsRemovable() {
+ return mIsRemovable;
+ }
/**
* Construct a new {@link GetEuiccProfileInfoListResult}.
@@ -71,30 +93,29 @@
public GetEuiccProfileInfoListResult(
int result, @Nullable EuiccProfileInfo[] profiles, boolean isRemovable) {
this.result = result;
- this.isRemovable = isRemovable;
+ this.mIsRemovable = isRemovable;
if (this.result == EuiccService.RESULT_OK) {
- this.profiles = profiles;
+ this.mProfiles = profiles;
} else {
if (profiles != null) {
throw new IllegalArgumentException(
"Error result with non-null profiles: " + result);
}
- this.profiles = null;
+ this.mProfiles = null;
}
-
}
private GetEuiccProfileInfoListResult(Parcel in) {
this.result = in.readInt();
- this.profiles = in.createTypedArray(EuiccProfileInfo.CREATOR);
- this.isRemovable = in.readBoolean();
+ this.mProfiles = in.createTypedArray(EuiccProfileInfo.CREATOR);
+ this.mIsRemovable = in.readBoolean();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
- dest.writeTypedArray(profiles, flags);
- dest.writeBoolean(isRemovable);
+ dest.writeTypedArray(mProfiles, flags);
+ dest.writeBoolean(mIsRemovable);
}
@Override
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index e10dd8c..45be527 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -24,6 +24,8 @@
import android.service.euicc.IGetEidCallback;
import android.service.euicc.IGetEuiccInfoCallback;
import android.service.euicc.IGetEuiccProfileInfoListCallback;
+import android.service.euicc.IGetOtaStatusCallback;
+import android.service.euicc.IOtaStatusChangedCallback;
import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
@@ -37,6 +39,8 @@
void getDownloadableSubscriptionMetadata(int slotId, in DownloadableSubscription subscription,
boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback);
void getEid(int slotId, in IGetEidCallback callback);
+ void getOtaStatus(int slotId, in IGetOtaStatusCallback callback);
+ void startOtaIfNecessary(int slotId, in IOtaStatusChangedCallback statusChangedCallback);
void getEuiccProfileInfoList(int slotId, in IGetEuiccProfileInfoListCallback callback);
void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim,
in IGetDefaultDownloadableSubscriptionListCallback callback);
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/service/euicc/IGetOtaStatusCallback.aidl
similarity index 83%
rename from core/java/android/os/Seccomp.java
rename to core/java/android/service/euicc/IGetOtaStatusCallback.aidl
index f14e93f..f667888 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/service/euicc/IGetOtaStatusCallback.aidl
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package android.os;
+package android.service.euicc;
-/**
- * @hide
- */
-public final class Seccomp {
- public static final native void setPolicy();
-}
+/** @hide */
+oneway interface IGetOtaStatusCallback {
+ void onSuccess(int status);
+}
\ No newline at end of file
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
similarity index 82%
copy from core/java/android/os/Seccomp.java
copy to core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
index f14e93f..caec75f 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package android.os;
+package android.service.euicc;
-/**
- * @hide
- */
-public final class Seccomp {
- public static final native void setPolicy();
-}
+/** @hide */
+oneway interface IOtaStatusChangedCallback {
+ void onOtaStatusChanged(int status);
+}
\ No newline at end of file
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index bbd9c9c..562ae7a 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -358,7 +358,7 @@
}
/**
- * Return the current time in YYYYMMDDTHHMMSS<tz> format
+ * Return the current time in YYYYMMDDTHHMMSS<tz> format
*/
@Override
public String toString() {
@@ -738,6 +738,7 @@
* <p>
* You should also use <tt>toMillis(false)</tt> if you want
* to read back the same milliseconds that you set with {@link #set(long)}
+ * or {@link #set(Time)} or after parsing a date string.
*
* <p>
* This method can return {@code -1} when the date / time fields have been
@@ -745,8 +746,6 @@
* For example, when daylight savings transitions cause an hour to be
* skipped: times within that hour will return {@code -1} if isDst =
* {@code -1}.
- *
- * or {@link #set(Time)} or after parsing a date string.
*/
public long toMillis(boolean ignoreDst) {
calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index b7099b6..13de172 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -120,6 +120,14 @@
public static final int DENSITY_420 = 420;
/**
+ * Intermediate density for screens that sit somewhere between
+ * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+ * This is not a density that applications should target, instead relying
+ * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+ */
+ public static final int DENSITY_440 = 440;
+
+ /**
* Standard quantized DPI for extra-extra-high-density screens.
*/
public static final int DENSITY_XXHIGH = 480;
diff --git a/core/java/android/util/MutableBoolean.java b/core/java/android/util/MutableBoolean.java
index ed837ab..44e73cc 100644
--- a/core/java/android/util/MutableBoolean.java
+++ b/core/java/android/util/MutableBoolean.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableBoolean {
public boolean value;
diff --git a/core/java/android/util/MutableByte.java b/core/java/android/util/MutableByte.java
index cc6b00a..b9ec25d 100644
--- a/core/java/android/util/MutableByte.java
+++ b/core/java/android/util/MutableByte.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableByte {
public byte value;
diff --git a/core/java/android/util/MutableChar.java b/core/java/android/util/MutableChar.java
index 9a2e2bc..9f7a9ae 100644
--- a/core/java/android/util/MutableChar.java
+++ b/core/java/android/util/MutableChar.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableChar {
public char value;
diff --git a/core/java/android/util/MutableDouble.java b/core/java/android/util/MutableDouble.java
index bd7329a..56e539b 100644
--- a/core/java/android/util/MutableDouble.java
+++ b/core/java/android/util/MutableDouble.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableDouble {
public double value;
diff --git a/core/java/android/util/MutableFloat.java b/core/java/android/util/MutableFloat.java
index e6f2d7d..6d7ad59 100644
--- a/core/java/android/util/MutableFloat.java
+++ b/core/java/android/util/MutableFloat.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableFloat {
public float value;
diff --git a/core/java/android/util/MutableInt.java b/core/java/android/util/MutableInt.java
index a3d8606..bb24566 100644
--- a/core/java/android/util/MutableInt.java
+++ b/core/java/android/util/MutableInt.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableInt {
public int value;
diff --git a/core/java/android/util/MutableLong.java b/core/java/android/util/MutableLong.java
index 575068e..86e70e1 100644
--- a/core/java/android/util/MutableLong.java
+++ b/core/java/android/util/MutableLong.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableLong {
public long value;
diff --git a/core/java/android/util/MutableShort.java b/core/java/android/util/MutableShort.java
index 48fb232..b94ab07 100644
--- a/core/java/android/util/MutableShort.java
+++ b/core/java/android/util/MutableShort.java
@@ -17,7 +17,9 @@
package android.util;
/**
+ * @deprecated This class will be removed from a future version of the Android API.
*/
+@Deprecated
public final class MutableShort {
public short value;
diff --git a/core/java/android/webkit/FindAddress.java b/core/java/android/webkit/FindAddress.java
new file mode 100644
index 0000000..31b2427
--- /dev/null
+++ b/core/java/android/webkit/FindAddress.java
@@ -0,0 +1,478 @@
+/*
+ * 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.webkit;
+
+import java.util.Locale;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Java implementation of legacy WebView.findAddress algorithm.
+ *
+ * @hide
+ */
+class FindAddress {
+ static class ZipRange {
+ int mLow;
+ int mHigh;
+ int mException1;
+ int mException2;
+ ZipRange(int low, int high, int exception1, int exception2) {
+ mLow = low;
+ mHigh = high;
+ mException1 = exception1;
+ mException2 = exception1;
+ }
+ boolean matches(String zipCode) {
+ int prefix = Integer.parseInt(zipCode.substring(0, 2));
+ return (mLow <= prefix && prefix <= mHigh) || prefix == mException1
+ || prefix == mException2;
+ }
+ }
+
+ // Addresses consist of at least this many words, not including state and zip code.
+ private static final int MIN_ADDRESS_WORDS = 4;
+
+ // Adddresses consist of at most this many words, not including state and zip code.
+ private static final int MAX_ADDRESS_WORDS = 14;
+
+ // Addresses consist of at most this many lines.
+ private static final int MAX_ADDRESS_LINES = 5;
+
+ // No words in an address are longer than this many characters.
+ private static final int kMaxAddressNameWordLength = 25;
+
+ // Location name should be in the first MAX_LOCATION_NAME_DISTANCE words
+ private static final int MAX_LOCATION_NAME_DISTANCE = 5;
+
+ private static final ZipRange[] sStateZipCodeRanges = {
+ new ZipRange(99, 99, -1, -1), // AK Alaska.
+ new ZipRange(35, 36, -1, -1), // AL Alabama.
+ new ZipRange(71, 72, -1, -1), // AR Arkansas.
+ new ZipRange(96, 96, -1, -1), // AS American Samoa.
+ new ZipRange(85, 86, -1, -1), // AZ Arizona.
+ new ZipRange(90, 96, -1, -1), // CA California.
+ new ZipRange(80, 81, -1, -1), // CO Colorado.
+ new ZipRange(6, 6, -1, -1), // CT Connecticut.
+ new ZipRange(20, 20, -1, -1), // DC District of Columbia.
+ new ZipRange(19, 19, -1, -1), // DE Delaware.
+ new ZipRange(32, 34, -1, -1), // FL Florida.
+ new ZipRange(96, 96, -1, -1), // FM Federated States of Micronesia.
+ new ZipRange(30, 31, -1, -1), // GA Georgia.
+ new ZipRange(96, 96, -1, -1), // GU Guam.
+ new ZipRange(96, 96, -1, -1), // HI Hawaii.
+ new ZipRange(50, 52, -1, -1), // IA Iowa.
+ new ZipRange(83, 83, -1, -1), // ID Idaho.
+ new ZipRange(60, 62, -1, -1), // IL Illinois.
+ new ZipRange(46, 47, -1, -1), // IN Indiana.
+ new ZipRange(66, 67, 73, -1), // KS Kansas.
+ new ZipRange(40, 42, -1, -1), // KY Kentucky.
+ new ZipRange(70, 71, -1, -1), // LA Louisiana.
+ new ZipRange(1, 2, -1, -1), // MA Massachusetts.
+ new ZipRange(20, 21, -1, -1), // MD Maryland.
+ new ZipRange(3, 4, -1, -1), // ME Maine.
+ new ZipRange(96, 96, -1, -1), // MH Marshall Islands.
+ new ZipRange(48, 49, -1, -1), // MI Michigan.
+ new ZipRange(55, 56, -1, -1), // MN Minnesota.
+ new ZipRange(63, 65, -1, -1), // MO Missouri.
+ new ZipRange(96, 96, -1, -1), // MP Northern Mariana Islands.
+ new ZipRange(38, 39, -1, -1), // MS Mississippi.
+ new ZipRange(55, 56, -1, -1), // MT Montana.
+ new ZipRange(27, 28, -1, -1), // NC North Carolina.
+ new ZipRange(58, 58, -1, -1), // ND North Dakota.
+ new ZipRange(68, 69, -1, -1), // NE Nebraska.
+ new ZipRange(3, 4, -1, -1), // NH New Hampshire.
+ new ZipRange(7, 8, -1, -1), // NJ New Jersey.
+ new ZipRange(87, 88, 86, -1), // NM New Mexico.
+ new ZipRange(88, 89, 96, -1), // NV Nevada.
+ new ZipRange(10, 14, 0, 6), // NY New York.
+ new ZipRange(43, 45, -1, -1), // OH Ohio.
+ new ZipRange(73, 74, -1, -1), // OK Oklahoma.
+ new ZipRange(97, 97, -1, -1), // OR Oregon.
+ new ZipRange(15, 19, -1, -1), // PA Pennsylvania.
+ new ZipRange(6, 6, 0, 9), // PR Puerto Rico.
+ new ZipRange(96, 96, -1, -1), // PW Palau.
+ new ZipRange(2, 2, -1, -1), // RI Rhode Island.
+ new ZipRange(29, 29, -1, -1), // SC South Carolina.
+ new ZipRange(57, 57, -1, -1), // SD South Dakota.
+ new ZipRange(37, 38, -1, -1), // TN Tennessee.
+ new ZipRange(75, 79, 87, 88), // TX Texas.
+ new ZipRange(84, 84, -1, -1), // UT Utah.
+ new ZipRange(22, 24, 20, -1), // VA Virginia.
+ new ZipRange(6, 9, -1, -1), // VI Virgin Islands.
+ new ZipRange(5, 5, -1, -1), // VT Vermont.
+ new ZipRange(98, 99, -1, -1), // WA Washington.
+ new ZipRange(53, 54, -1, -1), // WI Wisconsin.
+ new ZipRange(24, 26, -1, -1), // WV West Virginia.
+ new ZipRange(82, 83, -1, -1) // WY Wyoming.
+ };
+
+ // Newlines
+ private static final String NL = "\n\u000B\u000C\r\u0085\u2028\u2029";
+
+ // Space characters
+ private static final String SP = "\u0009\u0020\u00A0\u1680\u2000\u2001"
+ + "\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F"
+ + "\u205F\u3000";
+
+ // Whitespace
+ private static final String WS = SP + NL;
+
+ // Characters that are considered word delimiters.
+ private static final String WORD_DELIM = ",*\u2022" + WS;
+
+ // Lookahead for word end.
+ private static final String WORD_END = "(?=[" + WORD_DELIM + "]|$)";
+
+ // Address words are a sequence of non-delimiter characters.
+ private static final Pattern sWordRe =
+ Pattern.compile("[^" + WORD_DELIM + "]+" + WORD_END, Pattern.CASE_INSENSITIVE);
+
+ // Characters that are considered suffix delimiters for house numbers.
+ private static final String HOUSE_POST_DELIM = ",\"'" + WS;
+
+ // Lookahead for house end.
+ private static final String HOUSE_END = "(?=[" + HOUSE_POST_DELIM + "]|$)";
+
+ // Characters that are considered prefix delimiters for house numbers.
+ private static final String HOUSE_PRE_DELIM = ":" + HOUSE_POST_DELIM;
+
+ // A house number component is "one" or a number, optionally
+ // followed by a single alphabetic character, or
+ private static final String HOUSE_COMPONENT = "(?:one|\\d+([a-z](?=[^a-z]|$)|st|nd|rd|th)?)";
+
+ // House numbers are a repetition of |HOUSE_COMPONENT|, separated by -, and followed by
+ // a delimiter character.
+ private static final Pattern sHouseNumberRe =
+ Pattern.compile(HOUSE_COMPONENT + "(?:-" + HOUSE_COMPONENT + ")*" + HOUSE_END,
+ Pattern.CASE_INSENSITIVE);
+
+ // XXX: do we want to accept whitespace other than 0x20 in state names?
+ private static final Pattern sStateRe = Pattern.compile("(?:"
+ + "(ak|alaska)|"
+ + "(al|alabama)|"
+ + "(ar|arkansas)|"
+ + "(as|american[" + SP + "]+samoa)|"
+ + "(az|arizona)|"
+ + "(ca|california)|"
+ + "(co|colorado)|"
+ + "(ct|connecticut)|"
+ + "(dc|district[" + SP + "]+of[" + SP + "]+columbia)|"
+ + "(de|delaware)|"
+ + "(fl|florida)|"
+ + "(fm|federated[" + SP + "]+states[" + SP + "]+of[" + SP + "]+micronesia)|"
+ + "(ga|georgia)|"
+ + "(gu|guam)|"
+ + "(hi|hawaii)|"
+ + "(ia|iowa)|"
+ + "(id|idaho)|"
+ + "(il|illinois)|"
+ + "(in|indiana)|"
+ + "(ks|kansas)|"
+ + "(ky|kentucky)|"
+ + "(la|louisiana)|"
+ + "(ma|massachusetts)|"
+ + "(md|maryland)|"
+ + "(me|maine)|"
+ + "(mh|marshall[" + SP + "]+islands)|"
+ + "(mi|michigan)|"
+ + "(mn|minnesota)|"
+ + "(mo|missouri)|"
+ + "(mp|northern[" + SP + "]+mariana[" + SP + "]+islands)|"
+ + "(ms|mississippi)|"
+ + "(mt|montana)|"
+ + "(nc|north[" + SP + "]+carolina)|"
+ + "(nd|north[" + SP + "]+dakota)|"
+ + "(ne|nebraska)|"
+ + "(nh|new[" + SP + "]+hampshire)|"
+ + "(nj|new[" + SP + "]+jersey)|"
+ + "(nm|new[" + SP + "]+mexico)|"
+ + "(nv|nevada)|"
+ + "(ny|new[" + SP + "]+york)|"
+ + "(oh|ohio)|"
+ + "(ok|oklahoma)|"
+ + "(or|oregon)|"
+ + "(pa|pennsylvania)|"
+ + "(pr|puerto[" + SP + "]+rico)|"
+ + "(pw|palau)|"
+ + "(ri|rhode[" + SP + "]+island)|"
+ + "(sc|south[" + SP + "]+carolina)|"
+ + "(sd|south[" + SP + "]+dakota)|"
+ + "(tn|tennessee)|"
+ + "(tx|texas)|"
+ + "(ut|utah)|"
+ + "(va|virginia)|"
+ + "(vi|virgin[" + SP + "]+islands)|"
+ + "(vt|vermont)|"
+ + "(wa|washington)|"
+ + "(wi|wisconsin)|"
+ + "(wv|west[" + SP + "]+virginia)|"
+ + "(wy|wyoming)"
+ + ")" + WORD_END,
+ Pattern.CASE_INSENSITIVE);
+
+ private static final Pattern sLocationNameRe = Pattern.compile("(?:"
+ + "alley|annex|arcade|ave[.]?|avenue|alameda|bayou|"
+ + "beach|bend|bluffs?|bottom|boulevard|branch|bridge|"
+ + "brooks?|burgs?|bypass|broadway|camino|camp|canyon|"
+ + "cape|causeway|centers?|circles?|cliffs?|club|common|"
+ + "corners?|course|courts?|coves?|creek|crescent|crest|"
+ + "crossing|crossroad|curve|circulo|dale|dam|divide|"
+ + "drives?|estates?|expressway|extensions?|falls?|ferry|"
+ + "fields?|flats?|fords?|forest|forges?|forks?|fort|"
+ + "freeway|gardens?|gateway|glens?|greens?|groves?|"
+ + "harbors?|haven|heights|highway|hills?|hollow|inlet|"
+ + "islands?|isle|junctions?|keys?|knolls?|lakes?|land|"
+ + "landing|lane|lights?|loaf|locks?|lodge|loop|mall|"
+ + "manors?|meadows?|mews|mills?|mission|motorway|mount|"
+ + "mountains?|neck|orchard|oval|overpass|parks?|"
+ + "parkways?|pass|passage|path|pike|pines?|plains?|"
+ + "plaza|points?|ports?|prairie|privada|radial|ramp|"
+ + "ranch|rapids?|rd[.]?|rest|ridges?|river|roads?|route|"
+ + "row|rue|run|shoals?|shores?|skyway|springs?|spurs?|"
+ + "squares?|station|stravenue|stream|st[.]?|streets?|"
+ + "summit|speedway|terrace|throughway|trace|track|"
+ + "trafficway|trail|tunnel|turnpike|underpass|unions?|"
+ + "valleys?|viaduct|views?|villages?|ville|vista|walks?|"
+ + "wall|ways?|wells?|xing|xrd)" + WORD_END,
+ Pattern.CASE_INSENSITIVE);
+
+ private static final Pattern sSuffixedNumberRe =
+ Pattern.compile("(\\d+)(st|nd|rd|th)", Pattern.CASE_INSENSITIVE);
+
+ private static final Pattern sZipCodeRe =
+ Pattern.compile("(?:\\d{5}(?:-\\d{4})?)" + WORD_END, Pattern.CASE_INSENSITIVE);
+
+ private static boolean checkHouseNumber(String houseNumber) {
+ // Make sure that there are at most 5 digits.
+ int digitCount = 0;
+ for (int i = 0; i < houseNumber.length(); ++i) {
+ if (Character.isDigit(houseNumber.charAt(i))) ++digitCount;
+ }
+ if (digitCount > 5) return false;
+
+ // Make sure that any ordinals are valid.
+ Matcher suffixMatcher = sSuffixedNumberRe.matcher(houseNumber);
+ while (suffixMatcher.find()) {
+ int num = Integer.parseInt(suffixMatcher.group(1));
+ if (num == 0) {
+ return false; // 0th is invalid.
+ }
+ String suffix = suffixMatcher.group(2).toLowerCase(Locale.getDefault());
+ switch (num % 10) {
+ case 1:
+ return suffix.equals(num % 100 == 11 ? "th" : "st");
+ case 2:
+ return suffix.equals(num % 100 == 12 ? "th" : "nd");
+ case 3:
+ return suffix.equals(num % 100 == 13 ? "th" : "rd");
+ default:
+ return suffix.equals("th");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Attempt to match a house number beginnning at position offset
+ * in content. The house number must be followed by a word
+ * delimiter or the end of the string, and if offset is non-zero,
+ * then it must also be preceded by a word delimiter.
+ *
+ * @return a MatchResult if a valid house number was found.
+ */
+ private static MatchResult matchHouseNumber(String content, int offset) {
+ if (offset > 0 && HOUSE_PRE_DELIM.indexOf(content.charAt(offset - 1)) == -1) return null;
+ Matcher matcher = sHouseNumberRe.matcher(content).region(offset, content.length());
+ if (matcher.lookingAt()) {
+ MatchResult matchResult = matcher.toMatchResult();
+ if (checkHouseNumber(matchResult.group(0))) return matchResult;
+ }
+ return null;
+ }
+
+ /**
+ * Attempt to match a US state beginnning at position offset in
+ * content. The matching state must be followed by a word
+ * delimiter or the end of the string, and if offset is non-zero,
+ * then it must also be preceded by a word delimiter.
+ *
+ * @return a MatchResult if a valid US state (or two letter code)
+ * was found.
+ */
+ private static MatchResult matchState(String content, int offset) {
+ if (offset > 0 && WORD_DELIM.indexOf(content.charAt(offset - 1)) == -1) return null;
+ Matcher stateMatcher = sStateRe.matcher(content).region(offset, content.length());
+ return stateMatcher.lookingAt() ? stateMatcher.toMatchResult() : null;
+ }
+
+ /**
+ * Test whether zipCode matches the U.S. zip code format (ddddd or
+ * ddddd-dddd) and is within the expected range, given that
+ * stateMatch is a match of sStateRe.
+ *
+ * @return true if zipCode is a valid zip code, is legal for the
+ * matched state, and is followed by a word delimiter or the end
+ * of the string.
+ */
+ private static boolean isValidZipCode(String zipCode, MatchResult stateMatch) {
+ if (stateMatch == null) return false;
+ // Work out the index of the state, based on which group matched.
+ int stateIndex = stateMatch.groupCount();
+ while (stateIndex > 0) {
+ if (stateMatch.group(stateIndex--) != null) break;
+ }
+ return sZipCodeRe.matcher(zipCode).matches()
+ && sStateZipCodeRanges[stateIndex].matches(zipCode);
+ }
+
+ /**
+ * Test whether location is one of the valid locations.
+ *
+ * @return true if location starts with a valid location name
+ * followed by a word delimiter or the end of the string.
+ */
+ private static boolean isValidLocationName(String location) {
+ return sLocationNameRe.matcher(location).matches();
+ }
+
+ /**
+ * Attempt to match a complete address in content, starting with
+ * houseNumberMatch.
+ *
+ * @param content The string to search.
+ * @param houseNumberMatch A matching house number to start extending.
+ * @return +ve: the end of the match
+ * +ve: the position to restart searching for house numbers, negated.
+ */
+ private static int attemptMatch(String content, MatchResult houseNumberMatch) {
+ int restartPos = -1;
+ int nonZipMatch = -1;
+ int it = houseNumberMatch.end();
+ int numLines = 1;
+ boolean consecutiveHouseNumbers = true;
+ boolean foundLocationName = false;
+ int wordCount = 1;
+ String lastWord = "";
+
+ Matcher matcher = sWordRe.matcher(content);
+
+ for (; it < content.length(); lastWord = matcher.group(0), it = matcher.end()) {
+ if (!matcher.find(it)) {
+ // No more words in the input sequence.
+ return -content.length();
+ }
+ if (matcher.end() - matcher.start() > kMaxAddressNameWordLength) {
+ // Word is too long to be part of an address. Fail.
+ return -matcher.end();
+ }
+
+ // Count the number of newlines we just consumed.
+ while (it < matcher.start()) {
+ if (NL.indexOf(content.charAt(it++)) != -1) ++numLines;
+ }
+
+ // Consumed too many lines. Fail.
+ if (numLines > MAX_ADDRESS_LINES) break;
+
+ // Consumed too many words. Fail.
+ if (++wordCount > MAX_ADDRESS_WORDS) break;
+
+ if (matchHouseNumber(content, it) != null) {
+ if (consecutiveHouseNumbers && numLines > 1) {
+ // Last line ended with a number, and this this line starts with one.
+ // Restart at this number.
+ return -it;
+ }
+ // Remember the position of this match as the restart position.
+ if (restartPos == -1) restartPos = it;
+ continue;
+ }
+
+ consecutiveHouseNumbers = false;
+
+ if (isValidLocationName(matcher.group(0))) {
+ foundLocationName = true;
+ continue;
+ }
+
+ if (wordCount == MAX_LOCATION_NAME_DISTANCE && !foundLocationName) {
+ // Didn't find a location name in time. Fail.
+ it = matcher.end();
+ break;
+ }
+
+ if (foundLocationName && wordCount > MIN_ADDRESS_WORDS) {
+ // We can now attempt to match a state.
+ MatchResult stateMatch = matchState(content, it);
+ if (stateMatch != null) {
+ if (lastWord.equals("et") && stateMatch.group(0).equals("al")) {
+ // Reject "et al" as a false postitive.
+ it = stateMatch.end();
+ break;
+ }
+
+ // At this point we've matched a state; try to match a zip code after it.
+ Matcher zipMatcher = sWordRe.matcher(content);
+ if (zipMatcher.find(stateMatch.end())
+ && isValidZipCode(zipMatcher.group(0), stateMatch)) {
+ return zipMatcher.end();
+ }
+ // The content ends with a state but no zip
+ // code. This is a legal match according to the
+ // documentation. N.B. This differs from the
+ // original c++ implementation, which only allowed
+ // the zip code to be optional at the end of the
+ // string, which presumably is a bug. Now we
+ // prefer to find a match with a zip code, but
+ // remember non-zip matches and return them if
+ // necessary.
+ nonZipMatch = stateMatch.end();
+ }
+ }
+ }
+
+ if (nonZipMatch > 0) return nonZipMatch;
+
+ return -(restartPos > 0 ? restartPos : it);
+ }
+
+ /**
+ * Return the first matching address in content.
+ *
+ * @param content The string to search.
+ * @return The first valid address, or null if no address was matched.
+ */
+ static String findAddress(String content) {
+ Matcher houseNumberMatcher = sHouseNumberRe.matcher(content);
+ int start = 0;
+ while (houseNumberMatcher.find(start)) {
+ if (checkHouseNumber(houseNumberMatcher.group(0))) {
+ start = houseNumberMatcher.start();
+ int end = attemptMatch(content, houseNumberMatcher);
+ if (end > 0) {
+ return content.substring(start, end);
+ }
+ start = -end;
+ } else {
+ start = houseNumberMatcher.end();
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 637b60e..244b6bd 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1797,9 +1797,10 @@
* @return the address, or if no address is found, null
*/
public static String findAddress(String addr) {
- // TODO: Rewrite this in Java so it is not needed to start up chromium
- // Could also be deprecated
- return getFactory().getStatics().findAddress(addr);
+ if (addr == null) {
+ throw new NullPointerException("addr is null");
+ }
+ return FindAddress.findAddress(addr);
}
/**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 668cfba..d06a20b 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -463,7 +463,7 @@
*/
public static int onWebViewProviderChanged(PackageInfo packageInfo) {
String[] nativeLibs = null;
- String originalSourceDir = packageInfo.applicationInfo.sourceDir;
+ ApplicationInfo originalAppInfo = new ApplicationInfo(packageInfo.applicationInfo);
try {
fixupStubApplicationInfo(packageInfo.applicationInfo,
AppGlobals.getInitialApplication().getPackageManager());
@@ -474,7 +474,7 @@
Log.e(LOGTAG, "error preparing webview native library", t);
}
- WebViewZygote.onWebViewProviderChanged(packageInfo, originalSourceDir);
+ WebViewZygote.onWebViewProviderChanged(packageInfo, originalAppInfo);
return prepareWebViewInSystemServer(nativeLibs);
}
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 0204dff..d2923c4 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -17,31 +17,25 @@
package android.webkit;
import android.app.LoadedApk;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Build;
-import android.os.SystemService;
+import android.os.ChildZygoteProcess;
+import android.os.Process;
import android.os.ZygoteProcess;
import android.text.TextUtils;
-import android.util.AndroidRuntimeException;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.TimeoutException;
/** @hide */
public class WebViewZygote {
private static final String LOGTAG = "WebViewZygote";
- private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32";
- private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64";
- private static final String WEBVIEW_ZYGOTE_SOCKET = "webview_zygote";
-
/**
* Lock object that protects all other static members.
*/
@@ -52,14 +46,7 @@
* is not running or is not connected.
*/
@GuardedBy("sLock")
- private static ZygoteProcess sZygote;
-
- /**
- * Variable that allows us to determine whether the WebView zygote Service has already been
- * started.
- */
- @GuardedBy("sLock")
- private static boolean sStartedService = false;
+ private static ChildZygoteProcess sZygote;
/**
* Information about the selected WebView package. This is set from #onWebViewProviderChanged().
@@ -68,11 +55,11 @@
private static PackageInfo sPackage;
/**
- * Cache key for the selected WebView package's classloader. This is set from
+ * Original ApplicationInfo for the selected WebView package before stub fixup. This is set from
* #onWebViewProviderChanged().
*/
@GuardedBy("sLock")
- private static String sPackageCacheKey;
+ private static ApplicationInfo sPackageOriginalAppInfo;
/**
* Flag for whether multi-process WebView is enabled. If this is false, the zygote
@@ -85,7 +72,7 @@
synchronized (sLock) {
if (sZygote != null) return sZygote;
- waitForServiceStartAndConnect();
+ connectToZygoteIfNeededLocked();
return sZygote;
}
}
@@ -107,82 +94,43 @@
sMultiprocessEnabled = enabled;
// When toggling between multi-process being on/off, start or stop the
- // service. If it is enabled and the zygote is not yet started, bring up the service.
- // Otherwise, bring down the service. The name may be null if the package
- // information has not yet been resolved.
- final String serviceName = getServiceNameLocked();
- if (serviceName == null) return;
-
+ // zygote. If it is enabled and the zygote is not yet started, launch it.
+ // Otherwise, kill it. The name may be null if the package information has
+ // not yet been resolved.
if (enabled) {
- if (!sStartedService) {
- SystemService.start(serviceName);
- sStartedService = true;
- }
+ connectToZygoteIfNeededLocked();
} else {
- SystemService.stop(serviceName);
- sStartedService = false;
- sZygote = null;
+ stopZygoteLocked();
}
}
}
- public static void onWebViewProviderChanged(PackageInfo packageInfo, String cacheKey) {
+ public static void onWebViewProviderChanged(PackageInfo packageInfo,
+ ApplicationInfo originalAppInfo) {
synchronized (sLock) {
sPackage = packageInfo;
- sPackageCacheKey = cacheKey;
+ sPackageOriginalAppInfo = originalAppInfo;
// If multi-process is not enabled, then do not start the zygote service.
if (!sMultiprocessEnabled) {
return;
}
- final String serviceName = getServiceNameLocked();
- sZygote = null;
-
- // The service may enter the RUNNING state before it opens the socket,
- // so connectToZygoteIfNeededLocked() may still fail.
- if (SystemService.isStopped(serviceName)) {
- SystemService.start(serviceName);
- } else {
- SystemService.restart(serviceName);
- }
- sStartedService = true;
- }
- }
-
- private static void waitForServiceStartAndConnect() {
- if (!sStartedService) {
- throw new AndroidRuntimeException("Tried waiting for the WebView Zygote Service to " +
- "start running without first starting the service.");
- }
-
- String serviceName;
- synchronized (sLock) {
- serviceName = getServiceNameLocked();
- }
- try {
- SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
- } catch (TimeoutException e) {
- Log.e(LOGTAG, "Timed out waiting for " + serviceName);
- return;
- }
-
- synchronized (sLock) {
- connectToZygoteIfNeededLocked();
+ stopZygoteLocked();
}
}
@GuardedBy("sLock")
- private static String getServiceNameLocked() {
- if (sPackage == null)
- return null;
-
- if (Arrays.asList(Build.SUPPORTED_64_BIT_ABIS).contains(
- sPackage.applicationInfo.primaryCpuAbi)) {
- return WEBVIEW_ZYGOTE_SERVICE_64;
+ private static void stopZygoteLocked() {
+ if (sZygote != null) {
+ // Close the connection and kill the zygote process. This will not cause
+ // child processes to be killed by itself. But if this is called in response to
+ // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater
+ // will kill all processes that depend on the WebView package.
+ sZygote.close();
+ Process.killProcess(sZygote.getPid());
+ sZygote = null;
}
-
- return WEBVIEW_ZYGOTE_SERVICE_32;
}
@GuardedBy("sLock")
@@ -196,14 +144,17 @@
return;
}
- final String serviceName = getServiceNameLocked();
- if (!SystemService.isRunning(serviceName)) {
- Log.e(LOGTAG, serviceName + " is not running");
- return;
- }
-
try {
- sZygote = new ZygoteProcess(WEBVIEW_ZYGOTE_SOCKET, null);
+ sZygote = Process.zygoteProcess.startChildZygote(
+ "com.android.internal.os.WebViewZygoteInit",
+ "webview_zygote",
+ Process.WEBVIEW_ZYGOTE_UID,
+ Process.WEBVIEW_ZYGOTE_UID,
+ null, // gids
+ 0, // runtimeFlags
+ "webview_zygote", // seInfo
+ sPackage.applicationInfo.primaryCpuAbi, // abi
+ null); // instructionSet
// All the work below is usually done by LoadedApk, but the zygote can't talk to
// PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so
@@ -218,14 +169,21 @@
final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
TextUtils.join(File.pathSeparator, zipPaths);
- ZygoteProcess.waitForConnectionToZygote(WEBVIEW_ZYGOTE_SOCKET);
+ // In the case where the ApplicationInfo has been modified by the stub WebView,
+ // we need to use the original ApplicationInfo to determine what the original classpath
+ // would have been to use as a cache key.
+ LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null);
+ final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) :
+ TextUtils.join(File.pathSeparator, zipPaths);
+
+ ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress());
Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
- sZygote.preloadPackageForAbi(zip, librarySearchPath, sPackageCacheKey,
+ sZygote.preloadPackageForAbi(zip, librarySearchPath, cacheKey,
Build.SUPPORTED_ABIS[0]);
} catch (Exception e) {
- Log.e(LOGTAG, "Error connecting to " + serviceName, e);
- sZygote = null;
+ Log.e(LOGTAG, "Error connecting to webview zygote", e);
+ stopZygoteLocked();
}
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 91e2f7d..6c7455d 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3866,6 +3866,7 @@
private void onTouchDown(MotionEvent ev) {
mHasPerformedLongPress = false;
mActivePointerId = ev.getPointerId(0);
+ hideSelector();
if (mTouchMode == TOUCH_MODE_OVERFLING) {
// Stopped the fling. It is a scroll.
@@ -5226,17 +5227,21 @@
}
mRecycler.fullyDetachScrapViews();
+ boolean selectorOnScreen = false;
if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {
final int childIndex = mSelectedPosition - mFirstPosition;
if (childIndex >= 0 && childIndex < getChildCount()) {
positionSelector(mSelectedPosition, getChildAt(childIndex));
+ selectorOnScreen = true;
}
} else if (mSelectorPosition != INVALID_POSITION) {
final int childIndex = mSelectorPosition - mFirstPosition;
if (childIndex >= 0 && childIndex < getChildCount()) {
- positionSelector(INVALID_POSITION, getChildAt(childIndex));
+ positionSelector(mSelectorPosition, getChildAt(childIndex));
+ selectorOnScreen = true;
}
- } else {
+ }
+ if (!selectorOnScreen) {
mSelectorRect.setEmpty();
}
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 77cfc2fc..96d3baf 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -411,6 +411,9 @@
mContext.unbindService(mConnection);
mConnection.destroy();
}
+ if (mAfterCompute != null) {
+ mAfterCompute.afterCompute();
+ }
if (DEBUG) {
Log.d(TAG, "Unbinded Resolver Ranker.");
}
@@ -573,7 +576,6 @@
if (DEBUG) {
Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction");
}
- return;
} else {
try {
mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e923223..97500f2 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.dex.DexMetadataHelper;
import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
@@ -663,6 +664,9 @@
}
}
+ // Include raw dex metadata files
+ sizeBytes += DexMetadataHelper.getPackageDexMetadataSize(pkg);
+
// Include all relevant native code
sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride);
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 3d3e148..902bd12 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -31,13 +31,17 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
+import com.google.android.collect.Lists;
import libcore.io.IoUtils;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.net.ProtocolException;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -55,6 +59,8 @@
// Used for correct stats accounting on clatd interfaces.
private static final int IPV4V6_HEADER_DELTA = 20;
+ /** Path to {@code /proc/net/dev}. */
+ private final File mStatsIfaceDev;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
private final File mStatsXtIfaceAll;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
@@ -62,6 +68,8 @@
/** Path to {@code /proc/net/xt_qtaguid/stats}. */
private final File mStatsXtUid;
+ private boolean mUseBpfStats;
+
// TODO: to improve testability and avoid global state, do not use a static variable.
@GuardedBy("sStackedIfaces")
private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();
@@ -77,14 +85,54 @@
}
public NetworkStatsFactory() {
- this(new File("/proc/"));
+ this(new File("/proc/"), new File("/sys/fs/bpf/traffic_uid_stats_map").exists());
}
@VisibleForTesting
- public NetworkStatsFactory(File procRoot) {
+ public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
+ mStatsIfaceDev = new File(procRoot, "net/dev");
mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
+ mUseBpfStats = useBpfStats;
+ }
+
+ @VisibleForTesting
+ public NetworkStats readNetworkStatsIfaceDev() throws IOException {
+ final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(mStatsIfaceDev));
+
+ // skip first two header lines
+ reader.readLine();
+ reader.readLine();
+
+ // parse remaining lines
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] values = line.trim().split("\\:?\\s+");
+ entry.iface = values[0];
+ entry.uid = UID_ALL;
+ entry.set = SET_ALL;
+ entry.tag = TAG_NONE;
+ entry.rxBytes = Long.parseLong(values[1]);
+ entry.rxPackets = Long.parseLong(values[2]);
+ entry.txBytes = Long.parseLong(values[9]);
+ entry.txPackets = Long.parseLong(values[10]);
+ stats.addValues(entry);
+ }
+ } catch (NullPointerException|NumberFormatException e) {
+ throw new ProtocolException("problem parsing stats", e);
+ } finally {
+ IoUtils.closeQuietly(reader);
+ StrictMode.setThreadPolicy(savedPolicy);
+ }
+ return stats;
}
/**
@@ -96,6 +144,11 @@
* @throws IllegalStateException when problem parsing stats.
*/
public NetworkStats readNetworkStatsSummaryDev() throws IOException {
+
+ // Return the stats get from /proc/net/dev if switched to bpf module.
+ if (mUseBpfStats)
+ return readNetworkStatsIfaceDev();
+
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
@@ -147,6 +200,11 @@
* @throws IllegalStateException when problem parsing stats.
*/
public NetworkStats readNetworkStatsSummaryXt() throws IOException {
+
+ // Return the stats get from /proc/net/dev if qtaguid module is replaced.
+ if (mUseBpfStats)
+ return readNetworkStatsIfaceDev();
+
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
// return null when kernel doesn't support
@@ -217,7 +275,7 @@
}
NetworkStats.Entry adjust =
- new NetworkStats.Entry(baseIface, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
+ new NetworkStats.Entry(baseIface, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
// Subtract any 464lat traffic seen for the root UID on the current base interface.
adjust.rxBytes -= (entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
adjust.txBytes -= (entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
@@ -252,7 +310,7 @@
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
- limitIfaces, limitTag) != 0) {
+ limitIfaces, limitTag, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats");
}
if (SANITY_CHECK_NATIVE) {
@@ -346,6 +404,6 @@
* are expected to monotonically increase since device boot.
*/
@VisibleForTesting
- public static native int nativeReadNetworkStatsDetail(
- NetworkStats stats, String path, int limitUid, String[] limitIfaces, int limitTag);
+ public static native int nativeReadNetworkStatsDetail(NetworkStats stats, String path,
+ int limitUid, String[] limitIfaces, int limitTag, boolean useBpfStats);
}
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index 10d44bd..ef44ef7 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
jsharkey@android.com
lorenzo@google.com
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 66475e4..bb5a0ad 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -71,10 +71,11 @@
public void uncaughtException(Thread t, Throwable e) {
// Don't re-enter if KillApplicationHandler has already run
if (mCrashing) return;
- if (mApplicationObject == null) {
- // The "FATAL EXCEPTION" string is still used on Android even though
- // apps can set a custom UncaughtExceptionHandler that renders uncaught
- // exceptions non-fatal.
+
+ // mApplicationObject is null for non-zygote java programs (e.g. "am")
+ // There are also apps running with the system UID. We don't want the
+ // first clause in either of these two cases, only for system_server.
+ if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
@@ -229,7 +230,7 @@
* @param argv Argument vector for main()
* @param classLoader the classLoader to load {@className} with
*/
- private static Runnable findStaticMain(String className, String[] argv,
+ protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index cadb66a..32b580c 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -18,9 +18,11 @@
import android.app.ApplicationLoaders;
import android.net.LocalSocket;
+import android.net.LocalServerSocket;
import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebViewFactory;
@@ -118,18 +120,35 @@
}
public static void main(String argv[]) {
- sServer = new WebViewZygoteServer();
+ Log.i(TAG, "Starting WebViewZygoteInit");
- // Zygote goes into its own process group.
- try {
- Os.setpgid(0, 0);
- } catch (ErrnoException ex) {
- throw new RuntimeException("Failed to setpgid(0,0)", ex);
+ String socketName = null;
+ for (String arg : argv) {
+ Log.i(TAG, arg);
+ if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+ socketName = arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
+ }
}
+ if (socketName == null) {
+ throw new RuntimeException("No " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + " specified");
+ }
+
+ try {
+ Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ } catch (ErrnoException ex) {
+ throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
+ }
+
+ sServer = new WebViewZygoteServer();
final Runnable caller;
try {
- sServer.registerServerSocket("webview_zygote");
+ sServer.registerServerSocketAtAbstractName(socketName);
+
+ // Add the abstract socket to the FD whitelist so that the native zygote code
+ // can properly detach it after forking.
+ Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
+
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 3ee8b47..e23cbf8 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,10 @@
public static final int DISABLE_VERIFIER = 1 << 9;
/** Only use oat files located in /system. Otherwise use dex/jar/apk . */
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
+ /** Do enfore hidden API access restrictions. */
+ public static final int ENABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Force generation of native debugging information for backtraces. */
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = 0;
@@ -67,8 +71,18 @@
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
+ /**
+ * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
+ * in the abstract socket namespace. This socket name is what the new child zygote
+ * should listen for connections on.
+ */
+ public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
+
private Zygote() {}
+ /** Called for some security initialization before any fork. */
+ native static void nativeSecurityInit();
+
/**
* Forks a new VM instance. The current VM must have been started
* with the -Xzygote flag. <b>NOTE: new instance keeps all
@@ -95,6 +109,8 @@
* @param fdsToIgnore null-ok an array of ints, either null or holding
* one or more POSIX file descriptor numbers that are to be ignored
* in the file descriptor table check.
+ * @param startChildZygote if true, the new child process will itself be a
+ * new zygote process.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
*
@@ -103,13 +119,13 @@
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, String instructionSet, String appDataDir) {
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, instructionSet, appDataDir);
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -123,7 +139,7 @@
native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, String instructionSet, String appDataDir);
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir);
/**
* Called to do any initialization before starting an application.
@@ -183,8 +199,8 @@
native protected static void nativeUnmountStorageOnInit();
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
- String instructionSet) {
- VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, instructionSet);
+ boolean isZygote, String instructionSet) {
+ VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}
/**
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 6a87b1f..a32fb43 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -221,8 +221,8 @@
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
- parsedArgs.appDataDir);
+ parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
+ parsedArgs.instructionSet, parsedArgs.appDataDir);
try {
if (pid == 0) {
@@ -233,7 +233,8 @@
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
- return handleChildProc(parsedArgs, descriptors, childPipeFd);
+ return handleChildProc(parsedArgs, descriptors, childPipeFd,
+ parsedArgs.startChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
@@ -415,6 +416,14 @@
boolean preloadDefault;
/**
+ * Whether this is a request to start a zygote process as a child of this zygote.
+ * Set with --start-child-zygote. The remaining arguments must include the
+ * CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that
+ * should be used for communication.
+ */
+ boolean startChildZygote;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -565,6 +574,8 @@
preloadPackageCacheKey = args[++curArg];
} else if (arg.equals("--preload-default")) {
preloadDefault = true;
+ } else if (arg.equals("--start-child-zygote")) {
+ startChildZygote = true;
} else {
break;
}
@@ -587,6 +598,20 @@
remainingArgs = new String[args.length - curArg];
System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
}
+
+ if (startChildZygote) {
+ boolean seenChildSocketArg = false;
+ for (String arg : remainingArgs) {
+ if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+ seenChildSocketArg = true;
+ break;
+ }
+ }
+ if (!seenChildSocketArg) {
+ throw new IllegalArgumentException("--start-child-zygote specified " +
+ "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
+ }
+ }
}
}
@@ -739,9 +764,10 @@
* @param parsedArgs non-null; zygote args
* @param descriptors null-ok; new file descriptors for stdio if available.
* @param pipeFd null-ok; pipe for communication back to Zygote.
+ * @param isZygote whether this new child process is itself a new Zygote.
*/
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
- FileDescriptor pipeFd) {
+ FileDescriptor pipeFd, boolean isZygote) {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
@@ -778,8 +804,13 @@
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
- return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
- null /* classLoader */);
+ if (!isZygote) {
+ return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
+ null /* classLoader */);
+ } else {
+ return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
+ parsedArgs.remainingArgs, null /* classLoader */);
+ }
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 2be6212..a05454f 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -30,7 +30,6 @@
import android.os.Environment;
import android.os.Process;
import android.os.RemoteException;
-import android.os.Seccomp;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
@@ -572,10 +571,13 @@
final String seInfo = null;
final String classLoaderContext =
getSystemServerClassLoaderContext(classPathForElement);
+ final int targetSdkVersion = 0; // SystemServer targets the system's SDK version
try {
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
- uuid, classLoaderContext, seInfo, false /* downgrade */);
+ uuid, classLoaderContext, seInfo, false /* downgrade */,
+ targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
+ "server-dexopt");
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -754,7 +756,7 @@
throw new RuntimeException("No ABI list supplied.");
}
- zygoteServer.registerServerSocket(socketName);
+ zygoteServer.registerServerSocketFromEnv(socketName);
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
@@ -779,12 +781,11 @@
// Zygote.
Trace.setTracingEnabled(false, 0);
+ Zygote.nativeSecurityInit();
+
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
- // Set seccomp policy
- Seccomp.setPolicy();
-
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
@@ -870,5 +871,16 @@
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
+ /**
+ * The main function called when starting a child zygote process. This is used as an
+ * alternative to zygoteInit(), which skips calling into initialization routines that
+ * start the Binder threadpool.
+ */
+ static final Runnable childZygoteInit(
+ int targetSdkVersion, String[] argv, ClassLoader classLoader) {
+ RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
+ return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
+ }
+
private static final native void nativeZygoteInit();
}
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 8baa15a..fecf9b9 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -44,9 +44,21 @@
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
+ /**
+ * Listening socket that accepts new server connections.
+ */
private LocalServerSocket mServerSocket;
/**
+ * Whether or not mServerSocket's underlying FD should be closed directly.
+ * If mServerSocket is created with an existing FD, closing the socket does
+ * not close the FD and it must be closed explicitly. If the socket is created
+ * with a name instead, then closing the socket will close the underlying FD
+ * and it should not be double-closed.
+ */
+ private boolean mCloseSocketFd;
+
+ /**
* Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
*/
private boolean mIsForkChild;
@@ -59,11 +71,12 @@
}
/**
- * Registers a server socket for zygote command connections
+ * Registers a server socket for zygote command connections. This locates the server socket
+ * file descriptor through an ANDROID_SOCKET_ environment variable.
*
* @throws RuntimeException when open fails
*/
- void registerServerSocket(String socketName) {
+ void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
@@ -78,6 +91,7 @@
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);
+ mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
@@ -86,6 +100,22 @@
}
/**
+ * Registers a server socket for zygote command connections. This opens the server socket
+ * at the specified name in the abstract socket namespace.
+ */
+ void registerServerSocketAtAbstractName(String socketName) {
+ if (mServerSocket == null) {
+ try {
+ mServerSocket = new LocalServerSocket(socketName);
+ mCloseSocketFd = false;
+ } catch (IOException ex) {
+ throw new RuntimeException(
+ "Error binding to abstract socket '" + socketName + "'", ex);
+ }
+ }
+ }
+
+ /**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
@@ -112,7 +142,7 @@
if (mServerSocket != null) {
FileDescriptor fd = mServerSocket.getFileDescriptor();
mServerSocket.close();
- if (fd != null) {
+ if (fd != null && mCloseSocketFd) {
Os.close(fd);
}
}
@@ -219,6 +249,11 @@
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
+ } finally {
+ // Reset the child flag, in the event that the child process is a child-
+ // zygote. The flag will not be consulted this loop pass after the Runnable
+ // is returned.
+ mIsForkChild = false;
}
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 6af41a5..324f923 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -256,7 +256,7 @@
final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
if (hgrav == Gravity.RIGHT) {
- xOffset += mAnchorView.getWidth();
+ xOffset -= mAnchorView.getWidth();
}
popup.setHorizontalOffset(xOffset);
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index d9ca5be..445379b 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -263,7 +263,6 @@
mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
subPopup.setPresenterCallback(mPresenterCallback);
subPopup.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(subMenu));
- subPopup.setGravity(mDropDownGravity);
// Pass responsibility for handling onDismiss to the submenu.
subPopup.setOnDismissListener(mOnDismissListener);
@@ -273,8 +272,17 @@
mMenu.close(false /* closeAllMenus */);
// Show the new sub-menu popup at the same location as this popup.
- final int horizontalOffset = mPopup.getHorizontalOffset();
+ int horizontalOffset = mPopup.getHorizontalOffset();
final int verticalOffset = mPopup.getVerticalOffset();
+
+ // As xOffset of parent menu popup is subtracted with Anchor width for Gravity.RIGHT,
+ // So, again to display sub-menu popup in same xOffset, add the Anchor width.
+ final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
+ mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
+ if (hgrav == Gravity.RIGHT) {
+ horizontalOffset += mAnchorView.getWidth();
+ }
+
if (subPopup.tryShow(horizontalOffset, verticalOffset)) {
if (mPresenterCallback != null) {
mPresenterCallback.onOpenSubMenu(subMenu);
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 8848e393..87fde41 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -161,7 +161,7 @@
.append("Revision: ")
.append(SystemProperties.get("ro.revision", "")).append("\n")
.append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
- .append("Radio: ").append(Build.RADIO).append("\n")
+ .append("Radio: ").append(Build.getRadioVersion()).append("\n")
.append("Kernel: ")
.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
.append("\n").toString();
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 7778892..bdb5f99 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -60,6 +60,7 @@
private static final int ALLOW_PERMISSIONS = 0x04;
private static final int ALLOW_APP_CONFIGS = 0x08;
private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x10;
+ private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x20;
private static final int ALLOW_ALL = ~0;
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
@@ -134,6 +135,9 @@
// These are the permitted backup transport service components
final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
+ // Package names that are exempted from private API blacklisting
+ final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
+
// These are the packages of carrier-associated apps which should be disabled until used until
// a SIM is inserted which grants carrier privileges to that carrier app.
final ArrayMap<String, List<String>> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
@@ -204,6 +208,10 @@
return mSystemUserBlacklistedApps;
}
+ public ArraySet<String> getHiddenApiWhitelistedApps() {
+ return mHiddenApiPackageWhitelist;
+ }
+
public ArraySet<ComponentName> getDefaultVrComponents() {
return mDefaultVrComponents;
}
@@ -327,6 +335,7 @@
boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
+ boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -569,6 +578,15 @@
XmlUtils.skipCurrentTag(parser);
} else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
readPrivAppPermissions(parser);
+ } else if ("hidden-api-whitelisted-app".equals(name) && allowApiWhitelisting) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<hidden-api-whitelisted-app> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mHiddenApiPackageWhitelist.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;
diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS
index 6f77e04..ce50558 100644
--- a/core/java/com/android/server/net/OWNERS
+++ b/core/java/com/android/server/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 551d54a..b3fb43d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -86,7 +86,6 @@
"android_os_MessageQueue.cpp",
"android_os_Parcel.cpp",
"android_os_SELinux.cpp",
- "android_os_seccomp.cpp",
"android_os_SharedMemory.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
@@ -216,6 +215,8 @@
],
shared_libs: [
+ "libbpf",
+ "libnetdutils",
"libmemtrack",
"libandroidfw",
"libappfuse",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 047fa84..3784d4d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -163,7 +163,6 @@
extern int register_android_os_SELinux(JNIEnv* env);
extern int register_android_os_VintfObject(JNIEnv *env);
extern int register_android_os_VintfRuntimeInfo(JNIEnv *env);
-extern int register_android_os_seccomp(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
@@ -762,18 +761,17 @@
/*
* Enable debugging only for apps forked from zygote.
- * Set suspend=y to pause during VM init and use android ADB transport.
*/
if (zygote) {
+ // Set the JDWP provider and required arguments. By default let the runtime choose how JDWP is
+ // implemented. When this is not set the runtime defaults to not allowing JDWP.
addOption("-XjdwpOptions:suspend=n,server=y");
+ parseRuntimeOption("dalvik.vm.jdwp-provider",
+ jdwpProviderBuf,
+ "-XjdwpProvider:",
+ "default");
}
- // Set the JDWP provider. By default let the runtime choose.
- parseRuntimeOption("dalvik.vm.jdwp-provider",
- jdwpProviderBuf,
- "-XjdwpProvider:",
- "default");
-
parseRuntimeOption("dalvik.vm.lockprof.threshold",
lockProfThresholdBuf,
"-Xlockprofthreshold:");
@@ -970,6 +968,12 @@
addOption("--generate-debug-info");
}
+ // The mini-debug-info makes it possible to backtrace through JIT code.
+ if (property_get_bool("dalvik.vm.minidebuginfo", 0)) {
+ addOption("-Xcompiler-option");
+ addOption("--generate-mini-debug-info");
+ }
+
/*
* Retrieve the build fingerprint and provide it to the runtime. That way, ANR dumps will
* contain the fingerprint and can be parsed.
@@ -1420,7 +1424,6 @@
REG_JNI(register_android_os_GraphicsEnvironment),
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_SELinux),
- REG_JNI(register_android_os_seccomp),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
REG_JNI(register_android_net_LocalSocketImpl),
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 9494fb8..061349a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -391,6 +391,10 @@
Status status;
status_t err = ::android::hardware::readFromParcel(&status, *parcel);
signalExceptionForError(env, err);
+
+ if (!status.isOk()) {
+ signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+ }
}
static void JHwParcel_native_release(
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1eeea51..e8ef349 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -32,10 +32,13 @@
static jclass gHashMapClazz;
static jmethodID gHashMapInit;
static jmethodID gHashMapPut;
+static jclass gLongClazz;
+static jmethodID gLongValueOf;
namespace android {
using vintf::HalManifest;
+using vintf::Level;
using vintf::SchemaType;
using vintf::VintfObject;
using vintf::XmlConverter;
@@ -146,14 +149,22 @@
return nullptr;
}
jobject jMap = env->NewObject(gHashMapClazz, gHashMapInit);
- for (const Vndk &vndk : manifest->vndks()) {
- std::string key = to_string(vndk.versionRange());
+ for (const auto &vndk : manifest->vendorNdks()) {
+ std::string key = vndk.version();
env->CallObjectMethod(jMap, gHashMapPut,
env->NewStringUTF(key.c_str()), toJavaStringArray(env, vndk.libraries()));
}
return jMap;
}
+static jobject android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion(JNIEnv* env, jclass) {
+ std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+ if (manifest == nullptr || manifest->level() == Level::UNSPECIFIED) {
+ return nullptr;
+ }
+ return env->CallStaticObjectMethod(gLongClazz, gLongValueOf, static_cast<jlong>(manifest->level()));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gVintfObjectMethods[] = {
@@ -163,6 +174,7 @@
{"getHalNamesAndVersions", "()[Ljava/lang/String;", (void*)android_os_VintfObject_getHalNamesAndVersions},
{"getSepolicyVersion", "()Ljava/lang/String;", (void*)android_os_VintfObject_getSepolicyVersion},
{"getVndkSnapshots", "()Ljava/util/Map;", (void*)android_os_VintfObject_getVndkSnapshots},
+ {"getTargetFrameworkCompatibilityMatrixVersion", "()Ljava/lang/Long;", (void*)android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion},
};
const char* const kVintfObjectPathName = "android/os/VintfObject";
@@ -175,6 +187,8 @@
gHashMapInit = GetMethodIDOrDie(env, gHashMapClazz, "<init>", "()V");
gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz,
"put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ gLongClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Long"));
+ gLongValueOf = GetStaticMethodIDOrDie(env, gLongClazz, "valueOf", "(J)Ljava/lang/Long;");
return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
NELEM(gVintfObjectMethods));
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
deleted file mode 100644
index 06e2a16..0000000
--- a/core/jni/android_os_seccomp.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "core_jni_helpers.h"
-#include <nativehelper/JniConstants.h>
-#include "utils/Log.h"
-#include <selinux/selinux.h>
-
-#include "seccomp_policy.h"
-
-static void Seccomp_setPolicy(JNIEnv* /*env*/) {
- if (security_getenforce() == 0) {
- ALOGI("seccomp disabled by setenforce 0");
- return;
- }
-
- if (!set_seccomp_filter()) {
- ALOGE("Failed to set seccomp policy - killing");
- exit(1);
- }
-}
-
-static const JNINativeMethod method_table[] = {
- NATIVE_METHOD(Seccomp, setPolicy, "()V"),
-};
-
-namespace android {
-
-int register_android_os_seccomp(JNIEnv* env) {
- return android::RegisterMethodsOrDie(env, "android/os/Seccomp",
- method_table, NELEM(method_table));
-}
-
-}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 560c384..93abc63 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -664,7 +664,7 @@
nativeData->mObject = val;
gNativeDataCache = nullptr;
++gNumProxies;
- if (++gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
+ if (gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
ALOGW("Unexpectedly many live BinderProxies: %d\n", gNumProxies);
gProxiesWarned = gNumProxies;
}
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index fce5dd5..685df0f 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -236,17 +236,15 @@
return INSTALL_SUCCEEDED;
}
- char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 2];
+ char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 1];
if (strlcpy(localTmpFileName, nativeLibPath.c_str(), sizeof(localTmpFileName))
!= nativeLibPath.size()) {
ALOGD("Couldn't allocate local file name for library");
return INSTALL_FAILED_INTERNAL_ERROR;
}
- *(localTmpFileName + nativeLibPath.size()) = '/';
-
if (strlcpy(localTmpFileName + nativeLibPath.size(), TMP_FILE_PATTERN,
- TMP_FILE_PATTERN_LEN - nativeLibPath.size()) != TMP_FILE_PATTERN_LEN) {
+ TMP_FILE_PATTERN_LEN + 1) != TMP_FILE_PATTERN_LEN) {
ALOGI("Couldn't allocate temporary file name for library");
return INSTALL_FAILED_INTERNAL_ERROR;
}
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index 0cb6935..99d9839 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -30,7 +30,14 @@
#include <utils/Log.h>
#include <utils/misc.h>
-#include <utils/Vector.h>
+
+#include "android-base/unique_fd.h"
+#include "bpf/BpfNetworkStats.h"
+#include "bpf/BpfUtils.h"
+
+using android::bpf::hasBpfSupport;
+using android::bpf::parseBpfNetworkStatsDetail;
+using android::bpf::stats_line;
namespace android {
@@ -45,6 +52,7 @@
jfieldID tag;
jfieldID metered;
jfieldID roaming;
+ jfieldID defaultNetwork;
jfieldID rxBytes;
jfieldID rxPackets;
jfieldID txBytes;
@@ -52,17 +60,6 @@
jfieldID operations;
} gNetworkStatsClassInfo;
-struct stats_line {
- char iface[32];
- int32_t uid;
- int32_t set;
- int32_t tag;
- int64_t rxBytes;
- int64_t rxPackets;
- int64_t txBytes;
- int64_t txPackets;
-};
-
static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
{
if (!grow) {
@@ -96,33 +93,14 @@
return env->NewLongArray(size);
}
-static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
- jstring path, jint limitUid, jobjectArray limitIfacesObj, jint limitTag) {
- ScopedUtfChars path8(env, path);
- if (path8.c_str() == NULL) {
- return -1;
- }
-
- FILE *fp = fopen(path8.c_str(), "r");
+static int legacyReadNetworkStatsDetail(std::vector<stats_line>* lines,
+ const std::vector<std::string>& limitIfaces,
+ int limitTag, int limitUid, const char* path) {
+ FILE* fp = fopen(path, "r");
if (fp == NULL) {
return -1;
}
- Vector<String8> limitIfaces;
- if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
- int num = env->GetArrayLength(limitIfacesObj);
- limitIfaces.setCapacity(num);
- for (int i=0; i<num; i++) {
- jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
- ScopedUtfChars string8(env, string);
- if (string8.c_str() != NULL) {
- limitIfaces.add(String8(string8.c_str()));
- }
- }
- }
-
- Vector<stats_line> lines;
-
int lastIdx = 1;
int idx;
char buffer[384];
@@ -214,7 +192,7 @@
//ALOGI("skipping due to uid: %s", buffer);
continue;
}
- lines.push_back(s);
+ lines->push_back(s);
} else {
//ALOGI("skipping due to bad remaining fields: %s", pos);
}
@@ -224,8 +202,42 @@
ALOGE("Failed to close netstats file");
return -1;
}
+ return 0;
+}
+
+static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
+ jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
+ jboolean useBpfStats) {
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ return -1;
+ }
+
+ std::vector<std::string> limitIfaces;
+ if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
+ int num = env->GetArrayLength(limitIfacesObj);
+ for (int i = 0; i < num; i++) {
+ jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
+ ScopedUtfChars string8(env, string);
+ if (string8.c_str() != NULL) {
+ limitIfaces.push_back(std::string(string8.c_str()));
+ }
+ }
+ }
+ std::vector<stats_line> lines;
+
+
+ if (useBpfStats) {
+ if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
+ return -1;
+ } else {
+ if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
+ limitUid, path8.c_str()) < 0)
+ return -1;
+ }
int size = lines.size();
+
bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
@@ -246,6 +258,9 @@
ScopedIntArrayRW roaming(env, get_int_array(env, stats,
gNetworkStatsClassInfo.roaming, size, grow));
if (roaming.get() == NULL) return -1;
+ ScopedIntArrayRW defaultNetwork(env, get_int_array(env, stats,
+ gNetworkStatsClassInfo.defaultNetwork, size, grow));
+ if (defaultNetwork.get() == NULL) return -1;
ScopedLongArrayRW rxBytes(env, get_long_array(env, stats,
gNetworkStatsClassInfo.rxBytes, size, grow));
if (rxBytes.get() == NULL) return -1;
@@ -269,7 +284,7 @@
uid[i] = lines[i].uid;
set[i] = lines[i].set;
tag[i] = lines[i].tag;
- // Metered and Roaming are populated in Java-land by inspecting the iface properties.
+ // Metered, roaming and defaultNetwork are populated in Java-land.
rxBytes[i] = lines[i].rxBytes;
rxPackets[i] = lines[i].rxPackets;
txBytes[i] = lines[i].txBytes;
@@ -285,6 +300,8 @@
env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.defaultNetwork,
+ defaultNetwork.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
@@ -297,7 +314,7 @@
static const JNINativeMethod gMethods[] = {
{ "nativeReadNetworkStatsDetail",
- "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I",
+ "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I",
(void*) readNetworkStatsDetail }
};
@@ -318,6 +335,7 @@
gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I");
gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I");
gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I");
+ gNetworkStatsClassInfo.defaultNetwork = GetFieldIDOrDie(env, clazz, "defaultNetwork", "[I");
gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J");
gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J");
gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 32ef3dc..3f95cf4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -53,6 +53,7 @@
#include <private/android_filesystem_config.h>
#include <utils/String8.h>
#include <selinux/android.h>
+#include <seccomp_policy.h>
#include <processgroup/processgroup.h>
#include "core_jni_helpers.h"
@@ -76,6 +77,8 @@
static jclass gZygoteClass;
static jmethodID gCallPostForkChildHooks;
+static bool g_is_security_enforced = true;
+
// Must match values in com.android.internal.os.Zygote.
enum MountExternalKind {
MOUNT_EXTERNAL_NONE = 0,
@@ -138,32 +141,45 @@
errno = saved_errno;
}
-// Configures the SIGCHLD handler for the zygote process. This is configured
-// very late, because earlier in the runtime we may fork() and exec()
-// other processes, and we want to waitpid() for those rather than
+// Configures the SIGCHLD/SIGHUP handlers for the zygote process. This is
+// configured very late, because earlier in the runtime we may fork() and
+// exec() other processes, and we want to waitpid() for those rather than
// have them be harvested immediately.
//
+// Ignore SIGHUP because all processes forked by the zygote are in the same
+// process group as the zygote and we don't want to be notified if we become
+// an orphaned group and have one or more stopped processes. This is not a
+// theoretical concern :
+// - we can become an orphaned group if one of our direct descendants forks
+// and is subsequently killed before its children.
+// - crash_dump routinely STOPs the process it's tracing.
+//
+// See issues b/71965619 and b/25567761 for further details.
+//
// This ends up being called repeatedly before each fork(), but there's
// no real harm in that.
-static void SetSigChldHandler() {
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SigChldHandler;
+static void SetSignalHandlers() {
+ struct sigaction sig_chld = {};
+ sig_chld.sa_handler = SigChldHandler;
- int err = sigaction(SIGCHLD, &sa, NULL);
- if (err < 0) {
+ if (sigaction(SIGCHLD, &sig_chld, NULL) < 0) {
ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
}
+
+ struct sigaction sig_hup = {};
+ sig_hup.sa_handler = SIG_IGN;
+ if (sigaction(SIGHUP, &sig_hup, NULL) < 0) {
+ ALOGW("Error setting SIGHUP handler: %s", strerror(errno));
+ }
}
// Sets the SIGCHLD handler back to default behavior in zygote children.
-static void UnsetSigChldHandler() {
+static void UnsetChldSignalHandler() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
- int err = sigaction(SIGCHLD, &sa, NULL);
- if (err < 0) {
+ if (sigaction(SIGCHLD, &sa, NULL) < 0) {
ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
}
}
@@ -229,6 +245,20 @@
mallopt(M_DECAY_TIME, 1);
}
+static void SetUpSeccompFilter(uid_t uid) {
+ if (!g_is_security_enforced) {
+ ALOGI("seccomp disabled by setenforce 0");
+ return;
+ }
+
+ // Apply system or app filter based on uid.
+ if (getuid() >= AID_APP_START) {
+ set_app_seccomp_filter();
+ } else {
+ set_system_seccomp_filter();
+ }
+}
+
static void EnableKeepCapabilities(JNIEnv* env) {
int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
if (rc == -1) {
@@ -486,9 +516,9 @@
jint mount_external,
jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose,
- jintArray fdsToIgnore,
+ jintArray fdsToIgnore, bool is_child_zygote,
jstring instructionSet, jstring dataDir) {
- SetSigChldHandler();
+ SetSignalHandlers();
sigset_t sigchld;
sigemptyset(&sigchld);
@@ -541,6 +571,11 @@
RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
}
+ // Must be called when the new process still has CAP_SYS_ADMIN. The other alternative is to
+ // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+ // b/71859146).
+ SetUpSeccompFilter(uid);
+
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
EnableKeepCapabilities(env);
@@ -660,10 +695,11 @@
delete se_info;
delete se_name;
- UnsetSigChldHandler();
+ // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+ UnsetChldSignalHandler();
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
- is_system_server, instructionSet);
+ is_system_server, is_child_zygote, instructionSet);
if (env->ExceptionCheck()) {
RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
}
@@ -698,6 +734,12 @@
namespace android {
+static void com_android_internal_os_Zygote_nativeSecurityInit(JNIEnv*, jclass) {
+ // security_getenforce is not allowed on app process. Initialize and cache the value before
+ // zygote forks.
+ g_is_security_enforced = security_getenforce();
+}
+
static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) {
PreApplicationInit();
}
@@ -706,8 +748,7 @@
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
- jintArray fdsToClose,
- jintArray fdsToIgnore,
+ jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
jstring instructionSet, jstring appDataDir) {
jlong capabilities = 0;
@@ -744,13 +785,22 @@
capabilities |= (1LL << CAP_BLOCK_SUSPEND);
}
+ // If forking a child zygote process, that zygote will need to be able to change
+ // the UID and GID of processes it forks, as well as drop those capabilities.
+ if (is_child_zygote) {
+ capabilities |= (1LL << CAP_SETUID);
+ capabilities |= (1LL << CAP_SETGID);
+ capabilities |= (1LL << CAP_SETPCAP);
+ }
+
// Containers run without some capabilities, so drop any caps that are not
// available.
capabilities &= GetEffectiveCapabilityMask(env);
return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
rlimits, capabilities, capabilities, mount_external, se_info,
- se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir);
+ se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
+ instructionSet, appDataDir);
}
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
@@ -761,7 +811,7 @@
runtime_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
- NULL, NULL, NULL);
+ NULL, false, NULL, NULL);
if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -832,8 +882,10 @@
}
static const JNINativeMethod gMethods[] = {
+ { "nativeSecurityInit", "()V",
+ (void *) com_android_internal_os_Zygote_nativeSecurityInit },
{ "nativeForkAndSpecialize",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
@@ -848,7 +900,7 @@
int register_com_android_internal_os_Zygote(JNIEnv* env) {
gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
- "(IZLjava/lang/String;)V");
+ "(IZZLjava/lang/String;)V");
return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
}
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 956b724..1383bbd 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -313,10 +313,12 @@
return false;
}
- // This is a local socket with an abstract address, we do not accept it.
+ // This is a local socket with an abstract address. Remove the leading NUL byte and
+ // add a human-readable "ABSTRACT/" prefix.
if (unix_addr->sun_path[0] == '\0') {
- LOG(ERROR) << "Unsupported AF_UNIX socket (fd=" << fd << ") with abstract address.";
- return false;
+ *result = "ABSTRACT/";
+ result->append(&unix_addr->sun_path[1], path_len - 1);
+ return true;
}
// If we're here, sun_path must refer to a null terminated filesystem
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index ef6eb09..f78ebca 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -335,6 +335,7 @@
SettingProto uninstalled_instant_app_min_cache_period = 290;
SettingProto uninstalled_instant_app_max_cache_period = 291;
SettingProto unused_static_shared_lib_min_cache_period = 292;
+ SettingProto hidden_api_blacklist_exemptions = 293;
}
message SecureSettingsProto {
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index 5a577b1..e5dbdbb 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -64,6 +64,8 @@
bool roaming = 4;
bool metered = 5;
+
+ bool default_network = 6;
}
// Corresponds to NetworkStatsRecorder.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9072960..4ea2d10 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -159,6 +159,8 @@
<protected-broadcast
android:name="android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
@@ -171,8 +173,14 @@
<protected-broadcast
android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
<protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" />
@@ -559,6 +567,9 @@
<protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
<protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
+ <!-- Added in P -->
+ <protected-broadcast android:name="android.telephony.euicc.action.OTA_STATUS_CHANGED" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -943,6 +954,23 @@
android:description="@string/permdesc_manageOwnCalls"
android:protectionLevel="normal" />
+ <!-- Allows a calling app to continue a call which was started in another app. An example is a
+ video calling app that wants to continue a voice call on the user's mobile network.<p>
+ When the handover of a call from one app to another takes place, there are two devices
+ which are involved in the handover; the initiating and receiving devices. The initiating
+ device is where the request to handover the call was started, and the receiving device is
+ where the handover request is confirmed by the other party.<p>
+ This permission protects access to
+ {@link android.telecom.TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)}, which
+ the receiving side of the handover uses to accept a handover.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.ACCEPT_HANDOVER"
+ android:permissionGroup="android.permission-group.PHONE"
+ android.label="@string/permlab_acceptHandover"
+ android:description="@string/permdesc_acceptHandover"
+ android:protectionLevel="dangerous" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device microphone -->
<!-- ====================================================================== -->
@@ -1408,6 +1436,12 @@
android:label="@string/permlab_nfc"
android:protectionLevel="normal" />
+ <!-- Allows applications to receive NFC transaction events.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.NFC_TRANSACTION_EVENT"
+ android:protectionLevel="normal" />
+
<!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
@hide -->
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
@@ -1726,19 +1760,38 @@
<permission android:name="android.permission.BIND_IMS_SERVICE"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
- EuiccManager APIs.
+ <!-- Must be required by a telephony data service to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a NetworkService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to manage embedded subscriptions (those on a eUICC)
+ through EuiccManager APIs.
<p>Protection level: signature|privileged|development
- TODO(b/35851809): Mark this as a SystemApi and remove com. prefix.
- @hide -->
- <permission android:name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
+ @hide
+ -->
+ <permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
android:protectionLevel="signature|privileged|development" />
- <!-- Must be required by an EuiccService to ensure that only the system can bind to it.
+ <!-- @SystemApi Must be required by an EuiccService to ensure that only the system can bind to
+ it.
<p>Protection level: signature
- TODO(b/35851809): Mark this as a SystemApi and remove com. prefix.
- @hide -->
- <permission android:name="com.android.permission.BIND_EUICC_SERVICE"
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_EUICC_SERVICE"
android:protectionLevel="signature" />
<!-- ================================== -->
@@ -3560,6 +3613,11 @@
<permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
android:protectionLevel="signature|development|instant|appop" />
+ <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_RUNTIME_PROFILES"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index c66ed12..4a3bf22 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -25,15 +25,5 @@
-->
<integer name="config_mobile_mtu">1358</integer>
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-105</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-95</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-85</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 04f182e..cc7daa8 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -51,16 +51,6 @@
<bool name="config_auto_attach_data_on_creation">false</bool>
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-105</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-95</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-85</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
<bool name="config_use_sim_language_file">true</bool>
diff --git a/core/res/res/values-mcc505-mnc01/config.xml b/core/res/res/values-mcc505-mnc01/config.xml
index 5a5b8f7..bc088d1 100644
--- a/core/res/res/values-mcc505-mnc01/config.xml
+++ b/core/res/res/values-mcc505-mnc01/config.xml
@@ -31,16 +31,6 @@
<item>9</item>
</integer-array>
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-
<!-- Configure mobile network MTU. Carrier specific value is set here.
-->
<integer name="config_mobile_mtu">1400</integer>
diff --git a/core/res/res/values-mcc505-mnc11/config.xml b/core/res/res/values-mcc505-mnc11/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc11/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
- for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-</resources>
diff --git a/core/res/res/values-mcc505-mnc71/config.xml b/core/res/res/values-mcc505-mnc71/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc71/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
- for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-</resources>
diff --git a/core/res/res/values-mcc505-mnc72/config.xml b/core/res/res/values-mcc505-mnc72/config.xml
deleted file mode 100644
index 6d085c1..0000000
--- a/core/res/res/values-mcc505-mnc72/config.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
- for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-120</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-115</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-100</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-90</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 15fd34d..42efc21 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -360,6 +360,23 @@
<!-- Regex of wired ethernet ifaces -->
<string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
+
+
+ <!-- Configuration of Ethernet interfaces in the following format:
+ <interface name|mac address>;[Network Capabilities];[IP config]
+ Where
+ [Network Capabilities] Optional. A comma seprated list of network capabilities.
+ Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
+ [IP config] Optional. If empty or not specified - DHCP will be used, otherwise
+ static IP address with the mask.
+ -->
+ <string-array translatable="false" name="config_ethernet_interfaces">
+ <!--
+ <item>eth1;12,13,14,15;192.168.0.10/24</item>
+ <item>eth2;;192.168.0.11/24</item>
+ -->
+ </string-array>
+
<!-- If the mobile hotspot feature requires provisioning, a package name and class name
can be provided to launch a supported application that provisions the devices.
@@ -1753,9 +1770,6 @@
<string-array name="config_twoDigitNumberPattern" translatable="false">
</string-array>
- <!-- The VoiceMail default value is displayed to my own number if it is true -->
- <bool name="config_telephony_use_own_number_for_voicemail">false</bool>
-
<!-- If this value is true, Sms encoded as octet is decoded by utf8 decoder.
If false, decoded by Latin decoder. -->
<bool name="config_sms_utf8_support">false</bool>
@@ -2386,6 +2400,32 @@
in the display pipeline plus some slack just to be sure. -->
<integer name="config_drawLockTimeoutMillis">120</integer>
+ <!-- An array of device capabilities defined by GSMA SGP.22 v2.0.
+ The first item is the capability name that the device supports. The second item is the
+ major version. The minor and revision versions are default to 0s.
+ The device capabilities and their definition in the spec are:
+ gsm : gsmSupportedRelease
+ utran : utranSupportedRelease
+ cdma1x : cdma2000onexSupportedRelease
+ hrpd : cdma2000hrpdSupportedRelease
+ ehrpd : cdma2000ehrpdSupportedRelease
+ eutran : eutranSupportedRelease
+ nfc : contactlessSupportedRelease
+ crl : rspCrlSupportedVersion
+ -->
+ <string-array translatable="false" name="config_telephonyEuiccDeviceCapabilities">
+ <!-- Example:
+ <item>"gsm,11"</item>
+ <item>"utran,11"</item>
+ <item>"cdma1x,1"</item>
+ <item>"hrpd,3"</item>
+ <item>"ehrpd,12"</item>
+ <item>"eutran,11"</item>
+ <item>"nfc,1"</item>
+ <item>"crl,1"</item>
+ -->
+ </string-array>
+
<!-- default telephony hardware configuration for this platform.
-->
<!-- this string array should be overridden by the device to present a list
@@ -2471,6 +2511,14 @@
<!-- Flag specifying whether or not IMS will use the dynamic ImsResolver -->
<bool name="config_dynamic_bind_ims">false</bool>
+ <!-- Cellular data service package name to bind to by default. If none is specified in an overlay, an
+ empty string is passed in -->
+ <string name="config_wwan_data_service_package" translatable="false">com.android.phone</string>
+
+ <!-- IWLAN data service package name to bind to by default. If none is specified in an overlay, an
+ empty string is passed in -->
+ <string name="config_wlan_data_service_package" translatable="false"></string>
+
<bool name="config_networkSamplingWakesDevice">true</bool>
<!-- Home (non-roaming) values for CDMA roaming indicator.
@@ -2560,15 +2608,13 @@
<bool name="config_sms_force_7bit_encoding">false</bool>
- <!--Thresholds for LTE dbm in status bar-->
- <integer-array translatable="false" name="config_lteDbmThresholds">
- <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
- <item>-128</item> <!-- SIGNAL_STRENGTH_POOR -->
- <item>-118</item> <!-- SIGNAL_STRENGTH_MODERATE -->
- <item>-108</item> <!-- SIGNAL_STRENGTH_GOOD -->
- <item>-98</item> <!-- SIGNAL_STRENGTH_GREAT -->
- <item>-44</item>
- </integer-array>
+
+ <!-- Number of physical SIM slots on the device. This includes both eSIM and pSIM slots, and
+ is not necessarily the same as the number of phones/logical modems supported by the device.
+ For example, a multi-sim device can have 2 phones/logical modems, but 3 physical slots,
+ or a single SIM device can have 1 phones/logical modems, but 2 physical slots (one eSIM
+ and one pSIM) -->
+ <integer name="config_num_physical_slots">1</integer>
<!-- Enabled built-in zen mode condition providers -->
<string-array translatable="false" name="config_system_condition_providers">
@@ -3100,4 +3146,13 @@
<!-- Decide whether to display 'No service' on status bar instead of 'Emergency calls only'
when SIM is unready. -->
<bool name="config_display_no_service_when_sim_unready">false</bool>
+
+ <bool name="config_supportBluetoothPersistedState">true</bool>
+
+ <!-- Cellular network service package name to bind to by default. -->
+ <string name="config_wwan_network_service_package" translatable="false">com.android.phone</string>
+
+ <!-- IWLAN network service package name to bind to by default. If none is specified in an overlay, an
+ empty string is passed in -->
+ <string name="config_wlan_network_service_package" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index befebd9..0a36ba7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1076,6 +1076,17 @@
<string name="permdesc_manageOwnCalls">Allows the app to route its calls through the system in
order to improve the calling experience.</string>
+ <!-- Title of an application permission. When granted to a third party app, the user is giving
+ access to the app to continue a call which originated in another app. For example, the
+ user could be in a voice call over their carrier's mobile network, and a third party video
+ calling app wants to continue that voice call as a video call. -->
+ <string name="permlab_acceptHandover">continue a call from another app</string>
+ <!-- Description of an application permission. When granted to a third party app, the user is
+ giving access to the app to continue a call which originated in another app. For example,
+ the user could be in a voice call over their carrier's mobile network, and a third party
+ video calling app wants to continue that voice call as a video call. -->
+ <string name="permdesc_acceptHandover">Allows the app to continue a call which was started in another app.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readPhoneNumbers">read phone numbers</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1703ef0..ba9b8fe 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -264,6 +264,10 @@
<java-symbol type="bool" name="config_mms_content_disposition_support" />
<java-symbol type="string" name="config_ims_package" />
<java-symbol type="bool" name="config_dynamic_bind_ims" />
+ <java-symbol type="string" name="config_wwan_data_service_package" />
+ <java-symbol type="string" name="config_wlan_data_service_package" />
+ <java-symbol type="string" name="config_wwan_network_service_package" />
+ <java-symbol type="string" name="config_wlan_network_service_package" />
<java-symbol type="bool" name="config_networkSamplingWakesDevice" />
<java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" />
<java-symbol type="bool" name="config_sip_wifi_only" />
@@ -272,7 +276,6 @@
<java-symbol type="bool" name="config_suspendWhenScreenOffDueToProximity" />
<java-symbol type="bool" name="config_swipeDisambiguation" />
<java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
- <java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" />
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_enableHapticTextHandle" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
@@ -446,6 +449,7 @@
<java-symbol type="integer" name="config_keepPreloadsMinDays" />
<java-symbol type="bool" name="config_hasPermanentDpad" />
<java-symbol type="bool" name="config_useDefaultFocusHighlight" />
+ <java-symbol type="integer" name="config_num_physical_slots" />
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -634,6 +638,7 @@
<java-symbol type="string" name="chooseActivity" />
<java-symbol type="string" name="config_default_dns_server" />
<java-symbol type="string" name="config_ethernet_iface_regex" />
+ <java-symbol type="array" name="config_ethernet_interfaces" />
<java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
<java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -1190,6 +1195,7 @@
<java-symbol type="array" name="config_disabledUntilUsedPreinstalledCarrierApps" />
<java-symbol type="array" name="config_callBarringMMI" />
<java-symbol type="array" name="config_globalActionsList" />
+ <java-symbol type="array" name="config_telephonyEuiccDeviceCapabilities" />
<java-symbol type="array" name="config_telephonyHardware" />
<java-symbol type="array" name="config_keySystemUuidMapping" />
<java-symbol type="array" name="config_gpsParameters" />
@@ -2384,9 +2390,6 @@
<!-- Cascading submenus -->
<java-symbol type="dimen" name="cascading_menus_min_smallest_width" />
- <!-- From SignalStrength -->
- <java-symbol type="array" name="config_lteDbmThresholds" />
-
<java-symbol type="string" name="android_system_label" />
<java-symbol type="string" name="system_error_wipe_data" />
<java-symbol type="string" name="system_error_manufacturer" />
@@ -3082,4 +3085,6 @@
<java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
<java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
<java-symbol type="bool" name="config_display_no_service_when_sim_unready" />
+
+ <java-symbol type="bool" name="config_supportBluetoothPersistedState" />
</resources>
diff --git a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
index e62fbd6..c213464 100644
--- a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
+++ b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
@@ -53,7 +53,7 @@
stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL,
// Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70
// Fixed compilation problem but needs addressing properly.
- new String[0], 999);
+ new String[0], 999, false);
}
}
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index ab9912a..c0a8acd 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1041,6 +1041,13 @@
</intent-filter>
</activity>
+ <activity android:name="android.view.menu.ContextMenuActivity" android:label="ContextMenu" android:theme="@android:style/Theme.Material">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.view.menu.MenuWith1Item" android:label="MenuWith1Item">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/apks/install-split-base/Android.mk b/core/tests/coretests/apks/install-split-base/Android.mk
new file mode 100644
index 0000000..5b60e31
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-base/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install_split_base
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install-split-base/AndroidManifest.xml b/core/tests/coretests/apks/install-split-base/AndroidManifest.xml
new file mode 100644
index 0000000..c2bfedd
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-base/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_split"
+ android:isolatedSplits="true">
+
+ <application android:label="ClassloaderSplitApp">
+ <activity android:name=".BaseActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/core/java/android/os/Seccomp.java b/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
similarity index 74%
copy from core/java/android/os/Seccomp.java
copy to core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
index f14e93f..cb5760ce 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
@@ -1,5 +1,5 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
+/**
+ * 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.
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package android.os;
+package com.google.android.dexapis.splitapp;
-/**
- * @hide
- */
-public final class Seccomp {
- public static final native void setPolicy();
+import android.app.Activity;
+
+/** Main activity */
+public class BaseActivity extends Activity {
}
diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.mk b/core/tests/coretests/apks/install-split-feature-a/Android.mk
new file mode 100644
index 0000000..0f37d16
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-feature-a/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install_split_feature_a
+
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_FLAGS += --custom-package com.google.android.dexapis.splitapp.feature_a
+LOCAL_AAPT_FLAGS += --package-id 0x80
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml b/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml
new file mode 100644
index 0000000..3221c75
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_split"
+ featureSplit="feature_a">
+
+ <application>
+ <activity android:name=".feature_a.FeatureAActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
similarity index 72%
copy from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
copy to core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
index d648a35..0af5f89 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2013 The Android Open Source Project
+/**
+ * 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.
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package com.android.ims;
+package com.google.android.dexapis.splitapp.feature_a;
-parcelable ImsStreamMediaProfile;
+import android.app.Activity;
+
+/** Main activity */
+public class FeatureAActivity extends Activity {
+}
diff --git a/core/tests/coretests/res/layout/context_menu.xml b/core/tests/coretests/res/layout/context_menu.xml
new file mode 100644
index 0000000..3b9e2bd
--- /dev/null
+++ b/core/tests/coretests/res/layout/context_menu.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/context_menu_target_ltr"
+ android:orientation="horizontal"
+ android:layoutDirection="ltr"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="50px"
+ android:layout_marginEnd="50px">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="LTR"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/context_menu_target_rtl"
+ android:orientation="horizontal"
+ android:layoutDirection="rtl"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="50px"
+ android:layout_marginEnd="50px">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="RTL"/>
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
new file mode 100644
index 0000000..a183736
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -0,0 +1,235 @@
+/**
+ * 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.content.pm.dex;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.ApkLite;
+import android.content.pm.PackageParser.Package;
+import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.PackageParser.PackageParserException;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.frameworks.coretests.R;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import libcore.io.IoUtils;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DexMetadataHelperTest {
+ private static final String APK_FILE_EXTENSION = ".apk";
+ private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
+
+ private File mTmpDir = null;
+
+ @Before
+ public void setUp() {
+ mTmpDir = IoUtils.createTemporaryDirectory("DexMetadataHelperTest");
+ }
+
+ @After
+ public void tearDown() {
+ if (mTmpDir != null) {
+ File[] files = mTmpDir.listFiles();
+ for (File f : files) {
+ f.delete();
+ }
+ }
+ }
+
+ private File createDexMetadataFile(String apkFileName) throws IOException {
+ File dmFile = new File(mTmpDir, apkFileName.replace(APK_FILE_EXTENSION,
+ DEX_METADATA_FILE_EXTENSION));
+ try (FileOutputStream fos = new FileOutputStream(dmFile)) {
+ try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
+ zipOs.putNextEntry(new ZipEntry("primary.prof"));
+ zipOs.closeEntry();
+ }
+ }
+ return dmFile;
+ }
+
+ private File copyApkToToTmpDir(String apkFileName, int apkResourceId) throws IOException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ File outFile = new File(mTmpDir, apkFileName);
+ try (InputStream is = context.getResources().openRawResource(apkResourceId)) {
+ FileUtils.copyToFileOrThrow(is, outFile);
+ }
+ return outFile;
+ }
+
+ @Test
+ public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk");
+ Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+
+ Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ assertEquals(1, packageDexMetadata.size());
+ String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+ assertNotNull(baseDexMetadata);
+ assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+ }
+
+ @Test
+ public void testParsePackageSplitsWithDmFileValid()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_base.apk");
+ createDexMetadataFile("install_split_feature_a.apk");
+ Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+
+ Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ assertEquals(2, packageDexMetadata.size());
+ String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+ assertNotNull(baseDexMetadata);
+ assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+
+ String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+ assertNotNull(splitDexMetadata);
+ assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+ }
+
+ @Test
+ public void testParsePackageSplitsNoBaseWithDmFileValid()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_feature_a.apk");
+ Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+
+ Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ assertEquals(1, packageDexMetadata.size());
+
+ String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+ assertNotNull(splitDexMetadata);
+ assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+ }
+
+ @Test
+ public void testParsePackageWithDmFileInvalid() throws IOException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
+ Files.createFile(invalidDmFile.toPath());
+ try {
+ PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ DexMetadataHelper.validatePackageDexMetadata(pkg);
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageSplitsWithDmFileInvalid()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_base.apk");
+ File invalidDmFile = new File(mTmpDir, "install_split_feature_a.dm");
+ Files.createFile(invalidDmFile.toPath());
+
+ try {
+ PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+ DexMetadataHelper.validatePackageDexMetadata(pkg);
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testPackageWithDmFileNoMatch() throws IOException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("non_existent.apk");
+
+ try {
+ DexMetadataHelper.validateDexPaths(mTmpDir.list());
+ fail("Should fail validation");
+ } catch (IllegalStateException e) {
+ // expected.
+ }
+ }
+
+ @Test
+ public void testPackageSplitsWithDmFileNoMatch()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
+ createDexMetadataFile("install_split_base.apk");
+ createDexMetadataFile("install_split_feature_a.mistake.apk");
+
+ try {
+ DexMetadataHelper.validateDexPaths(mTmpDir.list());
+ fail("Should fail validation");
+ } catch (IllegalStateException e) {
+ // expected.
+ }
+ }
+
+ @Test
+ public void testPackageSizeWithDmFile()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ File dm = createDexMetadataFile("install_split_base.apk");
+ PackageParser.PackageLite pkg = new PackageParser().parsePackageLite(mTmpDir,
+ 0 /* flags */);
+
+ Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkg));
+ }
+
+ // This simulates the 'adb shell pm install' flow.
+ @Test
+ public void testPackageSizeWithPartialPackageLite() throws IOException, PackageParserException {
+ File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
+ File dm = createDexMetadataFile("install_split_base.apk");
+ ApkLite baseApk = PackageParser.parseApkLite(base, 0);
+ PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+ null, null);
+ Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
+
+ }
+
+ private static boolean isDexMetadataForApk(String dmaPath, String apkPath) {
+ return apkPath.substring(0, apkPath.length() - APK_FILE_EXTENSION.length()).equals(
+ dmaPath.substring(0, dmaPath.length() - DEX_METADATA_FILE_EXTENSION.length()));
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 19808ca..757a70c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -111,6 +111,12 @@
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
+ Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
+ Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
+ Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS,
+ Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
+ Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
+ Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
@@ -407,7 +413,8 @@
Settings.Global.WTF_IS_FATAL,
Settings.Global.ZEN_MODE,
Settings.Global.ZEN_MODE_CONFIG_ETAG,
- Settings.Global.ZEN_MODE_RINGER_LEVEL);
+ Settings.Global.ZEN_MODE_RINGER_LEVEL,
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
new file mode 100644
index 0000000..e69d1e7
--- /dev/null
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.euicc;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.carrier.CarrierIdentifier;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.UiccAccessRule;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EuiccProfileInfoTest {
+ @Test
+ public void testWriteToParcel() {
+ EuiccProfileInfo p =
+ new EuiccProfileInfo.Builder("21430000000000006587")
+ .setNickname("profile nickname")
+ .setServiceProviderName("service provider")
+ .setProfileName("profile name")
+ .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+ .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+ .setCarrierIdentifier(
+ new CarrierIdentifier(
+ new byte[] {0x23, 0x45, 0x67},
+ "123",
+ "45"))
+ .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+ .setUiccAccessRule(
+ Arrays.asList(new UiccAccessRule(new byte[] {}, "package", 12345L)))
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ assertTrue(parcel != null);
+ p.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ EuiccProfileInfo fromParcel = EuiccProfileInfo.CREATOR.createFromParcel(parcel);
+
+ assertEquals(p, fromParcel);
+ }
+
+ @Test
+ public void testWriteToParcelNullCarrierId() {
+ EuiccProfileInfo p =
+ new EuiccProfileInfo.Builder("21430000000000006587")
+ .setNickname("profile nickname")
+ .setServiceProviderName("service provider")
+ .setProfileName("profile name")
+ .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+ .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+ .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+ .setUiccAccessRule(
+ Arrays.asList(new UiccAccessRule(new byte[] {}, "package", 12345L))
+ )
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ assertTrue(parcel != null);
+ p.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ EuiccProfileInfo fromParcel = EuiccProfileInfo.CREATOR.createFromParcel(parcel);
+
+ assertEquals(p, fromParcel);
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ EuiccProfileInfo p =
+ new EuiccProfileInfo.Builder("21430000000000006587")
+ .setNickname("profile nickname")
+ .setProfileName("profile name")
+ .setServiceProviderName("service provider")
+ .setCarrierIdentifier(
+ new CarrierIdentifier(
+ new byte[] {0x23, 0x45, 0x67},
+ "123",
+ "45"))
+ .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+ .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+ .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+ .setUiccAccessRule(Arrays.asList(new UiccAccessRule(new byte[0], null, 0)))
+ .build();
+
+ assertEquals("21430000000000006587", p.getIccid());
+ assertEquals("profile nickname", p.getNickname());
+ assertEquals("profile name", p.getProfileName());
+ assertEquals("service provider", p.getServiceProviderName());
+ assertEquals("325", p.getCarrierIdentifier().getMcc());
+ assertEquals("764", p.getCarrierIdentifier().getMnc());
+ assertEquals("123", p.getCarrierIdentifier().getGid1());
+ assertEquals("45", p.getCarrierIdentifier().getGid2());
+ assertEquals(EuiccProfileInfo.PROFILE_STATE_ENABLED, p.getState());
+ assertEquals(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL, p.getProfileClass());
+ assertEquals(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE, p.getPolicyRules());
+ assertTrue(p.hasPolicyRules());
+ assertTrue(p.hasPolicyRule(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE));
+ assertFalse(p.hasPolicyRule(EuiccProfileInfo.POLICY_RULE_DO_NOT_DISABLE));
+ assertArrayEquals(
+ new UiccAccessRule[] {new UiccAccessRule(new byte[0], null, 0)},
+ p.getUiccAccessRules().toArray());
+ }
+
+ @Test
+ public void testBuilder_BasedOnAnotherProfile() {
+ EuiccProfileInfo p =
+ new EuiccProfileInfo.Builder("21430000000000006587")
+ .setNickname("profile nickname")
+ .setProfileName("profile name")
+ .setServiceProviderName("service provider")
+ .setCarrierIdentifier(
+ new CarrierIdentifier(
+ new byte[] {0x23, 0x45, 0x67},
+ "123",
+ "45"))
+ .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+ .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+ .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+ .setUiccAccessRule(
+ Arrays.asList(new UiccAccessRule(new byte[] {}, "package", 12345L)))
+ .build();
+
+ EuiccProfileInfo copied = new EuiccProfileInfo.Builder(p).build();
+
+ assertEquals(p, copied);
+ assertEquals(p.hashCode(), copied.hashCode());
+ }
+
+ @Test
+ public void testEqualsHashCode() {
+ EuiccProfileInfo p =
+ new EuiccProfileInfo.Builder("21430000000000006587")
+ .setNickname("profile nickname")
+ .setProfileName("profile name")
+ .setServiceProviderName("service provider")
+ .setCarrierIdentifier(
+ new CarrierIdentifier(
+ new byte[] {0x23, 0x45, 0x67},
+ "123",
+ "45"))
+ .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+ .setProfileClass(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+ .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+ .setUiccAccessRule(Arrays.asList(new UiccAccessRule(new byte[0], null, 0)))
+ .build();
+
+ assertTrue(p.equals(p));
+ assertFalse(p.equals(new Object()));
+
+ EuiccProfileInfo t = null;
+ assertFalse(p.equals(t));
+
+ t = new EuiccProfileInfo.Builder(p).setIccid("21").build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p).setNickname(null).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p).setProfileName(null).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p).setServiceProviderName(null).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p).setCarrierIdentifier(null).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p)
+ .setState(EuiccProfileInfo.PROFILE_STATE_DISABLED).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p)
+ .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_TESTING).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p).setPolicyRules(0).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+
+ t = new EuiccProfileInfo.Builder(p).setUiccAccessRule(null).build();
+ assertFalse(p.equals(t));
+ assertNotEquals(p.hashCode(), t.hashCode());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testBuilderBuild_IllegalIccid() {
+ new EuiccProfileInfo.Builder("abc").build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuilderSetOperatorMccMnc_Illegal() {
+ new EuiccProfileInfo.Builder("21430000000000006587")
+ .setCarrierIdentifier(new CarrierIdentifier(new byte[] {1, 2, 3, 4}, null, null));
+ }
+
+ @Test
+ public void testCreatorNewArray() {
+ EuiccProfileInfo[] profiles = EuiccProfileInfo.CREATOR.newArray(123);
+ assertEquals(123, profiles.length);
+ }
+}
diff --git a/core/tests/coretests/src/android/util/PollingCheck.java b/core/tests/coretests/src/android/util/PollingCheck.java
new file mode 100644
index 0000000..468b9b2
--- /dev/null
+++ b/core/tests/coretests/src/android/util/PollingCheck.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import org.junit.Assert;
+
+/**
+ * Utility used for testing that allows to poll for a certain condition to happen within a timeout.
+ *
+ * Code copied from com.android.compatibility.common.util.PollingCheck
+ */
+public abstract class PollingCheck {
+
+ private static final long DEFAULT_TIMEOUT = 3000;
+ private static final long TIME_SLICE = 50;
+ private final long mTimeout;
+
+ /**
+ * The condition that the PollingCheck should use to proceed successfully.
+ */
+ public interface PollingCheckCondition {
+
+ /**
+ * @return Whether the polling condition has been met.
+ */
+ boolean canProceed();
+ }
+
+ public PollingCheck(long timeout) {
+ mTimeout = timeout;
+ }
+
+ protected abstract boolean check();
+
+ /**
+ * Start running the polling check.
+ */
+ public void run() {
+ if (check()) {
+ return;
+ }
+
+ long timeout = mTimeout;
+ while (timeout > 0) {
+ try {
+ Thread.sleep(TIME_SLICE);
+ } catch (InterruptedException e) {
+ Assert.fail("unexpected InterruptedException");
+ }
+
+ if (check()) {
+ return;
+ }
+
+ timeout -= TIME_SLICE;
+ }
+
+ Assert.fail("unexpected timeout");
+ }
+
+ /**
+ * Instantiate and start polling for a given condition with a default 3000ms timeout.
+ *
+ * @param condition The condition to check for success.
+ */
+ public static void waitFor(final PollingCheckCondition condition) {
+ new PollingCheck(DEFAULT_TIMEOUT) {
+ @Override
+ protected boolean check() {
+ return condition.canProceed();
+ }
+ }.run();
+ }
+
+ /**
+ * Instantiate and start polling for a given condition.
+ *
+ * @param timeout Time out in ms
+ * @param condition The condition to check for success.
+ */
+ public static void waitFor(long timeout, final PollingCheckCondition condition) {
+ new PollingCheck(timeout) {
+ @Override
+ protected boolean check() {
+ return condition.canProceed();
+ }
+ }.run();
+ }
+}
+
diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuActivity.java b/core/tests/coretests/src/android/view/menu/ContextMenuActivity.java
new file mode 100644
index 0000000..830b3d5
--- /dev/null
+++ b/core/tests/coretests/src/android/view/menu/ContextMenuActivity.java
@@ -0,0 +1,54 @@
+/*
+ * 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.view.menu;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
+
+import com.android.frameworks.coretests.R;
+
+public class ContextMenuActivity extends Activity {
+
+ static final String LABEL_ITEM = "Item";
+ static final String LABEL_SUBMENU = "Submenu";
+ static final String LABEL_SUBITEM = "Subitem";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.context_menu);
+ registerForContextMenu(getTargetLtr());
+ registerForContextMenu(getTargetRtl());
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ menu.add(LABEL_ITEM);
+ menu.addSubMenu(LABEL_SUBMENU).add(LABEL_SUBITEM);
+ }
+
+ View getTargetLtr() {
+ return findViewById(R.id.context_menu_target_ltr);
+ }
+
+ View getTargetRtl() {
+ return findViewById(R.id.context_menu_target_rtl);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
new file mode 100644
index 0000000..59d4e55
--- /dev/null
+++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.view.menu;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.support.test.filters.MediumTest;
+import android.test.ActivityInstrumentationTestCase;
+import android.util.PollingCheck;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.espresso.ContextMenuUtils;
+
+@MediumTest
+public class ContextMenuTest extends ActivityInstrumentationTestCase<ContextMenuActivity> {
+
+ public ContextMenuTest() {
+ super("com.android.frameworks.coretests", ContextMenuActivity.class);
+ }
+
+ public void testContextMenuPositionLtr() throws InterruptedException {
+ testMenuPosition(getActivity().getTargetLtr());
+ }
+
+ public void testContextMenuPositionRtl() throws InterruptedException {
+ testMenuPosition(getActivity().getTargetRtl());
+ }
+
+ private void testMenuPosition(View target) throws InterruptedException {
+ final int minScreenDimension = getMinScreenDimension();
+ if (minScreenDimension < 320) {
+ // Assume there is insufficient room for the context menu to be aligned properly.
+ return;
+ }
+
+ int offsetX = target.getWidth() / 2;
+ int offsetY = target.getHeight() / 2;
+
+ getInstrumentation().runOnMainSync(() -> target.performLongClick(offsetX, offsetY));
+
+ PollingCheck.waitFor(
+ () -> ContextMenuUtils.isMenuItemClickable(ContextMenuActivity.LABEL_SUBMENU));
+
+ ContextMenuUtils.assertContextMenuAlignment(target, offsetX, offsetY);
+
+ ContextMenuUtils.clickMenuItem(ContextMenuActivity.LABEL_SUBMENU);
+
+ PollingCheck.waitFor(
+ () -> ContextMenuUtils.isMenuItemClickable(ContextMenuActivity.LABEL_SUBITEM));
+
+ if (minScreenDimension < getCascadingMenuTreshold()) {
+ // A non-cascading submenu should be displayed at the same location as its parent.
+ // Not testing cascading submenu position, as it is positioned differently.
+ ContextMenuUtils.assertContextMenuAlignment(target, offsetX, offsetY);
+ }
+ }
+
+ /**
+ * Returns the minimum of the default display's width and height.
+ */
+ private int getMinScreenDimension() {
+ final WindowManager windowManager = (WindowManager) getActivity().getSystemService(
+ Context.WINDOW_SERVICE);
+ final Display display = windowManager.getDefaultDisplay();
+ final Point displaySize = new Point();
+ display.getRealSize(displaySize);
+ return Math.min(displaySize.x, displaySize.y);
+ }
+
+ /**
+ * Returns the minimum display size where cascading submenus are supported.
+ */
+ private int getCascadingMenuTreshold() {
+ // Use the same dimension resource as in MenuPopupHelper.createPopup().
+ return getActivity().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.cascading_menus_min_smallest_width);
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
index c8218aa..487a881 100644
--- a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
@@ -17,25 +17,32 @@
package android.widget.espresso;
import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.not;
-import com.android.internal.view.menu.ListMenuItemView;
-
import android.support.test.espresso.NoMatchingRootException;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.ViewInteraction;
import android.support.test.espresso.matcher.ViewMatchers;
+import android.view.View;
import android.widget.MenuPopupWindow.MenuDropDownListView;
+import com.android.internal.view.menu.ListMenuItemView;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
/**
* Espresso utility methods for the context menu.
*/
@@ -82,10 +89,15 @@
private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel,
boolean enabled) {
onContextMenu().check(matches(
- hasDescendant(allOf(
- isAssignableFrom(ListMenuItemView.class),
- enabled ? isEnabled() : not(isEnabled()),
- hasDescendant(withText(itemLabel))))));
+ hasDescendant(getVisibleMenuItemMatcher(itemLabel, enabled))));
+ }
+
+ private static Matcher<View> getVisibleMenuItemMatcher(String itemLabel, boolean enabled) {
+ return allOf(
+ isAssignableFrom(ListMenuItemView.class),
+ hasDescendant(withText(itemLabel)),
+ enabled ? isEnabled() : not(isEnabled()),
+ isDisplayingAtLeast(90));
}
/**
@@ -107,4 +119,70 @@
public static void assertContextMenuContainsItemDisabled(String itemLabel) {
asssertContextMenuContainsItemWithEnabledState(itemLabel, false);
}
+
+ /**
+ * Asserts that the context menu window is aligned to a given view with a given offset.
+ *
+ * @param anchor Anchor view.
+ * @param offsetX x offset
+ * @param offsetY y offset.
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertContextMenuAlignment(View anchor, int offsetX, int offsetY) {
+ int [] expectedLocation = new int[2];
+ anchor.getLocationOnScreen(expectedLocation);
+ expectedLocation[0] += offsetX;
+ expectedLocation[1] += offsetY;
+
+ final boolean rtl = anchor.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+
+ onContextMenu().check(matches(new TypeSafeMatcher<View>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("root view ");
+ description.appendText(rtl ? "right" : "left");
+ description.appendText("=");
+ description.appendText(Integer.toString(offsetX));
+ description.appendText(", top=");
+ description.appendText(Integer.toString(offsetY));
+ }
+
+ @Override
+ public boolean matchesSafely(View view) {
+ View rootView = view.getRootView();
+ int [] actualLocation = new int[2];
+ rootView.getLocationOnScreen(actualLocation);
+ if (rtl) {
+ actualLocation[0] += rootView.getWidth();
+ }
+ return expectedLocation[0] == actualLocation[0]
+ && expectedLocation[1] == actualLocation[1];
+ }
+ }));
+ }
+
+ /**
+ * Check is the menu item is clickable (i.e. visible and enabled).
+ *
+ * @param itemLabel Label of the item.
+ * @return True if the menu item is clickable.
+ */
+ public static boolean isMenuItemClickable(String itemLabel) {
+ try {
+ onContextMenu().check(matches(
+ hasDescendant(getVisibleMenuItemMatcher(itemLabel, true))));
+ return true;
+ } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+ return false;
+ }
+ }
+
+ /**
+ * Click on a menu item with the specified label
+ * @param itemLabel Label of the item.
+ */
+ public static void clickMenuItem(String itemLabel) {
+ onView(getVisibleMenuItemMatcher(itemLabel, true))
+ .inRoot(withDecorView(hasFocus())).perform(click());
+ }
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index 99bcd6c..a6c5373 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -36,10 +36,8 @@
include $(BUILD_PACKAGE)
-ifndef LOCAL_JACK_ENABLED
$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
$(hide) mkdir -p $(dir $@)
$(MAINDEXCLASSES) $< 1>$@
$(built_dex_intermediate): $(mainDexList)
-endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml
index e3068920..7cd01e54 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/AndroidManifest.xml
@@ -7,6 +7,8 @@
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="19" />
+ <!-- Required for com.android.framework.multidexlegacytestservices.test2 -->
+ <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
<application
android:label="MultiDexLegacyTestServices">
@@ -124,6 +126,6 @@
<action android:name="com.android.framework.multidexlegacytestservices.action.Service19" />
</intent-filter>
</service>
- </application>
+ </application>
</manifest>
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
index 7b83999..cb0a591 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
@@ -60,35 +60,40 @@
// of the result file will be too big.
RandomAccessFile raf = new RandomAccessFile(resultFile, "rw");
raf.seek(raf.length());
- Log.i(TAG, "Writing 0x42434445 at " + raf.length() + " in " + resultFile.getPath());
- raf.writeInt(0x42434445);
+ if (raf.length() == 0) {
+ Log.i(TAG, "Writing 0x42434445 at " + raf.length() + " in " + resultFile.getPath());
+ raf.writeInt(0x42434445);
+ } else {
+ Log.w(TAG, "Service was restarted appending 0x42434445 twice at " + raf.length()
+ + " in " + resultFile.getPath());
+ raf.writeInt(0x42434445);
+ raf.writeInt(0x42434445);
+ }
raf.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- MultiDex.install(applicationContext);
- Log.i(TAG, "Multi dex installation done.");
+ MultiDex.install(applicationContext);
+ Log.i(TAG, "Multi dex installation done.");
- int value = getValue();
- Log.i(TAG, "Saving the result (" + value + ") to " + resultFile.getPath());
- try {
+ int value = getValue();
+ Log.i(TAG, "Saving the result (" + value + ") to " + resultFile.getPath());
// Append the check value in result file, keeping the constant values already written.
- RandomAccessFile raf = new RandomAccessFile(resultFile, "rw");
+ raf = new RandomAccessFile(resultFile, "rw");
raf.seek(raf.length());
Log.i(TAG, "Writing result at " + raf.length() + " in " + resultFile.getPath());
raf.writeInt(value);
raf.close();
} catch (IOException e) {
- e.printStackTrace();
- }
- try {
- // Writing end of processing flags, the existence of the file is the criteria
- RandomAccessFile raf = new RandomAccessFile(new File(applicationContext.getFilesDir(), getId() + ".complete"), "rw");
- Log.i(TAG, "creating complete file " + resultFile.getPath());
- raf.writeInt(0x32333435);
- raf.close();
- } catch (IOException e) {
- e.printStackTrace();
+ throw new AssertionError(e);
+ } finally {
+ try {
+ // Writing end of processing flags, the existence of the file is the criteria
+ RandomAccessFile raf = new RandomAccessFile(
+ new File(applicationContext.getFilesDir(), getId() + ".complete"), "rw");
+ Log.i(TAG, "creating complete file " + resultFile.getPath());
+ raf.writeInt(0x32333435);
+ raf.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
}
@@ -119,9 +124,10 @@
intermediate = ReflectIntermediateClass.get(45, 80, 20 /* 5 seems enough on a nakasi,
using 20 to get some margin */);
} catch (Exception e) {
- e.printStackTrace();
+ throw new AssertionError(e);
}
- int value = new com.android.framework.multidexlegacytestservices.manymethods.Big001().get1() +
+ int value =
+ new com.android.framework.multidexlegacytestservices.manymethods.Big001().get1() +
new com.android.framework.multidexlegacytestservices.manymethods.Big002().get2() +
new com.android.framework.multidexlegacytestservices.manymethods.Big003().get3() +
new com.android.framework.multidexlegacytestservices.manymethods.Big004().get4() +
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
new file mode 100644
index 0000000..f3d98a8
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2
+
+LOCAL_JAVA_LIBRARIES := android-support-multidex
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SDK_VERSION := 9
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
new file mode 100644
index 0000000..0ab2959
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.multidexlegacytestservices.test2"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="9" />
+ <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.framework.multidexlegacytestservices" />
+
+ <application
+ android:label="multidexlegacytestservices.test2" >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
new file mode 100644
index 0000000..900f203
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/src/com/android/framework/multidexlegacytestservices/test2/ServicesTests.java
@@ -0,0 +1,381 @@
+/*
+ * 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.framework.multidexlegacytestservices.test2;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.concurrent.TimeoutException;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Run the tests with: <code>adb shell am instrument -w
+ * com.android.framework.multidexlegacytestservices.test2/android.support.test.runner.AndroidJUnitRunner
+ * </code>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ServicesTests {
+ private static final String TAG = "ServicesTests";
+
+ static {
+ Log.i(TAG, "Initializing");
+ }
+
+ private class ExtensionFilter implements FileFilter {
+ private final String ext;
+
+ public ExtensionFilter(String ext) {
+ this.ext = ext;
+ }
+
+ @Override
+ public boolean accept(File file) {
+ return file.getName().endsWith(ext);
+ }
+ }
+
+ private class ExtractedZipFilter extends ExtensionFilter {
+ public ExtractedZipFilter() {
+ super(".zip");
+ }
+
+ @Override
+ public boolean accept(File file) {
+ return super.accept(file) && !file.getName().startsWith("tmp-");
+ }
+ }
+
+ private static final int ENDHDR = 22;
+
+ private static final String SERVICE_BASE_ACTION =
+ "com.android.framework.multidexlegacytestservices.action.Service";
+ private static final int MIN_SERVICE = 1;
+ private static final int MAX_SERVICE = 19;
+ private static final String COMPLETION_SUCCESS = "Success";
+
+ private File targetFilesDir;
+
+ @Before
+ public void setup() throws Exception {
+ Log.i(TAG, "setup");
+ killServices();
+
+ File applicationDataDir =
+ new File(InstrumentationRegistry.getTargetContext().getApplicationInfo().dataDir);
+ clearDirContent(applicationDataDir);
+ targetFilesDir = InstrumentationRegistry.getTargetContext().getFilesDir();
+
+ Log.i(TAG, "setup done");
+ }
+
+ @Test
+ public void testStressConcurentLaunch() throws Exception {
+ startServices();
+ waitServicesCompletion();
+ String completionStatus = getServicesCompletionStatus();
+ if (completionStatus != COMPLETION_SUCCESS) {
+ Assert.fail(completionStatus);
+ }
+ }
+
+ @Test
+ public void testRecoverFromZipCorruption() throws Exception {
+ int serviceId = 1;
+ // Ensure extraction.
+ initServicesWorkFiles();
+ startService(serviceId);
+ waitServicesCompletion(serviceId);
+
+ // Corruption of the extracted zips.
+ tamperAllExtractedZips();
+
+ killServices();
+ checkRecover();
+ }
+
+ @Test
+ public void testRecoverFromDexCorruption() throws Exception {
+ int serviceId = 1;
+ // Ensure extraction.
+ initServicesWorkFiles();
+ startService(serviceId);
+ waitServicesCompletion(serviceId);
+
+ // Corruption of the odex files.
+ tamperAllOdex();
+
+ killServices();
+ checkRecover();
+ }
+
+ @Test
+ public void testRecoverFromZipCorruptionStressTest() throws Exception {
+ Thread startServices =
+ new Thread() {
+ @Override
+ public void run() {
+ startServices();
+ }
+ };
+
+ startServices.start();
+
+ // Start services lasts more than 80s, lets cause a few corruptions during this interval.
+ for (int i = 0; i < 80; i++) {
+ Thread.sleep(1000);
+ tamperAllExtractedZips();
+ }
+ startServices.join();
+ try {
+ waitServicesCompletion();
+ } catch (TimeoutException e) {
+ // Can happen.
+ }
+
+ killServices();
+ checkRecover();
+ }
+
+ @Test
+ public void testRecoverFromDexCorruptionStressTest() throws Exception {
+ Thread startServices =
+ new Thread() {
+ @Override
+ public void run() {
+ startServices();
+ }
+ };
+
+ startServices.start();
+
+ // Start services lasts more than 80s, lets cause a few corruptions during this interval.
+ for (int i = 0; i < 80; i++) {
+ Thread.sleep(1000);
+ tamperAllOdex();
+ }
+ startServices.join();
+ try {
+ waitServicesCompletion();
+ } catch (TimeoutException e) {
+ // Will probably happen most of the time considering what we're doing...
+ }
+
+ killServices();
+ checkRecover();
+ }
+
+ private static void clearDirContent(File dir) {
+ for (File subElement : dir.listFiles()) {
+ if (subElement.isDirectory()) {
+ clearDirContent(subElement);
+ }
+ if (!subElement.delete()) {
+ throw new AssertionError("Failed to clear '" + subElement.getAbsolutePath() + "'");
+ }
+ }
+ }
+
+ private void startServices() {
+ Log.i(TAG, "start services");
+ initServicesWorkFiles();
+ for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+ startService(i);
+ try {
+ Thread.sleep((i - 1) * (1 << (i / 5)));
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ private void startService(int serviceId) {
+ Log.i(TAG, "start service " + serviceId);
+ InstrumentationRegistry.getContext().startService(new Intent(SERVICE_BASE_ACTION + serviceId));
+ }
+
+ private void initServicesWorkFiles() {
+ for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+ File resultFile = new File(targetFilesDir, "Service" + i);
+ resultFile.delete();
+ Assert.assertFalse(
+ "Failed to delete result file '" + resultFile.getAbsolutePath() + "'.",
+ resultFile.exists());
+ File completeFile = new File(targetFilesDir, "Service" + i + ".complete");
+ completeFile.delete();
+ Assert.assertFalse(
+ "Failed to delete completion file '" + completeFile.getAbsolutePath() + "'.",
+ completeFile.exists());
+ }
+ }
+
+ private void waitServicesCompletion() throws TimeoutException {
+ Log.i(TAG, "start sleeping");
+ int attempt = 0;
+ int maxAttempt = 50; // 10 is enough for a nexus S
+ do {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+ attempt++;
+ if (attempt >= maxAttempt) {
+ throw new TimeoutException();
+ }
+ } while (!areAllServicesCompleted());
+ }
+
+ private void waitServicesCompletion(int serviceId) throws TimeoutException {
+ Log.i(TAG, "start sleeping");
+ int attempt = 0;
+ int maxAttempt = 50; // 10 is enough for a nexus S
+ do {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
+ attempt++;
+ if (attempt >= maxAttempt) {
+ throw new TimeoutException();
+ }
+ } while (isServiceRunning(serviceId));
+ }
+
+ private String getServicesCompletionStatus() {
+ String status = COMPLETION_SUCCESS;
+ for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+ File resultFile = new File(targetFilesDir, "Service" + i);
+ if (!resultFile.isFile()) {
+ status = "Service" + i + " never completed.";
+ break;
+ }
+ if (resultFile.length() != 8) {
+ status = "Service" + i + " was restarted.";
+ break;
+ }
+ }
+ Log.i(TAG, "Services completion status: " + status);
+ return status;
+ }
+
+ private String getServiceCompletionStatus(int serviceId) {
+ String status = COMPLETION_SUCCESS;
+ File resultFile = new File(targetFilesDir, "Service" + serviceId);
+ if (!resultFile.isFile()) {
+ status = "Service" + serviceId + " never completed.";
+ } else if (resultFile.length() != 8) {
+ status = "Service" + serviceId + " was restarted.";
+ }
+ Log.i(TAG, "Service " + serviceId + " completion status: " + status);
+ return status;
+ }
+
+ private boolean areAllServicesCompleted() {
+ for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
+ if (isServiceRunning(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isServiceRunning(int i) {
+ File completeFile = new File(targetFilesDir, "Service" + i + ".complete");
+ return !completeFile.exists();
+ }
+
+ private File getSecondaryFolder() {
+ File dir =
+ new File(
+ new File(
+ InstrumentationRegistry.getTargetContext().getApplicationInfo().dataDir,
+ "code_cache"),
+ "secondary-dexes");
+ Assert.assertTrue(dir.getAbsolutePath(), dir.isDirectory());
+ return dir;
+ }
+
+ private void tamperAllExtractedZips() throws IOException {
+ // First attempt was to just overwrite zip entries but keep central directory, this was no
+ // trouble for Dalvik that was just ignoring those zip and using the odex files.
+ Log.i(TAG, "Tamper extracted zip files by overwriting all content by '\\0's.");
+ byte[] zeros = new byte[4 * 1024];
+ // Do not tamper tmp zip during their extraction.
+ for (File zip : getSecondaryFolder().listFiles(new ExtractedZipFilter())) {
+ long fileLength = zip.length();
+ Assert.assertTrue(fileLength > ENDHDR);
+ zip.setWritable(true);
+ RandomAccessFile raf = new RandomAccessFile(zip, "rw");
+ try {
+ int index = 0;
+ while (index < fileLength) {
+ int length = (int) Math.min(zeros.length, fileLength - index);
+ raf.write(zeros, 0, length);
+ index += length;
+ }
+ } finally {
+ raf.close();
+ }
+ }
+ }
+
+ private void tamperAllOdex() throws IOException {
+ Log.i(TAG, "Tamper odex files by overwriting some content by '\\0's.");
+ byte[] zeros = new byte[4 * 1024];
+ // I think max size would be 40 (u1[8] + 8 u4) but it's a test so lets take big margins.
+ int savedSizeForOdexHeader = 80;
+ for (File odex : getSecondaryFolder().listFiles(new ExtensionFilter(".dex"))) {
+ long fileLength = odex.length();
+ Assert.assertTrue(fileLength > zeros.length + savedSizeForOdexHeader);
+ odex.setWritable(true);
+ RandomAccessFile raf = new RandomAccessFile(odex, "rw");
+ try {
+ raf.seek(savedSizeForOdexHeader);
+ raf.write(zeros, 0, zeros.length);
+ } finally {
+ raf.close();
+ }
+ }
+ }
+
+ private void checkRecover() throws TimeoutException {
+ Log.i(TAG, "Check recover capability");
+ int serviceId = 1;
+ // Start one service and check it was able to run correctly even if a previous run failed.
+ initServicesWorkFiles();
+ startService(serviceId);
+ waitServicesCompletion(serviceId);
+ String completionStatus = getServiceCompletionStatus(serviceId);
+ if (completionStatus != COMPLETION_SUCCESS) {
+ Assert.fail(completionStatus);
+ }
+ }
+
+ private void killServices() {
+ ((ActivityManager)
+ InstrumentationRegistry.getContext().getSystemService(Context.ACTIVITY_SERVICE))
+ .killBackgroundProcesses("com.android.framework.multidexlegacytestservices");
+ }
+}
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk
index 994131a..9b9e811 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk
@@ -29,13 +29,10 @@
LOCAL_SRC_FILES:= \
native.cpp
-# All of the shard libraries we link against.
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_LDLIBS := -llog
LOCAL_CFLAGS += -Wall -Wextra -Werror
-# Also need the JNI headers.
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := current
include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
index 99cf587..fe32454 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
@@ -15,12 +15,15 @@
*/
#define LOG_TAG "pmtest32 native.cpp"
-#include <utils/Log.h>
+#include <android/log.h>
#include <stdio.h>
#include "jni.h"
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
static jint
add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) {
int result = a + b;
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk
index 6c2679b..600a5d1 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk
@@ -30,14 +30,10 @@
LOCAL_SRC_FILES:= \
native.cpp
-# All of the shared libraries we link against.
-LOCAL_SHARED_LIBRARIES := \
- libutils liblog
+LOCAL_LDLIBS := -llog
LOCAL_CFLAGS += -Wall -Wextra -Werror
-# Also need the JNI headers.
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := current
include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
index 0b6d750..ad9e746 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
@@ -15,12 +15,15 @@
*/
#define LOG_TAG "pmtest64 native.cpp"
-#include <utils/Log.h>
+#include <android/log.h>
#include <stdio.h>
#include "jni.h"
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
static jint
add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) {
int result = a + b;
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk
index d668f29..8e9ac6b 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk
@@ -29,14 +29,10 @@
LOCAL_SRC_FILES:= \
native.cpp
-# All of the shard libraries we link against.
LOCAL_LDLIBS = -llog
-LOCAL_SHARED_LIBRARIES := liblog
LOCAL_CFLAGS += -Wall -Wextra -Werror
-# Also need the JNI headers.
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := current
include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
index 3947e21..5c5088f 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
@@ -15,12 +15,15 @@
*/
#define LOG_TAG "pmtestdual native.cpp"
-#include <utils/Log.h>
+#include <android/log.h>
#include <stdio.h>
#include "jni.h"
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
static jint
add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) {
int result = a + b;
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index b2c6840..936ad22 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -39,3 +39,11 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hiddenapi-package-whitelist.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
new file mode 100644
index 0000000..1d46d42
--- /dev/null
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -0,0 +1,101 @@
+<?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
+ -->
+
+<!--
+This XML file declares which system apps should be exempted from the hidden API blacklisting, i.e.
+which apps should be allowed to access the entire private API.
+-->
+
+<config>
+ <hidden-api-whitelisted-app package="android.car.cluster.loggingrenderer" />
+ <hidden-api-whitelisted-app package="android.car.input.service" />
+ <hidden-api-whitelisted-app package="android.car.usb.handler" />
+ <hidden-api-whitelisted-app package="android.ext.services" />
+ <hidden-api-whitelisted-app package="android.ext.shared" />
+ <hidden-api-whitelisted-app package="com.android.backupconfirm" />
+ <hidden-api-whitelisted-app package="com.android.bluetooth" />
+ <hidden-api-whitelisted-app package="com.android.bluetoothdebug" />
+ <hidden-api-whitelisted-app package="com.android.bluetoothmidiservice" />
+ <hidden-api-whitelisted-app package="com.android.calllogbackup" />
+ <hidden-api-whitelisted-app package="com.android.captiveportallogin" />
+ <hidden-api-whitelisted-app package="com.android.car" />
+ <hidden-api-whitelisted-app package="com.android.car.hvac" />
+ <hidden-api-whitelisted-app package="com.android.car.mapsplaceholder" />
+ <hidden-api-whitelisted-app package="com.android.car.media" />
+ <hidden-api-whitelisted-app package="com.android.car.media.localmediaplayer" />
+ <hidden-api-whitelisted-app package="com.android.car.radio" />
+ <hidden-api-whitelisted-app package="com.android.car.settings" />
+ <hidden-api-whitelisted-app package="com.android.car.systemupdater" />
+ <hidden-api-whitelisted-app package="com.android.car.trust" />
+ <hidden-api-whitelisted-app package="com.android.carrierconfig" />
+ <hidden-api-whitelisted-app package="com.android.carrierdefaultapp" />
+ <hidden-api-whitelisted-app package="com.android.cellbroadcastreceiver" />
+ <hidden-api-whitelisted-app package="com.android.certinstaller" />
+ <hidden-api-whitelisted-app package="com.android.customlocale2" />
+ <hidden-api-whitelisted-app package="com.android.defcontainer" />
+ <hidden-api-whitelisted-app package="com.android.documentsui" />
+ <hidden-api-whitelisted-app package="com.android.egg" />
+ <hidden-api-whitelisted-app package="com.android.email.policy" />
+ <hidden-api-whitelisted-app package="com.android.emergency" />
+ <hidden-api-whitelisted-app package="com.android.externalstorage" />
+ <hidden-api-whitelisted-app package="com.android.fakeoemfeatures" />
+ <hidden-api-whitelisted-app package="com.android.gallery" />
+ <hidden-api-whitelisted-app package="com.android.hotspot2" />
+ <hidden-api-whitelisted-app package="com.android.inputdevices" />
+ <hidden-api-whitelisted-app package="com.android.keychain" />
+ <hidden-api-whitelisted-app package="com.android.location.fused" />
+ <hidden-api-whitelisted-app package="com.android.managedprovisioning" />
+ <hidden-api-whitelisted-app package="com.android.mms.service" />
+ <hidden-api-whitelisted-app package="com.android.mtp" />
+ <hidden-api-whitelisted-app package="com.android.nfc" />
+ <hidden-api-whitelisted-app package="com.android.osu" />
+ <hidden-api-whitelisted-app package="com.android.packageinstaller" />
+ <hidden-api-whitelisted-app package="com.android.pacprocessor" />
+ <hidden-api-whitelisted-app package="com.android.phone" />
+ <hidden-api-whitelisted-app package="com.android.pmc" />
+ <hidden-api-whitelisted-app package="com.android.providers.blockednumber" />
+ <hidden-api-whitelisted-app package="com.android.providers.contacts" />
+ <hidden-api-whitelisted-app package="com.android.providers.downloads" />
+ <hidden-api-whitelisted-app package="com.android.providers.downloads.ui" />
+ <hidden-api-whitelisted-app package="com.android.providers.media" />
+ <hidden-api-whitelisted-app package="com.android.providers.settings" />
+ <hidden-api-whitelisted-app package="com.android.providers.telephony" />
+ <hidden-api-whitelisted-app package="com.android.providers.userdictionary" />
+ <hidden-api-whitelisted-app package="com.android.provision" />
+ <hidden-api-whitelisted-app package="com.android.proxyhandler" />
+ <hidden-api-whitelisted-app package="com.android.sdksetup" />
+ <hidden-api-whitelisted-app package="com.android.se" />
+ <hidden-api-whitelisted-app package="com.android.server.telecom" />
+ <hidden-api-whitelisted-app package="com.android.service.ims" />
+ <hidden-api-whitelisted-app package="com.android.service.ims.presence" />
+ <hidden-api-whitelisted-app package="com.android.settings" />
+ <hidden-api-whitelisted-app package="com.android.sharedstoragebackup" />
+ <hidden-api-whitelisted-app package="com.android.shell" />
+ <hidden-api-whitelisted-app package="com.android.stk" />
+ <hidden-api-whitelisted-app package="com.android.support.car.lenspicker" />
+ <hidden-api-whitelisted-app package="com.android.systemui" />
+ <hidden-api-whitelisted-app package="com.android.systemui.theme.dark" />
+ <hidden-api-whitelisted-app package="com.android.timezone.updater" />
+ <hidden-api-whitelisted-app package="com.android.traceur" />
+ <hidden-api-whitelisted-app package="com.android.tv.settings" />
+ <hidden-api-whitelisted-app package="com.android.vpndialogs" />
+ <hidden-api-whitelisted-app package="com.android.wallpaper.livepicker" />
+ <hidden-api-whitelisted-app package="com.android.wallpaperbackup" />
+ <hidden-api-whitelisted-app package="com.android.wallpapercropper" />
+ <hidden-api-whitelisted-app package="com.googlecode.android_scripting" />
+</config>
+
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6a3a973..0af187c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -134,6 +134,7 @@
<permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
<permission name="android.permission.BIND_IMS_SERVICE"/>
+ <permission name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
<permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
@@ -146,6 +147,7 @@
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
@@ -164,7 +166,7 @@
<permission name="android.permission.UPDATE_LOCK"/>
<permission name="android.permission.WRITE_APN_SETTINGS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+ <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
</privapp-permissions>
@@ -212,6 +214,7 @@
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING" />
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
@@ -346,6 +349,7 @@
<permission name="android.permission.WRITE_DREAM_STATE"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
</privapp-permissions>
<privapp-permissions package="com.android.tv">
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e3740e3..7ad062a 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -163,7 +163,7 @@
/**
* Create a drawable by opening a given file path and decoding the bitmap.
*/
- @SuppressWarnings("unused")
+ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, String filepath) {
this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
mBitmapState.mTargetDensity = mTargetDensity;
@@ -188,7 +188,7 @@
/**
* Create a drawable by decoding a bitmap from the given input stream.
*/
- @SuppressWarnings("unused")
+ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
public BitmapDrawable(Resources res, java.io.InputStream is) {
this(new BitmapState(BitmapFactory.decodeStream(is)), null);
mBitmapState.mTargetDensity = mTargetDensity;
diff --git a/legacy-test/Android.bp b/legacy-test/Android.bp
deleted file mode 100644
index 1173bc6..0000000
--- a/legacy-test/Android.bp
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// Build the legacy-test library
-// =============================
-// This contains the junit.framework and android.test classes that were in
-// Android API level 25 excluding those from android.test.runner.
-// Also contains the com.android.internal.util.Predicate[s] classes.
-java_library {
- name: "legacy-test",
-
- srcs: ["src/**/*.java"],
-
- no_framework_libs: true,
- libs: [
- "framework",
- ],
-
-}
-
-// Build the repackaged-legacy-test library
-// ========================================
-// This contains repackaged versions of the classes from legacy-test.
-java_library_static {
- name: "repackaged-legacy-test",
-
- static_libs: ["legacy-test"],
-
- jarjar_rules: "jarjar-rules.txt",
-}
-
-// Build the legacy-android-test library
-// =====================================
-// This contains the android.test classes that were in Android API level 25,
-// including those from android.test.runner.
-// Also contains the com.android.internal.util.Predicate[s] classes.
-java_library_static {
- name: "legacy-android-test",
-
- srcs: [
- "src/android/**/*.java",
- "src/com/**/*.java",
- ],
-
- static_libs: [
- "android.test.runner",
- "android.test.mock",
- ],
-
- no_framework_libs: true,
- libs: [
- "framework",
- "junit",
- ],
-}
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
deleted file mode 100644
index 793bbe8..0000000
--- a/legacy-test/Android.mk
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-# Generate the stub source files for legacy.test.stubs
-# ====================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
- core-oj \
- core-libart \
- framework \
-
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src
-
-LEGACY_TEST_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/api.txt
-LEGACY_TEST_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/removed.txt
-
-LEGACY_TEST_API_FILE := $(LOCAL_PATH)/api/legacy-test-current.txt
-LEGACY_TEST_REMOVED_API_FILE := $(LOCAL_PATH)/api/legacy-test-removed.txt
-
-LOCAL_DROIDDOC_OPTIONS:= \
- -stubpackages android.test:android.test.suitebuilder.annotation:com.android.internal.util:junit.framework \
- -stubsourceonly \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/src \
- -nodocs \
- -api $(LEGACY_TEST_OUTPUT_API_FILE) \
- -removedApi $(LEGACY_TEST_OUTPUT_REMOVED_API_FILE) \
-
-LOCAL_UNINSTALLABLE_MODULE := true
-LOCAL_MODULE := legacy-test-api-stubs-gen
-
-include $(BUILD_DROIDDOC)
-
-# Remember the target that will trigger the code generation.
-legacy_test_api_gen_stamp := $(full_target)
-
-# Add some additional dependencies
-$(LEGACY_TEST_OUTPUT_API_FILE): $(full_target)
-$(LEGACY_TEST_OUTPUT_REMOVED_API_FILE): $(full_target)
-
-# Build the legacy.test.stubs library
-# ===================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := legacy.test.stubs
-
-LOCAL_SOURCE_FILES_ALL_GENERATED := true
-
-# Make sure to run droiddoc first to generate the stub source files.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(legacy_test_api_gen_stamp)
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Archive a copy of the classes.jar in SDK build.
-$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):legacy.test.stubs.jar)
-
-# Check that the legacy.test.stubs library has not changed
-# ========================================================
-
-# Check that the API we're building hasn't changed from the not-yet-released
-# SDK version.
-$(eval $(call check-api, \
- check-legacy-test-api-current, \
- $(LEGACY_TEST_API_FILE), \
- $(LEGACY_TEST_OUTPUT_API_FILE), \
- $(LEGACY_TEST_REMOVED_API_FILE), \
- $(LEGACY_TEST_OUTPUT_REMOVED_API_FILE), \
- -error 2 -error 3 -error 4 -error 5 -error 6 \
- -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
- -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
- -error 25 -error 26 -error 27, \
- cat $(LOCAL_PATH)/api/apicheck_msg_legacy_test.txt, \
- check-legacy-test-api, \
- $(call doc-timestamp-for,legacy-test-api-stubs-gen) \
- ))
-
-.PHONY: check-legacy-test-api
-checkapi: check-legacy-test-api
-
-.PHONY: update-legacy-test-api
-update-api: update-legacy-test-api
-
-update-legacy-test-api: $(LEGACY_TEST_OUTPUT_API_FILE) | $(ACP)
- @echo Copying current.txt
- $(hide) $(ACP) $(LEGACY_TEST_OUTPUT_API_FILE) $(LEGACY_TEST_API_FILE)
- @echo Copying removed.txt
- $(hide) $(ACP) $(LEGACY_TEST_OUTPUT_REMOVED_API_FILE) $(LEGACY_TEST_REMOVED_API_FILE)
-
-ifeq ($(HOST_OS),linux)
-# Build the legacy-performance-test-hostdex library
-# =================================================
-# This contains the android.test.PerformanceTestCase class only
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
-LOCAL_MODULE := legacy-performance-test-hostdex
-
-include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
-endif # HOST_OS == linux
diff --git a/legacy-test/api/apicheck_msg_legacy_test.txt b/legacy-test/api/apicheck_msg_legacy_test.txt
deleted file mode 100644
index ad5f235..0000000
--- a/legacy-test/api/apicheck_msg_legacy_test.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-
-******************************
-You have tried to change the API from what has been previously approved.
-
-To make these errors go away, you have two choices:
- 1) You can add "@hide" javadoc comments to the methods, etc. listed in the
- errors above.
-
- 2) You can update legacy-test-current.txt by executing the following command:
- make update-legacy-test-api
-
- To submit the revised legacy-test-current.txt to the main Android repository,
- you will need approval.
-******************************
-
-
-
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index c4c14c9e..1d14fa0 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -89,4 +89,7 @@
// initialization with {}.
},
},
+ sanitize: {
+ blacklist: "libandroidfw_blacklist.txt",
+ },
}
diff --git a/libs/androidfw/libandroidfw_blacklist.txt b/libs/androidfw/libandroidfw_blacklist.txt
new file mode 100644
index 0000000..dd17e4d
--- /dev/null
+++ b/libs/androidfw/libandroidfw_blacklist.txt
@@ -0,0 +1 @@
+src:*/ResourceTypes.cpp
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 7c60467..e3af655 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -551,18 +551,20 @@
}
// Animate spots that are fading out and being removed.
- for (size_t i = 0; i < mLocked.spots.size(); i++) {
+ for (size_t i = 0; i < mLocked.spots.size();) {
Spot* spot = mLocked.spots.itemAt(i);
if (spot->id == Spot::INVALID_ID) {
spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
if (spot->alpha <= 0) {
- mLocked.spots.removeAt(i--);
+ mLocked.spots.removeAt(i);
releaseSpotLocked(spot);
+ continue;
} else {
spot->sprite->setAlpha(spot->alpha);
keepAnimating = true;
}
}
+ ++i;
}
return keepAnimating;
}
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
new file mode 100644
index 0000000..53e6201
--- /dev/null
+++ b/libs/services/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+// Provides C++ wrappers for system services.
+
+cc_library_shared {
+ name: "libservices",
+ srcs: [
+ ":IDropBoxManagerService.aidl",
+ "src/os/DropBoxManager.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libcutils",
+ "libutils",
+ ],
+ header_libs: [
+ "libbase_headers",
+ ],
+ aidl: {
+ include_dirs: ["frameworks/base/core/java/"],
+ },
+
+ export_include_dirs: ["include"],
+ export_header_lib_headers: ["libbase_headers"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
deleted file mode 100644
index cbfd4b3..0000000
--- a/libs/services/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 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)
-
-# Provides C++ wrappers for system services.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libservices
-LOCAL_SRC_FILES := \
- ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
- src/os/DropBoxManager.cpp
-
-LOCAL_AIDL_INCLUDES := \
- $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES := \
- system/core/include
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libcutils \
- libutils
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
index 8717178..3449a7b 100644
--- a/libs/services/include/android/os/DropBoxManager.h
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -57,7 +57,11 @@
// and a handle will be passed to the system process, so no additional permissions
// are required from the system process. Returns NULL if the file can't be opened.
Status addFile(const String16& tag, const string& filename, int flags);
-
+
+ // Create a new Entry from an already opened file. Takes ownership of the
+ // file descriptor.
+ Status addFile(const String16& tag, int fd, int flags);
+
class Entry : public virtual RefBase, public Parcelable {
public:
Entry();
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index bbb45f0..95246a0 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -179,7 +179,17 @@
ALOGW("DropboxManager: %s", message.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
}
+ return addFile(tag, fd, flags);
+}
+Status
+DropBoxManager::addFile(const String16& tag, int fd, int flags)
+{
+ if (fd == -1) {
+ string message("invalid fd (-1) passed to to addFile");
+ ALOGW("DropboxManager: %s", message.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
+ }
Entry entry(tag, flags, fd);
return add(entry);
}
@@ -196,4 +206,3 @@
}
}} // namespace android::os
-
diff --git a/location/lib/Android.mk b/location/lib/Android.mk
index 62f5677..8424601 100644
--- a/location/lib/Android.mk
+++ b/location/lib/Android.mk
@@ -22,9 +22,7 @@
LOCAL_MODULE:= com.android.location.provider
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := \
- $(call all-subdir-java-files) \
- $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
include $(BUILD_JAVA_LIBRARY)
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3df1706..ede172c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3606,6 +3606,21 @@
}
/**
+ * Indicate Hearing Aid connection state change.
+ * @param device Bluetooth device connected/disconnected
+ * @param state new connection state (BluetoothProfile.STATE_xxx)
+ * {@hide}
+ */
+ public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) {
+ final IAudioService service = getService();
+ try {
+ service.setHearingAidDeviceConnectionState(device, state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicate A2DP source or sink connection state change.
* @param device Bluetooth device connected/disconnected
* @param state new connection state (BluetoothProfile.STATE_xxx)
@@ -3629,6 +3644,33 @@
}
/**
+ * Indicate A2DP source or sink connection state change and eventually suppress
+ * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+ * @param device Bluetooth device connected/disconnected
+ * @param state new connection state (BluetoothProfile.STATE_xxx)
+ * @param profile profile for the A2DP device
+ * (either {@link android.bluetooth.BluetoothProfile.A2DP} or
+ * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
+ * @param suppressNoisyIntent if true the
+ * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+ * @return a delay in ms that the caller should wait before broadcasting
+ * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
+ * {@hide}
+ */
+ public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent) {
+ final IAudioService service = getService();
+ int delay = 0;
+ try {
+ delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
+ state, profile, suppressNoisyIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return delay;
+ }
+
+ /**
* Indicate A2DP device configuration has changed.
* @param device Bluetooth device whose configuration has changed.
* {@hide}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bb6ae98..07b6bbd 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -143,6 +143,8 @@
void setWiredDeviceConnectionState(int type, int state, String address, String name,
String caller);
+ void setHearingAidDeviceConnectionState(in BluetoothDevice device, int state);
+
int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
@@ -203,5 +205,8 @@
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
+ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
+ int state, int profile, boolean suppressNoisyIntent);
+
// WARNING: read warning at top of file, it is recommended to add new methods at the end
}
diff --git a/media/lib/remotedisplay/Android.mk b/media/lib/remotedisplay/Android.mk
index ea1ac2b..e88c0f1 100644
--- a/media/lib/remotedisplay/Android.mk
+++ b/media/lib/remotedisplay/Android.mk
@@ -22,9 +22,7 @@
LOCAL_MODULE:= com.android.media.remotedisplay
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, java) \
- $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
include $(BUILD_JAVA_LIBRARY)
diff --git a/media/lib/signer/Android.mk b/media/lib/signer/Android.mk
index b0d3177..69ca4d2 100644
--- a/media/lib/signer/Android.mk
+++ b/media/lib/signer/Android.mk
@@ -22,9 +22,7 @@
LOCAL_MODULE:= com.android.mediadrm.signer
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, java) \
- $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
include $(BUILD_JAVA_LIBRARY)
diff --git a/media/lib/tvremote/Android.mk b/media/lib/tvremote/Android.mk
index 06838c2..1ffdd62 100644
--- a/media/lib/tvremote/Android.mk
+++ b/media/lib/tvremote/Android.mk
@@ -22,9 +22,7 @@
LOCAL_MODULE:= com.android.media.tv.remoteprovider
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, java) \
- $(call all-aidl-files-under, java)
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
LOCAL_DEX_PREOPT := false
@@ -45,4 +43,4 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
-include $(BUILD_PREBUILT)
\ No newline at end of file
+include $(BUILD_PREBUILT)
diff --git a/native/android/OWNERS b/native/android/OWNERS
new file mode 100644
index 0000000..11d4be4
--- /dev/null
+++ b/native/android/OWNERS
@@ -0,0 +1,11 @@
+set noparent
+
+per-file libandroid_net.map.txt=ek@google.com
+per-file libandroid_net.map.txt=jchalard@google.com
+per-file libandroid_net.map.txt=lorenzo@google.com
+per-file libandroid_net.map.txt=satk@google.com
+
+per-file net.c=ek@google.com
+per-file net.c=jchalard@google.com
+per-file net.c=lorenzo@google.com
+per-file net.c=satk@google.com
diff --git a/native/android/net.c b/native/android/net.c
index de4b90c..60296a7 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -27,7 +27,7 @@
static const uint32_t k32BitMask = 0xffffffff;
// This value MUST be kept in sync with the corresponding value in
// the android.net.Network#getNetworkHandle() implementation.
- static const uint32_t kHandleMagic = 0xfacade;
+ static const uint32_t kHandleMagic = 0xcafed00d;
// Check for minimum acceptable version of the API in the low bits.
if (handle != NETWORK_UNSPECIFIED &&
diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS
index 6f77e04..ce50558 100644
--- a/packages/CaptivePortalLogin/OWNERS
+++ b/packages/CaptivePortalLogin/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
new file mode 100644
index 0000000..7057ce6
--- /dev/null
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -0,0 +1,12 @@
+tgunn@google.com
+breadley@google.com
+hallliu@google.com
+rgreenwalt@google.com
+mpq@google.com
+amitmahajan@google.com
+fionaxu@google.com
+jackyu@google.com
+jminjie@google.com
+satk@google.com
+shuoq@google.com
+refuhoo@google.com
\ No newline at end of file
diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk
index 88b85e0..12972f1 100644
--- a/packages/CtsShim/Android.mk
+++ b/packages/CtsShim/Android.mk
@@ -32,9 +32,10 @@
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
-my_archs := arm x86
-my_src_arch := $(call get-prebuilt-src-arch, $(my_archs))
-LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShimPriv.apk
+LOCAL_SRC_FILES_arm := apk/arm/CtsShimPriv.apk
+LOCAL_SRC_FILES_arm64 := apk/arm/CtsShimPriv.apk
+LOCAL_SRC_FILES_x86 := apk/x86/CtsShimPriv.apk
+LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShimPriv.apk
include $(BUILD_PREBUILT)
@@ -53,9 +54,10 @@
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
-my_archs := arm x86
-my_src_arch := $(call get-prebuilt-src-arch, $(my_archs))
-LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShim.apk
+LOCAL_SRC_FILES_arm := apk/arm/CtsShim.apk
+LOCAL_SRC_FILES_arm64 := apk/arm/CtsShim.apk
+LOCAL_SRC_FILES_x86 := apk/x86/CtsShim.apk
+LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShim.apk
include $(BUILD_PREBUILT)
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 184e559..888fedb 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -154,16 +154,16 @@
@AfterClass
public static void enableAnimations() throws Exception {
- if (sWindowAnimationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sWindowAnimationScaleBefore)) {
runShellCommand(
"settings put global window_animation_scale " + sWindowAnimationScaleBefore);
}
- if (sTransitionAnimationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sTransitionAnimationScaleBefore)) {
runShellCommand(
"settings put global transition_animation_scale " +
sTransitionAnimationScaleBefore);
}
- if (sAnimatiorDurationScaleBefore != Float.NaN) {
+ if (!Float.isNaN(sAnimatiorDurationScaleBefore)) {
runShellCommand(
"settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
}
diff --git a/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml b/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml
new file mode 100644
index 0000000..e14c99b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 77df02b..3460ecd 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -250,6 +250,19 @@
<item>Best Effort (Adaptive Bit Rate)</item>
</string-array>
+ <!-- TODO: Enable for translation per b/73007419 -->
+ <!-- Summaries for Bluetooth Audio Active Device status. [CHAR LIMIT=50]-->
+ <string-array name="bluetooth_audio_active_device_summaries" translatable="false" >
+ <!-- Status message when the device is not Active. -->
+ <item></item>
+ <!-- Status message when the device is Active for Media and Phone. -->
+ <item>, active</item>
+ <!-- Status message when the device is Active for Media only. -->
+ <item>, active(media)</item>
+ <!-- Status message when the device is Active for Phone only. -->
+ <item>, active(phone)</item>
+ </string-array>
+
<!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
<string-array name="select_logd_size_titles">
<item>Off</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8a4a774..428f0b8 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -128,27 +128,27 @@
<!-- Bluetooth settings. Message when connecting to a device -->
<string name="bluetooth_connecting">Connecting\u2026</string>
<!-- Bluetooth settings. Message when connected to a device. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected">Connected</string>
+ <string name="bluetooth_connected">Connected<xliff:g id="active_device">%1$s</xliff:g></string>
<!--Bluetooth settings screen, summary text under individual Bluetooth devices when pairing -->
<string name="bluetooth_pairing">Pairing\u2026</string>
<!-- Bluetooth settings. Message when connected to a device, except for phone audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_headset">Connected (no phone)</string>
+ <string name="bluetooth_connected_no_headset">Connected (no phone)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for media audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_a2dp">Connected (no media)</string>
+ <string name="bluetooth_connected_no_a2dp">Connected (no media)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for map. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_map">Connected (no message access)</string>
+ <string name="bluetooth_connected_no_map">Connected (no message access)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_headset_no_a2dp">Connected (no phone or media)</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp">Connected (no phone or media)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_battery_level">Connected, battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_battery_level">Connected, battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for phone audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_no_headset_battery_level">Connected (no phone), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_no_headset_battery_level">Connected (no phone), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_no_a2dp_battery_level">Connected (no media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_no_a2dp_battery_level">Connected (no media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$s</xliff:g></string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the A2DP profile. -->
<string name="bluetooth_profile_a2dp">Media audio</string>
@@ -177,6 +177,11 @@
<!-- Bluetooth settings. Similar to bluetooth_profile_a2dp_high_quality, but used when the device supports high quality audio but we don't know which codec that will be used. -->
<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>
+ <!-- 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>
+
<!-- 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>
<!-- Bluetooth settings. Connection options screen. The summary for the headset checkbox preference when headset is connected. -->
@@ -214,6 +219,8 @@
for the HID checkbox preference that describes how checking it
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>
<!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
<string name="bluetooth_pairing_accept">Pair</string>
@@ -488,8 +495,6 @@
<string name="bluetooth_show_devices_without_names">Show Bluetooth devices without names</string>
<!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
- <!-- Setting Checkbox title for enabling Bluetooth inband ringing -->
- <string name="bluetooth_enable_inband_ringing">Enable in-band ringing</string>
<!-- UI debug setting: Select Bluetooth AVRCP Version -->
<string name="bluetooth_select_avrcp_version_string">Bluetooth AVRCP Version</string>
@@ -582,8 +587,6 @@
<string name="bluetooth_show_devices_without_names_summary">Bluetooth devices without names (MAC addresses only) will be displayed</string>
<!-- Summary of checkbox for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
- <!-- Summary of checkbox for enabling Bluetooth inband ringing -->
- <string name="bluetooth_enable_inband_ringing_summary">Allow ringtones on the phone to be played on Bluetooth headsets</string>
<!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
<string name="enable_terminal_title">Local terminal</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
old mode 100755
new mode 100644
index 0946181..853cbba
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -129,14 +129,18 @@
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
- List<BluetoothDevice> sinks = getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- if (sink.equals(device)) {
- Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
- continue;
+ int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
+ if (max_connected_devices == 1) {
+ // Original behavior: disconnect currently connected device
+ List<BluetoothDevice> sinks = getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ if (sink.equals(device)) {
+ Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+ continue;
+ }
+ mService.disconnect(sink);
}
- mService.disconnect(sink);
}
}
return mService.connect(device);
@@ -158,6 +162,16 @@
return mService.getConnectionState(device);
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.setActiveDevice(device);
+ }
+
+ public BluetoothDevice getActiveDevice() {
+ if (mService == null) return null;
+ return mService.getActiveDevice();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
@@ -181,8 +195,8 @@
boolean isA2dpPlaying() {
if (mService == null) return false;
List<BluetoothDevice> sinks = mService.getConnectedDevices();
- if (!sinks.isEmpty()) {
- if (mService.isA2dpPlaying(sinks.get(0))) {
+ for (BluetoothDevice device : sinks) {
+ if (mService.isA2dpPlaying(device)) {
return true;
}
}
@@ -206,8 +220,8 @@
return true;
}
BluetoothCodecConfig codecConfig = null;
- if (mServiceWrapper.getCodecStatus() != null) {
- codecConfig = mServiceWrapper.getCodecStatus().getCodecConfig();
+ if (mServiceWrapper.getCodecStatus(device) != null) {
+ codecConfig = mServiceWrapper.getCodecStatus(device).getCodecConfig();
}
if (codecConfig != null) {
return !codecConfig.isMandatoryCodec();
@@ -225,9 +239,9 @@
return;
}
if (enabled) {
- mService.enableOptionalCodecs();
+ mService.enableOptionalCodecs(device);
} else {
- mService.disableOptionalCodecs();
+ mService.disableOptionalCodecs(device);
}
}
@@ -240,8 +254,8 @@
// We want to get the highest priority codec, since that's the one that will be used with
// this device, and see if it is high-quality (ie non-mandatory).
BluetoothCodecConfig[] selectable = null;
- if (mServiceWrapper.getCodecStatus() != null) {
- selectable = mServiceWrapper.getCodecStatus().getCodecsSelectableCapabilities();
+ if (mServiceWrapper.getCodecStatus(device) != null) {
+ selectable = mServiceWrapper.getCodecStatus(device).getCodecsSelectableCapabilities();
// To get the highest priority, we sort in reverse.
Arrays.sort(selectable,
(a, b) -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
index aa3e835..dace1bb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java
@@ -39,7 +39,7 @@
/**
* Wraps {@code BluetoothA2dp.getCodecStatus}
*/
- public BluetoothCodecStatus getCodecStatus();
+ public BluetoothCodecStatus getCodecStatus(BluetoothDevice device);
/**
* Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
index 14fa796..c49bb98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java
@@ -41,8 +41,8 @@
}
@Override
- public BluetoothCodecStatus getCodecStatus() {
- return mService.getCodecStatus();
+ public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+ return mService.getCodecStatus(device);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4c41b49..ac3599c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -28,4 +28,5 @@
void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
+ void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
old mode 100755
new mode 100644
index f57d02b..3cda9c9
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,9 +16,12 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +34,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -106,6 +110,12 @@
// Dock event broadcasts
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
+ // Active device broadcasts
+ addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
+ new ActiveDeviceChangedHandler());
+ addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
+ new ActiveDeviceChangedHandler());
+
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
}
@@ -409,4 +419,35 @@
return deviceAdded;
}
+
+ private class ActiveDeviceChangedHandler implements Handler {
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
+ return;
+ }
+ CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+ int bluetoothProfile = 0;
+ if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
+ bluetoothProfile = BluetoothProfile.A2DP;
+ } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
+ bluetoothProfile = BluetoothProfile.HEADSET;
+ } else {
+ Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+ return;
+ }
+ dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
+ }
+ }
+
+ private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9caff10..e1ebbc4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -105,6 +105,10 @@
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
+ // Active device state
+ private boolean mIsActiveDeviceA2dp = false;
+ private boolean mIsActiveDeviceHeadset = false;
+
/**
* Describes the current device and profile for logging.
*
@@ -156,6 +160,7 @@
mRemovedProfiles.add(profile);
mLocalNapRoleConnected = false;
}
+ fetchActiveDevices();
}
CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@
fetchName();
fetchBtClass();
updateProfiles();
+ fetchActiveDevices();
migratePhonebookPermissionChoice();
migrateMessagePermissionChoice();
fetchMessageRejectionCount();
@@ -454,6 +460,33 @@
return mDevice.getBondState();
}
+ /**
+ * Set the device status as active or non-active per Bluetooth profile.
+ *
+ * @param isActive true if the device is active
+ * @param bluetoothProfile the Bluetooth profile
+ */
+ public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+ boolean changed = false;
+ switch (bluetoothProfile) {
+ case BluetoothProfile.A2DP:
+ changed = (mIsActiveDeviceA2dp != isActive);
+ mIsActiveDeviceA2dp = isActive;
+ break;
+ case BluetoothProfile.HEADSET:
+ changed = (mIsActiveDeviceHeadset != isActive);
+ mIsActiveDeviceHeadset = isActive;
+ break;
+ default:
+ Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+ " isActive " + isActive);
+ break;
+ }
+ if (changed) {
+ dispatchAttributesChanged();
+ }
+ }
+
void setRssi(short rssi) {
if (mRssi != rssi) {
mRssi = rssi;
@@ -529,6 +562,17 @@
return true;
}
+ private void fetchActiveDevices() {
+ A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null) {
+ mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
+ }
+ HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+ if (headsetProfile != null) {
+ mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
+ }
+ }
+
/**
* Refreshes the UI for the BT class, including fetching the latest value
* for the class.
@@ -896,37 +940,55 @@
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
+ // Prepare the string for the Active Device summary
+ String[] activeDeviceStringsArray = mContext.getResources().getStringArray(
+ R.array.bluetooth_audio_active_device_summaries);
+ String activeDeviceString = activeDeviceStringsArray[0]; // Default value: not active
+ if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
+ activeDeviceString = activeDeviceStringsArray[1]; // Active for Media and Phone
+ } else {
+ if (mIsActiveDeviceA2dp) {
+ activeDeviceString = activeDeviceStringsArray[2]; // Active for Media only
+ }
+ if (mIsActiveDeviceHeadset) {
+ activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
+ }
+ }
+
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(
R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
+ return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp,
+ activeDeviceString);
}
} else if (a2dpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_a2dp);
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp,
+ activeDeviceString);
}
} else if (hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset);
+ return mContext.getString(R.string.bluetooth_connected_no_headset,
+ activeDeviceString);
}
} else {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_battery_level,
- batteryLevelPercentageString);
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected);
+ return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
old mode 100755
new mode 100644
index d45fe1a..ee12191
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -153,6 +153,16 @@
return BluetoothProfile.STATE_DISCONNECTED;
}
+ public boolean setActiveDevice(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.setActiveDevice(device);
+ }
+
+ public BluetoothDevice getActiveDevice() {
+ if (mService == null) return null;
+ return mService.getActiveDevice();
+ }
+
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
new file mode 100644
index 0000000..8f9e4635
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -0,0 +1,235 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class HearingAidProfile implements LocalBluetoothProfile {
+ private static final String TAG = "HearingAidProfile";
+ private static boolean V = true;
+
+ private Context mContext;
+
+ private BluetoothHearingAid mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+
+ static final String NAME = "HearingAid";
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 1;
+
+ // These callbacks run on the main thread.
+ private final class HearingAidServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothHearingAid) proxy;
+ // We just bound to the service, so refresh the UI for any connected HearingAid devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "HearingAidProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(HearingAidProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ HearingAidProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mContext = context;
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new HearingAidServiceListener(),
+ BluetoothProfile.HEARING_AID);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ // Downgrade priority as user is disconnecting the hearing aid.
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public int getVolume() {
+ if (mService == null) {
+ return 0;
+ }
+ return mService.getVolume();
+ }
+
+ public void setVolume(int volume) {
+ if (mService == null) {
+ return;
+ }
+ mService.setVolume(volume);
+ }
+
+ public long getHiSyncId(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+ return mService.getHiSyncId(device);
+ }
+
+ public int getDeviceSide(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothHearingAid.SIDE_LEFT;
+ }
+ return mService.getDeviceSide(device);
+ }
+
+ public int getDeviceMode(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothHearingAid.MODE_MONAURAL;
+ }
+ return mService.getDeviceMode(device);
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_hearing_aid;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_hearing_aid_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_hearing_aid_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_hearing_aid;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEARING_AID,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up Hearing Aid proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
old mode 100755
new mode 100644
index 22674cb..5f7ba586
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -194,8 +194,13 @@
return mState;
}
- synchronized void setBluetoothStateInt(int state) {
- mState = state;
+ void setBluetoothStateInt(int state) {
+ synchronized(this) {
+ if (mState == state) {
+ return;
+ }
+ mState = state;
+ }
if (state == BluetoothAdapter.STATE_ON) {
// if mProfileManager hasn't been constructed yet, it will
@@ -239,4 +244,8 @@
public BluetoothDevice getRemoteDevice(String address) {
return mAdapter.getRemoteDevice(address);
}
+
+ public int getMaxConnectedAudioDevices() {
+ return mAdapter.getMaxConnectedAudioDevices();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 991d922..34a099c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -21,6 +21,7 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothHidHost;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothMapClient;
@@ -91,6 +92,7 @@
private final PbapServerProfile mPbapProfile;
private final boolean mUsePbapPce;
private final boolean mUseMapClient;
+ private HearingAidProfile mHearingAidProfile;
/**
* Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -143,10 +145,14 @@
//Create PBAP server profile
if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
+
mPbapProfile = new PbapServerProfile(context);
addProfile(mPbapProfile, PbapServerProfile.NAME,
BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
+ mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+ BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
}
@@ -254,6 +260,18 @@
"Warning: PBAP Client profile was previously added but the UUID is now missing.");
}
+ //Hearing Aid Client
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
+ if (mHearingAidProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
+ mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+ BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mHearingAidProfile != null) {
+ Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing.");
+ }
+
mEventManager.registerProfileIntentReceiver();
// There is no local SDP record for HID and Settings app doesn't control PBAP Server.
@@ -416,6 +434,10 @@
return mMapClientProfile;
}
+ public HearingAidProfile getHearingAidProfile() {
+ return mHearingAidProfile;
+ }
+
/**
* Fill in a list of LocalBluetoothProfile objects that are supported by
* the local device and the remote device.
@@ -515,6 +537,12 @@
removedProfiles.remove(mPbapClientProfile);
}
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) &&
+ mHearingAidProfile != null) {
+ profiles.add(mHearingAidProfile);
+ removedProfiles.remove(mHearingAidProfile);
+ }
+
if (DEBUG) {
Log.d(TAG,"New Profiles" + profiles.toString());
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
old mode 100755
new mode 100644
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 4a73c1b..8761807 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -122,7 +122,7 @@
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_CONNECTED);
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
- when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+ when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
when(status.getCodecConfig()).thenReturn(config);
when(config.isMandatoryCodec()).thenReturn(false);
@@ -185,7 +185,7 @@
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
BluetoothCodecConfig[] configs = {config};
- when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+ when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(true);
@@ -200,7 +200,7 @@
BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
BluetoothCodecConfig[] configs = {config};
- when(mBluetoothA2dpWrapper.getCodecStatus()).thenReturn(status);
+ when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
when(config.isMandatoryCodec()).thenReturn(false);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 4091ce1..1481161 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -80,22 +80,12 @@
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
}
- /**
- * Test to verify the current test context object works so that we are not checking null
- * against null
- */
- @Test
- public void testContextMock() {
- assertThat(mContext.getString(R.string.bluetooth_connected)).isEqualTo("Connected");
- }
-
@Test
public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -105,9 +95,7 @@
mBatteryLevel = 10;
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -118,8 +106,7 @@
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -134,28 +121,23 @@
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
// Disconnect HFP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_headset_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%");
// Disconnect A2DP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_a2dp_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%");
// Disconnect both HFP and A2DP and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone or media), battery 10%");
// Disconnect all profiles and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -163,6 +145,117 @@
}
@Test
+ public void testGetConnectionSummary_testSingleProfileActiveDeviceA2dp() {
+ // Test without battery level
+ // Set A2DP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active(media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void testGetConnectionSummary_testSingleProfileActiveDeviceHfp() {
+ // Test without battery level
+ // Set HFP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for HFP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active(phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set HFP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
+ // Test without battery level
+ // Set A2DP and HFP profiles to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and HFP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active");
+
+ // Disconnect A2DP only and test connection state summary
+ mCachedDevice.setActiveDevice(false, BluetoothProfile.A2DP);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%, active(phone)");
+
+ // Disconnect HFP only and test connection state summary
+ mCachedDevice.setActiveDevice(false, BluetoothProfile.HEADSET);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%, active(media)");
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP and HFP profiles to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+ // Set A2DP and HFP profiles to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
public void testDeviceName_testAliasNameAvailable() {
when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS);
when(mDevice.getName()).thenReturn(DEVICE_NAME);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index d256b12..d32db84 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -924,6 +924,9 @@
Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
GlobalSettingsProto.CONTACTS_DATABASE_WAL_ENABLED);
dumpSetting(s, p,
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
+ GlobalSettingsProto.HIDDEN_API_BLACKLIST_EXEMPTIONS);
+ dumpSetting(s, p,
Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7d7f9ae7..04be92e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1687,18 +1687,9 @@
}
private List<String> getSettingsNamesLocked(int settingsType, int userId) {
- boolean instantApp;
- if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
- instantApp = false;
- } else {
- ApplicationInfo ai = getCallingApplicationInfoOrThrow();
- instantApp = ai.isInstantApp();
- }
- if (instantApp) {
- return new ArrayList<String>(getInstantAppAccessibleSettings(settingsType));
- } else {
- return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId);
- }
+ // Don't enforce the instant app whitelist for now -- its too prone to unintended breakage
+ // in the current form.
+ return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId);
}
private void enforceSettingReadable(String settingName, int settingsType, int userId) {
@@ -1711,8 +1702,10 @@
}
if (!getInstantAppAccessibleSettings(settingsType).contains(settingName)
&& !getOverlayInstantAppAccessibleSettings(settingsType).contains(settingName)) {
- throw new SecurityException("Setting " + settingName + " is not accessible from"
- + " ephemeral package " + getCallingPackage());
+ // Don't enforce the instant app whitelist for now -- its too prone to unintended
+ // breakage in the current form.
+ Slog.w(LOG_TAG, "Instant App " + ai.packageName
+ + " trying to access unexposed setting, this will be an error in the future.");
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 87971cb..9268c8f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -108,7 +108,7 @@
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<!-- Keyguard -->
- <uses-permission android:name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
+ <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
diff --git a/packages/SystemUI/res/layout/back.xml b/packages/SystemUI/res/layout/back.xml
index 43bec91..6843db9 100644
--- a/packages/SystemUI/res/layout/back.xml
+++ b/packages/SystemUI/res/layout/back.xml
@@ -24,8 +24,8 @@
systemui:keyCode="4"
android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_back"
- android:paddingTop="15dp"
- android:paddingBottom="15dp"
+ android:paddingTop="@dimen/home_padding"
+ android:paddingBottom="@dimen/home_padding"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/layout/recent_apps.xml b/packages/SystemUI/res/layout/recent_apps.xml
index c84d280..6b08cea 100644
--- a/packages/SystemUI/res/layout/recent_apps.xml
+++ b/packages/SystemUI/res/layout/recent_apps.xml
@@ -23,8 +23,8 @@
android:layout_weight="0"
android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_recent"
- android:paddingTop="15dp"
- android:paddingBottom="15dp"
+ android:paddingTop="@dimen/home_padding"
+ android:paddingBottom="@dimen/home_padding"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index c8ffe8f..a16ea70 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -98,4 +98,7 @@
<!-- The offsets the tasks animate from when recents is launched while docking -->
<dimen name="recents_task_stack_animation_launched_while_docking_offset">192dp</dimen>
+
+ <!-- Home button padding for sizing -->
+ <dimen name="home_padding">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4b775a5..b8411e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -608,6 +608,9 @@
public void onScanningStateChanged(boolean started) { }
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) { }
}
private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 81b8622..4320b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -76,6 +76,7 @@
@Override
public void handleSetListening(boolean listening) {
+ if (mController == null) return;
if (listening) {
mController.addCallback(mCallback);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 9e265e22..52b4c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -235,6 +235,7 @@
public void handleSetListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
+ if (mController == null) return;
if (mListening) {
mController.addCallback(mZenCallback);
Prefs.registerListener(mContext, mPrefListener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index b3ff4e5b..12daff1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -98,6 +98,8 @@
protected void handleUpdateState(BooleanState state, Object arg) {
final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled);
final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled);
+
+ if (getAdapter() == null) return;
state.value = getAdapter().isEnabled();
state.label = mContext.getString(R.string.quick_settings_nfc_label);
state.icon = new DrawableIcon(state.value ? mEnable : mDisable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 2370273..fdbb260 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -76,6 +76,7 @@
@Override
public void handleSetListening(boolean listening) {
+ if (mController == null) return;
if (listening) {
mController.addCallback(mSignalCallback);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 4e79314b..9f89fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -219,6 +219,11 @@
newLayout = getDefaultLayout();
}
String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+ if (sets.length != 3) {
+ Log.d(TAG, "Invalid layout.");
+ newLayout = getDefaultLayout();
+ sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+ }
String[] start = sets[0].split(BUTTON_SEPARATOR);
String[] center = sets[1].split(BUTTON_SEPARATOR);
String[] end = sets[2].split(BUTTON_SEPARATOR);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 3b15c2b..fcf084b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -276,6 +276,9 @@
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
ActuallyCachedState state = mCachedState.get(device);
if (state == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
index 2951943..2ede327 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java
@@ -74,17 +74,9 @@
}
}
- private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+ private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
@Override
- public void onUidRulesChanged(int uid, int uidRules) throws RemoteException {
- }
-
- @Override
- public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
- }
-
- @Override
- public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
+ public void onRestrictBackgroundChanged(final boolean isDataSaving) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -92,10 +84,6 @@
}
});
}
-
- @Override
- public void onUidPoliciesChanged(int uid, int uidPolicies) throws RemoteException {
- }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index b08b26d..e467903 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -49,7 +49,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.qs.tiles.DndTile;
@@ -340,11 +339,15 @@
private boolean shouldShowUI(int flags) {
updateStatusBar();
- return mStatusBar != null
- && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
- && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
+ // if status bar isn't null, check if phone is in AOD, else check flags
+ // since we could be using a different status bar
+ return mStatusBar != null ?
+ mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
+ && mStatusBar.getWakefulnessState() !=
+ WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
&& mStatusBar.isDeviceInteractive()
- && (flags & AudioManager.FLAG_SHOW_UI) != 0;
+ && (flags & AudioManager.FLAG_SHOW_UI) != 0
+ : (flags & AudioManager.FLAG_SHOW_UI) != 0;
}
boolean onVolumeChangedW(int stream, int flags) {
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
old mode 100644
new mode 100755
index e970367..dc2707b
--- a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -22,11 +22,15 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
import android.os.IBinder;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
@@ -216,7 +220,27 @@
intent.setClassName(mContext, lastapp.className);
intent.setComponent(new ComponentName(lastapp.packageName,
lastapp.className));
- if (mContext.startService(intent) == null) {
+ PackageManager pm = mContext.getPackageManager();
+ PowerManager powerManager =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ try {
+ ApplicationInfo appInfo = pm.getApplicationInfo(lastapp.packageName, 0);
+ if (appInfo.targetSdkVersion < Build.VERSION_CODES.O ||
+ powerManager.isIgnoringBatteryOptimizations(lastapp.packageName)) {
+ if (mContext.startService(intent) == null) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ } else {
+ if (mContext.startForegroundService(intent) == null) {
+ Log.w(LOG_TAG, "invalid name " +
+ lastapp.packageName + "/" + lastapp.className);
+ return WapPushManagerParams.INVALID_RECEIVER_NAME;
+ }
+ }
+
+ } catch (NameNotFoundException e) {
Log.w(LOG_TAG, "invalid name " +
lastapp.packageName + "/" + lastapp.className);
return WapPushManagerParams.INVALID_RECEIVER_NAME;
diff --git a/proto/Android.bp b/proto/Android.bp
index 95f453c..f3811bd 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -6,6 +6,8 @@
},
srcs: ["src/**/*.proto"],
no_framework_libs: true,
+ // 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",
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d9713a5..2077790 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -60,6 +60,7 @@
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.util.DumpUtils;
import com.android.server.pm.UserRestrictionsUtils;
@@ -415,9 +416,14 @@
int systemUiUid = -1;
try {
- systemUiUid = mContext.getPackageManager()
- .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
- UserHandle.USER_SYSTEM);
+ // Check if device is configured with no home screen, which implies no SystemUI.
+ boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
+ if (!noHome) {
+ systemUiUid = mContext.getPackageManager()
+ .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
+ }
+ Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
@@ -433,10 +439,17 @@
Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
}
+ private boolean supportBluetoothPersistedState() {
+ return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState);
+ }
+
/**
* Returns true if the Bluetooth saved state is "on"
*/
private boolean isBluetoothPersistedStateOn() {
+ if (!supportBluetoothPersistedState()) {
+ return false;
+ }
int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
if (DBG) {
Slog.d(TAG, "Bluetooth persisted state: " + state);
@@ -448,6 +461,9 @@
* Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
*/
private boolean isBluetoothPersistedStateOnBluetooth() {
+ if (!supportBluetoothPersistedState()) {
+ return false;
+ }
return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON,
BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5228498..fd2ef18 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -30,6 +30,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -63,6 +65,7 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
+import android.net.NetworkPolicyManager;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
@@ -104,6 +107,7 @@
import android.security.KeyStore;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.LocalLog.ReadOnlyLocalLog;
import android.util.Log;
@@ -130,6 +134,8 @@
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.DnsManager;
+import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
@@ -171,6 +177,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -224,7 +231,11 @@
@GuardedBy("mVpns")
private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
+ // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
+ // a direct call to LockdownVpnTracker.isEnabled().
+ @GuardedBy("mVpns")
private boolean mLockdownEnabled;
+ @GuardedBy("mVpns")
private LockdownVpnTracker mLockdownTracker;
final private Context mContext;
@@ -232,8 +243,6 @@
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
- private int mNumDnsEntries;
-
private boolean mTestMode;
private static ConnectivityService sServiceInstance;
@@ -396,6 +405,9 @@
*/
private static final int EVENT_REVALIDATE_NETWORK = 36;
+ // Handle changes in Private DNS settings.
+ private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -407,6 +419,7 @@
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
final private NetworkStateTrackerHandler mTrackerHandler;
+ private final DnsManager mDnsManager;
private boolean mSystemReady;
private Intent mInitialBroadcast;
@@ -444,8 +457,8 @@
private LingerMonitor mLingerMonitor;
// sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
- private final static int MIN_NET_ID = 100; // some reserved marks
- private final static int MAX_NET_ID = 65535;
+ private static final int MIN_NET_ID = 100; // some reserved marks
+ private static final int MAX_NET_ID = 65535 - 0x0400; // Top 1024 bits reserved by IpSecService
private int mNextNetId = MIN_NET_ID;
// sequence number of NetworkRequests
@@ -700,12 +713,12 @@
mSystemProperties = getSystemProperties();
mMetricsLog = logger;
- mDefaultRequest = createInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
+ mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
mNetworkRequests.put(mDefaultRequest, defaultNRI);
mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
- mDefaultMobileDataRequest = createInternetRequestForTransport(
+ mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
mHandlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -857,6 +870,9 @@
mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
mMultinetworkPolicyTracker.start();
+
+ mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+ registerPrivateDnsSettingsCallbacks();
}
private Tethering makeTethering() {
@@ -867,7 +883,7 @@
deps);
}
- private NetworkRequest createInternetRequestForTransport(
+ private NetworkRequest createDefaultInternetRequestForTransport(
int transportType, NetworkRequest.Type type) {
NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
@@ -917,6 +933,12 @@
EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
}
+ private void registerPrivateDnsSettingsCallbacks() {
+ for (Uri u : DnsManager.getPrivateDnsSettingsUris()) {
+ mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
+ }
+ }
+
private synchronized int nextNetworkRequestId() {
return mNextNetworkRequestId++;
}
@@ -972,9 +994,9 @@
}
private Network[] getVpnUnderlyingNetworks(int uid) {
- if (!mLockdownEnabled) {
- int user = UserHandle.getUserId(uid);
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ if (!mLockdownEnabled) {
+ int user = UserHandle.getUserId(uid);
Vpn vpn = mVpns.get(user);
if (vpn != null && vpn.appliesToUid(uid)) {
return vpn.getUnderlyingNetworks();
@@ -1062,8 +1084,10 @@
if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) {
state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
}
- if (mLockdownTracker != null) {
- mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.augmentNetworkInfo(state.networkInfo);
+ }
}
}
@@ -1228,8 +1252,8 @@
result.put(nai.network, nc);
}
- if (!mLockdownEnabled) {
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ if (!mLockdownEnabled) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
Network[] networks = vpn.getUnderlyingNetworks();
@@ -1305,7 +1329,9 @@
if (nai != null) {
synchronized (nai) {
if (nai.networkCapabilities != null) {
- return new NetworkCapabilities(nai.networkCapabilities);
+ // TODO : don't remove the UIDs when communicating with processes
+ // that have the NETWORK_SETTINGS permission.
+ return networkCapabilitiesWithoutUids(nai.networkCapabilities);
}
}
}
@@ -1318,6 +1344,10 @@
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
}
+ private NetworkCapabilities networkCapabilitiesWithoutUids(NetworkCapabilities nc) {
+ return new NetworkCapabilities(nc).setUids(null);
+ }
+
@Override
public NetworkState[] getAllNetworkState() {
// Require internal since we're handing out IMSI details
@@ -1327,6 +1357,10 @@
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai != null) {
+ // TODO (b/73321673) : NetworkState contains a copy of the
+ // NetworkCapabilities, which may contain UIDs of apps to which the
+ // network applies. Should the UIDs be cleared so as not to leak or
+ // interfere ?
result.add(nai.getNetworkState());
}
}
@@ -1459,15 +1493,12 @@
return true;
}
- private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+ private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
// TODO: notify UID when it has requested targeted updates
}
@Override
- public void onMeteredIfacesChanged(String[] meteredIfaces) {
- }
- @Override
public void onRestrictBackgroundChanged(boolean restrictBackground) {
// TODO: relocate this specific callback in Tethering.
if (restrictBackground) {
@@ -1475,9 +1506,6 @@
mTethering.untetherAll();
}
}
- @Override
- public void onUidPoliciesChanged(int uid, int uidPolicies) {
- }
};
/**
@@ -1555,9 +1583,11 @@
}
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
- if (mLockdownTracker != null) {
- info = new NetworkInfo(info);
- mLockdownTracker.augmentNetworkInfo(info);
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ info = new NetworkInfo(info);
+ mLockdownTracker.augmentNetworkInfo(info);
+ }
}
Intent intent = new Intent(bcastType);
@@ -1803,24 +1833,6 @@
}
}
- private void flushVmDnsCache() {
- /*
- * Tell the VMs to toss their DNS caches
- */
- Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- /*
- * Connectivity events can happen before boot has completed ...
- */
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
@Override
public int getRestoreDefaultNetworkDelay(int networkType) {
String restoreDefaultNetworkDelayStr = mSystemProperties.get(
@@ -2052,24 +2064,6 @@
if (score != null) updateNetworkScore(nai, score.intValue());
break;
}
- case NetworkAgent.EVENT_UID_RANGES_ADDED: {
- try {
- mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
- } catch (Exception e) {
- // Never crash!
- loge("Exception in addVpnUidRanges: " + e);
- }
- break;
- }
- case NetworkAgent.EVENT_UID_RANGES_REMOVED: {
- try {
- mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
- } catch (Exception e) {
- // Never crash!
- loge("Exception in removeVpnUidRanges: " + e);
- }
- break;
- }
case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
if (nai.everConnected && !nai.networkMisc.explicitlySelected) {
loge("ERROR: already-connected network explicitly selected.");
@@ -2094,36 +2088,59 @@
synchronized (mNetworkForNetId) {
nai = mNetworkForNetId.get(msg.arg2);
}
- if (nai != null) {
- final boolean valid =
- (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
- final boolean wasValidated = nai.lastValidated;
- final boolean wasDefault = isDefaultNetwork(nai);
- if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
- (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
- if (valid != nai.lastValidated) {
- if (wasDefault) {
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
- }
- final int oldScore = nai.getCurrentScore();
- nai.lastValidated = valid;
- nai.everValidated |= valid;
- updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkFactories. b/17726566
- if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ if (nai == null) break;
+
+ final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
+ final boolean wasValidated = nai.lastValidated;
+ final boolean wasDefault = isDefaultNetwork(nai);
+
+ final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig)
+ ? (PrivateDnsConfig) msg.obj : null;
+ final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
+
+ final boolean reevaluationRequired;
+ final String logMsg;
+ if (valid) {
+ reevaluationRequired = updatePrivateDns(nai, privateDnsCfg);
+ logMsg = (DBG && (privateDnsCfg != null))
+ ? " with " + privateDnsCfg.toString() : "";
+ } else {
+ reevaluationRequired = false;
+ logMsg = (DBG && !TextUtils.isEmpty(redirectUrl))
+ ? " with redirect to " + redirectUrl : "";
+ }
+ if (DBG) {
+ log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+ }
+ // If there is a change in Private DNS configuration,
+ // trigger reevaluation of the network to test it.
+ if (reevaluationRequired) {
+ nai.networkMonitor.sendMessage(
+ NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
+ break;
+ }
+ if (valid != nai.lastValidated) {
+ if (wasDefault) {
+ metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
+ SystemClock.elapsedRealtime(), valid);
}
- updateInetCondition(nai);
- // Let the NetworkAgent know the state of its network
- Bundle redirectUrlBundle = new Bundle();
- redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj);
- nai.asyncChannel.sendMessage(
- NetworkAgent.CMD_REPORT_NETWORK_STATUS,
- (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
- 0, redirectUrlBundle);
- if (wasValidated && !nai.lastValidated) {
- handleNetworkUnvalidated(nai);
- }
+ final int oldScore = nai.getCurrentScore();
+ nai.lastValidated = valid;
+ nai.everValidated |= valid;
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ // If score has changed, rebroadcast to NetworkFactories. b/17726566
+ if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ }
+ updateInetCondition(nai);
+ // Let the NetworkAgent know the state of its network
+ Bundle redirectUrlBundle = new Bundle();
+ redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+ 0, redirectUrlBundle);
+ if (wasValidated && !nai.lastValidated) {
+ handleNetworkUnvalidated(nai);
}
break;
}
@@ -2163,6 +2180,21 @@
}
break;
}
+ case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
+ final NetworkAgentInfo nai;
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(msg.arg2);
+ }
+ if (nai == null) break;
+
+ final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj;
+ final boolean reevaluationRequired = updatePrivateDns(nai, cfg);
+ if (nai.lastValidated && reevaluationRequired) {
+ nai.networkMonitor.sendMessage(
+ NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
+ }
+ break;
+ }
}
return true;
}
@@ -2198,6 +2230,63 @@
}
}
+ private void handlePrivateDnsSettingsChanged() {
+ final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
+
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ // Private DNS only ever applies to networks that might provide
+ // Internet access and therefore also require validation.
+ if (!NetworkMonitor.isValidationRequired(
+ mDefaultRequest.networkCapabilities, nai.networkCapabilities)) {
+ continue;
+ }
+
+ // Notify the NetworkMonitor thread in case it needs to cancel or
+ // schedule DNS resolutions. If a DNS resolution is required the
+ // result will be sent back to us.
+ nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
+
+ if (!cfg.inStrictMode()) {
+ // No strict mode hostname DNS resolution needed, so just update
+ // DNS settings directly. In opportunistic and "off" modes this
+ // just reprograms netd with the network-supplied DNS servers
+ // (and of course the boolean of whether or not to attempt TLS).
+ //
+ // TODO: Consider code flow parity with strict mode, i.e. having
+ // NetworkMonitor relay the PrivateDnsConfig back to us and then
+ // performing this call at that time.
+ updatePrivateDns(nai, cfg);
+ }
+ }
+ }
+
+ private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
+ final boolean reevaluationRequired = true;
+ final boolean dontReevaluate = false;
+
+ final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg);
+ updateDnses(nai.linkProperties, null, nai.network.netId);
+
+ if (newCfg == null) {
+ if (oldCfg == null) return dontReevaluate;
+ return oldCfg.useTls ? reevaluationRequired : dontReevaluate;
+ }
+
+ if (oldCfg == null) {
+ return newCfg.useTls ? reevaluationRequired : dontReevaluate;
+ }
+
+ if (oldCfg.useTls != newCfg.useTls) {
+ return reevaluationRequired;
+ }
+
+ if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) {
+ return reevaluationRequired;
+ }
+
+ return dontReevaluate;
+ }
+
private void updateLingerState(NetworkAgentInfo nai, long now) {
// 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
// 2. If the network was lingering and there are now requests, unlinger it.
@@ -2332,6 +2421,7 @@
} catch (Exception e) {
loge("Exception removing network: " + e);
}
+ mDnsManager.removeNetwork(nai.network);
}
synchronized (mNetworkForNetId) {
mNetIdInUse.delete(nai.network.netId);
@@ -2488,6 +2578,7 @@
private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
nri.unlinkDeathRecipient();
mNetworkRequests.remove(nri.request);
+
synchronized (mUidToNetworkRequestCount) {
int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
if (requests < 1) {
@@ -2500,6 +2591,7 @@
mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
}
}
+
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
boolean wasKept = false;
@@ -2876,6 +2968,9 @@
handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2));
break;
}
+ case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
+ handlePrivateDnsSettingsChanged();
+ break;
}
}
}
@@ -3422,9 +3517,9 @@
public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
int userId) {
enforceCrossUserPermission(userId);
- throwIfLockdownEnabled();
synchronized (mVpns) {
+ throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
return vpn.prepare(oldPackage, newPackage);
@@ -3468,9 +3563,9 @@
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).establish(config);
}
}
@@ -3481,13 +3576,13 @@
*/
@Override
public void startLegacyVpn(VpnProfile profile) {
- throwIfLockdownEnabled();
+ int user = UserHandle.getUserId(Binder.getCallingUid());
final LinkProperties egress = getActiveLinkProperties();
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
}
- int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
}
}
@@ -3513,11 +3608,11 @@
@Override
public VpnInfo[] getAllVpnInfo() {
enforceConnectivityInternalPermission();
- if (mLockdownEnabled) {
- return new VpnInfo[0];
- }
-
synchronized (mVpns) {
+ if (mLockdownEnabled) {
+ return new VpnInfo[0];
+ }
+
List<VpnInfo> infoList = new ArrayList<>();
for (int i = 0; i < mVpns.size(); i++) {
VpnInfo info = createVpnInfo(mVpns.valueAt(i));
@@ -3582,33 +3677,33 @@
return false;
}
- // Tear down existing lockdown if profile was removed
- mLockdownEnabled = LockdownVpnTracker.isEnabled();
- if (mLockdownEnabled) {
- byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
- if (profileTag == null) {
- Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
- return false;
- }
- String profileName = new String(profileTag);
- final VpnProfile profile = VpnProfile.decode(
- profileName, mKeyStore.get(Credentials.VPN + profileName));
- if (profile == null) {
- Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
- setLockdownTracker(null);
- return true;
- }
- int user = UserHandle.getUserId(Binder.getCallingUid());
- synchronized (mVpns) {
+ synchronized (mVpns) {
+ // Tear down existing lockdown if profile was removed
+ mLockdownEnabled = LockdownVpnTracker.isEnabled();
+ if (mLockdownEnabled) {
+ byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
+ if (profileTag == null) {
+ Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
+ return false;
+ }
+ String profileName = new String(profileTag);
+ final VpnProfile profile = VpnProfile.decode(
+ profileName, mKeyStore.get(Credentials.VPN + profileName));
+ if (profile == null) {
+ Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+ setLockdownTracker(null);
+ return true;
+ }
+ int user = UserHandle.getUserId(Binder.getCallingUid());
Vpn vpn = mVpns.get(user);
if (vpn == null) {
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+ } else {
+ setLockdownTracker(null);
}
- } else {
- setLockdownTracker(null);
}
return true;
@@ -3618,6 +3713,7 @@
* Internally set new {@link LockdownVpnTracker}, shutting down any existing
* {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
*/
+ @GuardedBy("mVpns")
private void setLockdownTracker(LockdownVpnTracker tracker) {
// Shutdown any existing tracker
final LockdownVpnTracker existing = mLockdownTracker;
@@ -3632,6 +3728,7 @@
}
}
+ @GuardedBy("mVpns")
private void throwIfLockdownEnabled() {
if (mLockdownEnabled) {
throw new IllegalStateException("Unavailable in lockdown mode");
@@ -3679,12 +3776,12 @@
enforceConnectivityInternalPermission();
enforceCrossUserPermission(userId);
- // Can't set always-on VPN if legacy VPN is already in lockdown mode.
- if (LockdownVpnTracker.isEnabled()) {
- return false;
- }
-
synchronized (mVpns) {
+ // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+ if (LockdownVpnTracker.isEnabled()) {
+ return false;
+ }
+
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
@@ -3860,9 +3957,9 @@
}
userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
mVpns.put(userId, userVpn);
- }
- if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
- updateLockdownVpn();
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ }
}
}
@@ -3899,11 +3996,13 @@
}
private void onUserUnlocked(int userId) {
- // User present may be sent because of an unlock, which might mean an unlocked keystore.
- if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
- updateLockdownVpn();
- } else {
- startAlwaysOnVpn(userId);
+ synchronized (mVpns) {
+ // User present may be sent because of an unlock, which might mean an unlocked keystore.
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ } else {
+ startAlwaysOnVpn(userId);
+ }
}
}
@@ -4103,6 +4202,7 @@
// the system default network.
if (type == NetworkRequest.Type.TRACK_DEFAULT) {
networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities);
+ networkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
enforceAccessPermission();
} else {
networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -4113,6 +4213,13 @@
enforceMeteredApnPolicy(networkCapabilities);
}
ensureRequestableCapabilities(networkCapabilities);
+ // Set the UID range for this request to the single UID of the requester.
+ // This will overwrite any allowed UIDs in the requested capabilities. Though there
+ // are no visible methods to set the UIDs, an app could use reflection to try and get
+ // networks for other apps so it's essential that the UIDs are overwritten.
+ // TODO : don't forcefully set the UID when communicating with processes
+ // that have the NETWORK_SETTINGS permission.
+ networkCapabilities.setSingleUid(Binder.getCallingUid());
if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
@@ -4186,6 +4293,9 @@
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
ensureValidNetworkSpecifier(networkCapabilities);
+ // TODO : don't forcefully set the UID when communicating with processes
+ // that have the NETWORK_SETTINGS permission.
+ networkCapabilities.setSingleUid(Binder.getCallingUid());
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -4239,6 +4349,9 @@
}
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
+ // TODO : don't forcefully set the UIDs when communicating with processes
+ // that have the NETWORK_SETTINGS permission.
+ nc.setSingleUid(Binder.getCallingUid());
if (!ConnectivityManager.checkChangePermission(mContext)) {
// Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
// make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
@@ -4267,8 +4380,12 @@
}
ensureValidNetworkSpecifier(networkCapabilities);
- NetworkRequest networkRequest = new NetworkRequest(
- new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(),
+ final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
+ // TODO : don't forcefully set the UIDs when communicating with processes
+ // that have the NETWORK_SETTINGS permission.
+ nc.setSingleUid(Binder.getCallingUid());
+
+ NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
if (VDBG) log("pendingListenForNetwork for " + nri);
@@ -4387,10 +4504,12 @@
lp.ensureDirectlyConnectedRoutes();
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
+ final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
- new Network(reserveNetId()), new NetworkInfo(networkInfo), lp,
- new NetworkCapabilities(networkCapabilities), currentScore,
+ new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
+ // Make sure the network capabilities reflect what the agent info says.
+ nai.networkCapabilities = mixInCapabilities(nai, nc);
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
@@ -4411,6 +4530,7 @@
NetworkInfo networkInfo = na.networkInfo;
na.networkInfo = null;
updateNetworkInfo(na, networkInfo);
+ updateUids(na, null, na.networkCapabilities);
}
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
@@ -4558,41 +4678,18 @@
return; // no updating necessary
}
- Collection<InetAddress> dnses = newLp.getDnsServers();
- if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses);
+ final NetworkAgentInfo defaultNai = getDefaultNetwork();
+ final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);
+
+ if (DBG) {
+ final Collection<InetAddress> dnses = newLp.getDnsServers();
+ log("Setting DNS servers for network " + netId + " to " + dnses);
+ }
try {
- mNetd.setDnsConfigurationForNetwork(
- netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
+ mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork);
} catch (Exception e) {
loge("Exception in setDnsConfigurationForNetwork: " + e);
}
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
- if (defaultNai != null && defaultNai.network.netId == netId) {
- setDefaultDnsSystemProperties(dnses);
- }
- flushVmDnsCache();
- }
-
- private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
- int last = 0;
- for (InetAddress dns : dnses) {
- ++last;
- setNetDnsProperty(last, dns.getHostAddress());
- }
- for (int i = last + 1; i <= mNumDnsEntries; ++i) {
- setNetDnsProperty(i, "");
- }
- mNumDnsEntries = last;
- }
-
- private void setNetDnsProperty(int which, String value) {
- final String key = "net.dns" + which;
- // Log and forget errors setting unsupported properties.
- try {
- mSystemProperties.set(key, value);
- } catch (Exception e) {
- Log.e(TAG, "Error setting unsupported net.dns property: ", e);
- }
}
private String getNetworkPermission(NetworkCapabilities nc) {
@@ -4607,51 +4704,72 @@
}
/**
- * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities}
- * augmented with any stateful capabilities implied from {@code networkAgent}
- * (e.g., validated status and captive portal status).
- *
- * @param oldScore score of the network before any of the changes that prompted us
- * to call this function.
- * @param nai the network having its capabilities updated.
- * @param networkCapabilities the new network capabilities.
+ * Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
+ * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
+ * and foreground status).
*/
- private void updateCapabilities(
- int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
+ private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
- if (nai.everConnected && !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
- networkCapabilities)) {
- // TODO: consider not complaining when a network agent degrade its capabilities if this
+ if (nai.everConnected &&
+ !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(nc)) {
+ // TODO: consider not complaining when a network agent degrades its capabilities if this
// does not cause any request (that is not a listen) currently matching that agent to
// stop being matched by the updated agent.
- String diff = nai.networkCapabilities.describeImmutableDifferences(networkCapabilities);
+ String diff = nai.networkCapabilities.describeImmutableDifferences(nc);
if (!TextUtils.isEmpty(diff)) {
Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
}
}
// Don't modify caller's NetworkCapabilities.
- networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ NetworkCapabilities newNc = new NetworkCapabilities(nc);
if (nai.lastValidated) {
- networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
+ newNc.addCapability(NET_CAPABILITY_VALIDATED);
} else {
- networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
+ newNc.removeCapability(NET_CAPABILITY_VALIDATED);
}
if (nai.lastCaptivePortalDetected) {
- networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ newNc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
} else {
- networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+ newNc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
}
if (nai.isBackgroundNetwork()) {
- networkCapabilities.removeCapability(NET_CAPABILITY_FOREGROUND);
+ newNc.removeCapability(NET_CAPABILITY_FOREGROUND);
} else {
- networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
+ newNc.addCapability(NET_CAPABILITY_FOREGROUND);
+ }
+ if (nai.isSuspended()) {
+ newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ } else {
+ newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
- if (Objects.equals(nai.networkCapabilities, networkCapabilities)) return;
+ return newNc;
+ }
+
+ /**
+ * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
+ *
+ * 1. Calls mixInCapabilities to merge the passed-in NetworkCapabilities {@code nc} with the
+ * capabilities we manage and store in {@code nai}, such as validated status and captive
+ * portal status)
+ * 2. Takes action on the result: changes network permissions, sends CAP_CHANGED callbacks, and
+ * potentially triggers rematches.
+ * 3. Directly informs other network stack components (NetworkStatsService, VPNs, etc. of the
+ * change.)
+ *
+ * @param oldScore score of the network before any of the changes that prompted us
+ * to call this function.
+ * @param nai the network having its capabilities updated.
+ * @param nc the new network capabilities.
+ */
+ private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
+ NetworkCapabilities newNc = mixInCapabilities(nai, nc);
+
+ if (Objects.equals(nai.networkCapabilities, newNc)) return;
final String oldPermission = getNetworkPermission(nai.networkCapabilities);
- final String newPermission = getNetworkPermission(networkCapabilities);
+ final String newPermission = getNetworkPermission(newNc);
if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
try {
mNetd.setNetworkPermission(nai.network.netId, newPermission);
@@ -4663,11 +4781,12 @@
final NetworkCapabilities prevNc;
synchronized (nai) {
prevNc = nai.networkCapabilities;
- nai.networkCapabilities = networkCapabilities;
+ nai.networkCapabilities = newNc;
}
- if (nai.getCurrentScore() == oldScore &&
- networkCapabilities.equalRequestableCapabilities(prevNc)) {
+ updateUids(nai, prevNc, newNc);
+
+ if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) {
// If the requestable capabilities haven't changed, and the score hasn't changed, then
// the change we're processing can't affect any requests, it can only affect the listens
// on this network. We might have been called by rematchNetworkAndRequests when a
@@ -4683,15 +4802,15 @@
// Report changes that are interesting for network statistics tracking.
if (prevNc != null) {
final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
- networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
+ newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
- networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
if (meteredChanged || roamingChanged) {
notifyIfacesChangedForNetworkStats();
}
}
- if (!networkCapabilities.hasTransport(TRANSPORT_VPN)) {
+ if (!newNc.hasTransport(TRANSPORT_VPN)) {
// Tell VPNs about updated capabilities, since they may need to
// bubble those changes through.
synchronized (mVpns) {
@@ -4703,6 +4822,34 @@
}
}
+ private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
+ NetworkCapabilities newNc) {
+ Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
+ Set<UidRange> newRanges = null == newNc ? null : newNc.getUids();
+ if (null == prevRanges) prevRanges = new ArraySet<>();
+ if (null == newRanges) newRanges = new ArraySet<>();
+ final Set<UidRange> prevRangesCopy = new ArraySet<>(prevRanges);
+
+ prevRanges.removeAll(newRanges);
+ newRanges.removeAll(prevRangesCopy);
+
+ try {
+ if (!newRanges.isEmpty()) {
+ final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
+ newRanges.toArray(addedRangesArray);
+ mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+ }
+ if (!prevRanges.isEmpty()) {
+ final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
+ prevRanges.toArray(removedRangesArray);
+ mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+ }
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in updateUids: " + e);
+ }
+ }
+
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
if (mNetworkForNetId.get(nai.network.netId) != nai) {
// Ignore updates for disconnected networks
@@ -4776,7 +4923,7 @@
releasePendingNetworkRequestWithDelay(pendingIntent);
}
- private static void callCallbackForRequest(NetworkRequestInfo nri,
+ private void callCallbackForRequest(NetworkRequestInfo nri,
NetworkAgentInfo networkAgent, int notificationType, int arg1) {
if (nri.messenger == null) {
return; // Default request has no msgr
@@ -4789,12 +4936,20 @@
putParcelable(bundle, networkAgent.network);
}
switch (notificationType) {
+ case ConnectivityManager.CALLBACK_AVAILABLE: {
+ putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
+ putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ break;
+ }
case ConnectivityManager.CALLBACK_LOSING: {
msg.arg1 = arg1;
break;
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
- putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
+ // networkAgent can't be null as it has been accessed a few lines above.
+ final NetworkCapabilities nc =
+ networkCapabilitiesWithoutUids(networkAgent.networkCapabilities);
+ putParcelable(bundle, nc);
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -4862,10 +5017,12 @@
} catch (Exception e) {
loge("Exception setting default network :" + e);
}
+
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
updateTcpBufferSizes(newNetwork);
- setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+ mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+ notifyIfacesChangedForNetworkStats();
}
private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -5215,11 +5372,13 @@
}
private void notifyLockdownVpn(NetworkAgentInfo nai) {
- if (mLockdownTracker != null) {
- if (nai != null && nai.isVPN()) {
- mLockdownTracker.onVpnStateChanged(nai.networkInfo);
- } else {
- mLockdownTracker.onNetworkInfoChanged();
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ if (nai != null && nai.isVPN()) {
+ mLockdownTracker.onVpnStateChanged(nai.networkInfo);
+ } else {
+ mLockdownTracker.onNetworkInfoChanged();
+ }
}
}
}
@@ -5314,6 +5473,7 @@
}
}
}
+ updateUids(networkAgent, networkAgent.networkCapabilities, null);
}
} else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
state == NetworkInfo.State.SUSPENDED) {
@@ -5321,6 +5481,10 @@
if (networkAgent.getCurrentScore() != oldScore) {
rematchAllNetworksAndRequests(networkAgent, oldScore);
}
+ updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
+ networkAgent.networkCapabilities);
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
ConnectivityManager.CALLBACK_SUSPENDED :
ConnectivityManager.CALLBACK_RESUMED));
@@ -5357,14 +5521,6 @@
}
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
- // Whether a network is currently suspended is also an important
- // element of state to be transferred (it would not otherwise be
- // delivered by any currently available mechanism).
- if (nai.networkInfo.getState() == NetworkInfo.State.SUSPENDED) {
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_SUSPENDED, 0);
- }
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_CAP_CHANGED, 0);
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_IP_CHANGED, 0);
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
@@ -5437,44 +5593,62 @@
}
/**
+ * Returns the list of all interfaces that could be used by network traffic that does not
+ * explicitly specify a network. This includes the default network, but also all VPNs that are
+ * currently connected.
+ *
+ * Must be called on the handler thread.
+ */
+ private Network[] getDefaultNetworks() {
+ ArrayList<Network> defaultNetworks = new ArrayList<>();
+ NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
+ defaultNetworks.add(nai.network);
+ }
+ }
+ return defaultNetworks.toArray(new Network[0]);
+ }
+
+ /**
* Notify NetworkStatsService that the set of active ifaces has changed, or that one of the
* properties tracked by NetworkStatsService on an active iface has changed.
*/
private void notifyIfacesChangedForNetworkStats() {
try {
- mStatsService.forceUpdateIfaces();
+ mStatsService.forceUpdateIfaces(getDefaultNetworks());
} catch (Exception ignored) {
}
}
@Override
public boolean addVpnAddress(String address, int prefixLength) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).addAddress(address, prefixLength);
}
}
@Override
public boolean removeVpnAddress(String address, int prefixLength) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized (mVpns) {
+ throwIfLockdownEnabled();
return mVpns.get(user).removeAddress(address, prefixLength);
}
}
@Override
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
- throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
- boolean success;
+ final boolean success;
synchronized (mVpns) {
+ throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
- notifyIfacesChangedForNetworkStats();
+ mHandler.post(() -> notifyIfacesChangedForNetworkStats());
}
return success;
}
@@ -5530,31 +5704,31 @@
setAlwaysOnVpnPackage(userId, null, false);
setVpnPackageAuthorization(alwaysOnPackage, userId, false);
}
- }
- // Turn Always-on VPN off
- if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mKeyStore.delete(Credentials.LOCKDOWN_VPN);
- mLockdownEnabled = false;
- setLockdownTracker(null);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ // Turn Always-on VPN off
+ if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+ mLockdownEnabled = false;
+ setLockdownTracker(null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- }
- // Turn VPN off
- VpnConfig vpnConfig = getVpnConfig(userId);
- if (vpnConfig != null) {
- if (vpnConfig.legacy) {
- prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
- } else {
- // Prevent this app (packagename = vpnConfig.user) from initiating VPN connections
- // in the future without user intervention.
- setVpnPackageAuthorization(vpnConfig.user, userId, false);
+ // Turn VPN off
+ VpnConfig vpnConfig = getVpnConfig(userId);
+ if (vpnConfig != null) {
+ if (vpnConfig.legacy) {
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
+ } else {
+ // Prevent this app (packagename = vpnConfig.user) from initiating
+ // VPN connections in the future without user intervention.
+ setVpnPackageAuthorization(vpnConfig.user, userId, false);
- prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+ prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d3ab125..45a4dfb9 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -19,11 +19,13 @@
import static android.Manifest.permission.DUMP;
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.IIpSecService;
import android.net.INetd;
import android.net.IpSecAlgorithm;
@@ -32,7 +34,9 @@
import android.net.IpSecSpiResponse;
import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
+import android.net.IpSecTunnelInterfaceResponse;
import android.net.IpSecUdpEncapResponse;
+import android.net.Network;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.net.util.NetdService;
@@ -48,9 +52,11 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -60,7 +66,6 @@
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils;
@@ -81,7 +86,8 @@
private static final String NETD_SERVICE_NAME = "netd";
private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN};
+ new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN};
+ private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"};
private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms
private static final int MAX_PORT_BIND_ATTEMPTS = 10;
@@ -97,12 +103,19 @@
static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved
static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer
+ static final String TUNNEL_INTERFACE_PREFIX = "ipsec";
/* Binder context for this service */
private final Context mContext;
- /** Should be a never-repeating global ID for resources */
- private static AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0);
+ /**
+ * The next non-repeating global ID for tracking resources between users, this service, and
+ * kernel data structures. Accessing this variable is not thread safe, so it is only read or
+ * modified within blocks synchronized on IpSecService.this. We want to avoid -1
+ * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it).
+ */
+ @GuardedBy("IpSecService.this")
+ private int mNextResourceId = 1;
interface IpSecServiceConfiguration {
INetd getNetdInstance() throws RemoteException;
@@ -140,7 +153,7 @@
* resources.
*
* <p>References to the IResource object may be held by other RefcountedResource objects,
- * and as such, the kernel resources and quota may not be cleaned up.
+ * and as such, the underlying resources and quota may not be cleaned up.
*/
void invalidate() throws RemoteException;
@@ -290,7 +303,12 @@
}
}
- /* Very simple counting class that looks much like a counting semaphore */
+ /**
+ * Very simple counting class that looks much like a counting semaphore
+ *
+ * <p>This class is not thread-safe, and expects that that users of this class will ensure
+ * synchronization and thread safety by holding the IpSecService.this instance lock.
+ */
@VisibleForTesting
static class ResourceTracker {
private final int mMax;
@@ -333,27 +351,43 @@
@VisibleForTesting
static final class UserRecord {
- /* Type names */
- public static final String TYPENAME_SPI = "SecurityParameterIndex";
- public static final String TYPENAME_TRANSFORM = "IpSecTransform";
- public static final String TYPENAME_ENCAP_SOCKET = "UdpEncapSocket";
-
/* Maximum number of each type of resource that a single UID may possess */
+ public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
public static final int MAX_NUM_ENCAP_SOCKETS = 2;
public static final int MAX_NUM_TRANSFORMS = 4;
public static final int MAX_NUM_SPIS = 8;
+ /**
+ * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
+ * and explicit (user) reference management.
+ *
+ * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
+ *
+ * <p>Resources are removed from this array when the user releases their explicit reference
+ * by calling one of the releaseResource() methods.
+ */
final RefcountedResourceArray<SpiRecord> mSpiRecords =
- new RefcountedResourceArray<>(TYPENAME_SPI);
- final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
-
+ new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
final RefcountedResourceArray<TransformRecord> mTransformRecords =
- new RefcountedResourceArray<>(TYPENAME_TRANSFORM);
- final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
-
+ new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
- new RefcountedResourceArray<>(TYPENAME_ENCAP_SOCKET);
+ new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
+ final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords =
+ new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName());
+
+ /**
+ * Trackers for quotas for each of the OwnedResource types.
+ *
+ * <p>These trackers are separate from the resource arrays, since they are incremented and
+ * decremented at different points in time. Specifically, quota is only returned upon final
+ * resource deallocation (after all explicit and implicit references are released). Note
+ * that it is possible that calls to releaseResource() will not return the used quota if
+ * there are other resources that depend on (are parents of) the resource being released.
+ */
+ final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
+ final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
+ final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES);
void removeSpiRecord(int resourceId) {
mSpiRecords.remove(resourceId);
@@ -363,6 +397,10 @@
mTransformRecords.remove(resourceId);
}
+ void removeTunnelInterfaceRecord(int resourceId) {
+ mTunnelInterfaceRecords.remove(resourceId);
+ }
+
void removeEncapSocketRecord(int resourceId) {
mEncapSocketRecords.remove(resourceId);
}
@@ -376,22 +414,30 @@
.append(mTransformQuotaTracker)
.append(", mSocketQuotaTracker=")
.append(mSocketQuotaTracker)
+ .append(", mTunnelQuotaTracker=")
+ .append(mTunnelQuotaTracker)
.append(", mSpiRecords=")
.append(mSpiRecords)
.append(", mTransformRecords=")
.append(mTransformRecords)
.append(", mEncapSocketRecords=")
.append(mEncapSocketRecords)
+ .append(", mTunnelInterfaceRecords=")
+ .append(mTunnelInterfaceRecords)
.append("}")
.toString();
}
}
+ /**
+ * This class is not thread-safe, and expects that that users of this class will ensure
+ * synchronization and thread safety by holding the IpSecService.this instance lock.
+ */
@VisibleForTesting
static final class UserResourceTracker {
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
- /** Never-fail getter that populates the list of UIDs as-needed */
+ /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
public UserRecord getUserRecord(int uid) {
checkCallerUid(uid);
@@ -420,18 +466,20 @@
@VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
/**
- * The KernelResourceRecord class provides a facility to cleanly and reliably track system
+ * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
* resources. It relies on a provided resourceId that should uniquely identify the kernel
* resource. To use this class, the user should implement the invalidate() and
* freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
- * tracking arrays and kernel resources, respectively
+ * tracking arrays and kernel resources, respectively.
+ *
+ * <p>This class associates kernel resources with the UID that owns and controls them.
*/
- private abstract class KernelResourceRecord implements IResource {
+ private abstract class OwnedResourceRecord implements IResource {
final int pid;
final int uid;
protected final int mResourceId;
- KernelResourceRecord(int resourceId) {
+ OwnedResourceRecord(int resourceId) {
super();
if (resourceId == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
@@ -471,8 +519,6 @@
}
};
- // TODO: Move this to right after RefcountedResource. With this here, Gerrit was showing many
- // more things as changed.
/**
* Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
*
@@ -526,46 +572,56 @@
}
}
- private final class TransformRecord extends KernelResourceRecord {
+ /**
+ * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
+ * created, the SpiRecord that originally tracked the SAs will reliquish the
+ * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
+ */
+ private final class TransformRecord extends OwnedResourceRecord {
private final IpSecConfig mConfig;
- private final SpiRecord[] mSpis;
+ private final SpiRecord mSpi;
private final EncapSocketRecord mSocket;
TransformRecord(
- int resourceId, IpSecConfig config, SpiRecord[] spis, EncapSocketRecord socket) {
+ int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) {
super(resourceId);
mConfig = config;
- mSpis = spis;
+ mSpi = spi;
mSocket = socket;
+
+ spi.setOwnedByTransform();
}
public IpSecConfig getConfig() {
return mConfig;
}
- public SpiRecord getSpiRecord(int direction) {
- return mSpis[direction];
+ public SpiRecord getSpiRecord() {
+ return mSpi;
+ }
+
+ public EncapSocketRecord getSocketRecord() {
+ return mSocket;
}
/** always guarded by IpSecService#this */
@Override
public void freeUnderlyingResources() {
- for (int direction : DIRECTIONS) {
- int spi = mSpis[direction].getSpi();
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityAssociation(
- mResourceId,
- direction,
- mConfig.getLocalAddress(),
- mConfig.getRemoteAddress(),
- spi);
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
- }
+ int spi = mSpi.getSpi();
+ try {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecDeleteSecurityAssociation(
+ mResourceId,
+ mConfig.getSourceAddress(),
+ mConfig.getDestinationAddress(),
+ spi,
+ mConfig.getMarkValue(),
+ mConfig.getMarkMask());
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
}
getResourceTracker().give();
@@ -589,10 +645,8 @@
.append(super.toString())
.append(", mSocket=")
.append(mSocket)
- .append(", mSpis[OUT].mResourceId=")
- .append(mSpis[IpSecTransform.DIRECTION_OUT].mResourceId)
- .append(", mSpis[IN].mResourceId=")
- .append(mSpis[IpSecTransform.DIRECTION_IN].mResourceId)
+ .append(", mSpi.mResourceId=")
+ .append(mSpi.mResourceId)
.append(", mConfig=")
.append(mConfig)
.append("}");
@@ -600,45 +654,33 @@
}
}
- private final class SpiRecord extends KernelResourceRecord {
- private final int mDirection;
- private final String mLocalAddress;
- private final String mRemoteAddress;
+ /**
+ * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
+ * responsibility for cleaning up underlying resources will be passed to the TransformRecord
+ * object
+ */
+ private final class SpiRecord extends OwnedResourceRecord {
+ private final String mSourceAddress;
+ private final String mDestinationAddress;
private int mSpi;
private boolean mOwnedByTransform = false;
- SpiRecord(
- int resourceId,
- int direction,
- String localAddress,
- String remoteAddress,
- int spi) {
+ SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
super(resourceId);
- mDirection = direction;
- mLocalAddress = localAddress;
- mRemoteAddress = remoteAddress;
+ mSourceAddress = sourceAddress;
+ mDestinationAddress = destinationAddress;
mSpi = spi;
}
/** always guarded by IpSecService#this */
@Override
public void freeUnderlyingResources() {
- if (mOwnedByTransform) {
- Log.d(TAG, "Cannot release Spi " + mSpi + ": Currently locked by a Transform");
- // Because SPIs are "handed off" to transform, objects, they should never be
- // freed from the SpiRecord once used in a transform. (They refer to the same SA,
- // thus ownership and responsibility for freeing these resources passes to the
- // Transform object). Thus, we should let the user free them without penalty once
- // they are applied in a Transform object.
- return;
- }
-
try {
mSrvConfig
.getNetdInstance()
.ipSecDeleteSecurityAssociation(
- mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
+ mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
} catch (ServiceSpecificException e) {
// FIXME: get the error code and throw is at an IOException from Errno Exception
} catch (RemoteException e) {
@@ -654,6 +696,10 @@
return mSpi;
}
+ public String getDestinationAddress() {
+ return mDestinationAddress;
+ }
+
public void setOwnedByTransform() {
if (mOwnedByTransform) {
// Programming error
@@ -663,6 +709,10 @@
mOwnedByTransform = true;
}
+ public boolean getOwnedByTransform() {
+ return mOwnedByTransform;
+ }
+
@Override
public void invalidate() throws RemoteException {
getUserRecord().removeSpiRecord(mResourceId);
@@ -681,12 +731,10 @@
.append(super.toString())
.append(", mSpi=")
.append(mSpi)
- .append(", mDirection=")
- .append(mDirection)
- .append(", mLocalAddress=")
- .append(mLocalAddress)
- .append(", mRemoteAddress=")
- .append(mRemoteAddress)
+ .append(", mSourceAddress=")
+ .append(mSourceAddress)
+ .append(", mDestinationAddress=")
+ .append(mDestinationAddress)
.append(", mOwnedByTransform=")
.append(mOwnedByTransform)
.append("}");
@@ -694,7 +742,175 @@
}
}
- private final class EncapSocketRecord extends KernelResourceRecord {
+ // These values have been reserved in ConnectivityService
+ @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
+
+ @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400;
+
+ private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
+ private int mNextTunnelNetIdIndex = 0;
+
+ /**
+ * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
+ *
+ * <p>This method should only be called from Binder threads. Do not call this from within the
+ * system server as it will crash the system on failure.
+ *
+ * @return an integer key within the netId range, if successful
+ * @throws IllegalStateException if unsuccessful (all netId are currently reserved)
+ */
+ @VisibleForTesting
+ int reserveNetId() {
+ synchronized (mTunnelNetIds) {
+ for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
+ int index = mNextTunnelNetIdIndex;
+ int netId = index + TUN_INTF_NETID_START;
+ if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
+ if (!mTunnelNetIds.get(netId)) {
+ mTunnelNetIds.put(netId, true);
+ return netId;
+ }
+ }
+ }
+ throw new IllegalStateException("No free netIds to allocate");
+ }
+
+ @VisibleForTesting
+ void releaseNetId(int netId) {
+ synchronized (mTunnelNetIds) {
+ mTunnelNetIds.delete(netId);
+ }
+ }
+
+ private final class TunnelInterfaceRecord extends OwnedResourceRecord {
+ private final String mInterfaceName;
+ private final Network mUnderlyingNetwork;
+
+ // outer addresses
+ private final String mLocalAddress;
+ private final String mRemoteAddress;
+
+ private final int mIkey;
+ private final int mOkey;
+
+ TunnelInterfaceRecord(
+ int resourceId,
+ String interfaceName,
+ Network underlyingNetwork,
+ String localAddr,
+ String remoteAddr,
+ int ikey,
+ int okey) {
+ super(resourceId);
+
+ mInterfaceName = interfaceName;
+ mUnderlyingNetwork = underlyingNetwork;
+ mLocalAddress = localAddr;
+ mRemoteAddress = remoteAddr;
+ mIkey = ikey;
+ mOkey = okey;
+ }
+
+ /** always guarded by IpSecService#this */
+ @Override
+ public void freeUnderlyingResources() {
+ // Calls to netd
+ // Teardown VTI
+ // Delete global policies
+ try {
+ mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName);
+
+ for(String wildcardAddr : WILDCARD_ADDRESSES) {
+ for (int direction : DIRECTIONS) {
+ int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey;
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecDeleteSecurityPolicy(
+ 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
+ }
+ }
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ } catch (RemoteException e) {
+ Log.e(
+ TAG,
+ "Failed to delete VTI with interface name: "
+ + mInterfaceName
+ + " and id: "
+ + mResourceId);
+ }
+
+ getResourceTracker().give();
+ releaseNetId(mIkey);
+ releaseNetId(mOkey);
+ }
+
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ public Network getUnderlyingNetwork() {
+ return mUnderlyingNetwork;
+ }
+
+ /** Returns the local, outer address for the tunnelInterface */
+ public String getLocalAddress() {
+ return mLocalAddress;
+ }
+
+ /** Returns the remote, outer address for the tunnelInterface */
+ public String getRemoteAddress() {
+ return mRemoteAddress;
+ }
+
+ public int getIkey() {
+ return mIkey;
+ }
+
+ public int getOkey() {
+ return mOkey;
+ }
+
+ @Override
+ protected ResourceTracker getResourceTracker() {
+ return getUserRecord().mTunnelQuotaTracker;
+ }
+
+ @Override
+ public void invalidate() {
+ getUserRecord().removeTunnelInterfaceRecord(mResourceId);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{super=")
+ .append(super.toString())
+ .append(", mInterfaceName=")
+ .append(mInterfaceName)
+ .append(", mUnderlyingNetwork=")
+ .append(mUnderlyingNetwork)
+ .append(", mLocalAddress=")
+ .append(mLocalAddress)
+ .append(", mRemoteAddress=")
+ .append(mRemoteAddress)
+ .append(", mIkey=")
+ .append(mIkey)
+ .append(", mOkey=")
+ .append(mOkey)
+ .append("}")
+ .toString();
+ }
+ }
+
+ /**
+ * Tracks a UDP encap socket, and manages cleanup paths
+ *
+ * <p>While this class does not manage non-kernel resources, race conditions around socket
+ * binding require that the service creates the encap socket, binds it and applies the socket
+ * policy before handing it to a user.
+ */
+ private final class EncapSocketRecord extends OwnedResourceRecord {
private FileDescriptor mSocket;
private final int mPort;
@@ -764,14 +980,17 @@
/** @hide */
@VisibleForTesting
public IpSecService(Context context, IpSecServiceConfiguration config) {
- this(context, config, (fd, uid) -> {
- try{
- TrafficStats.setThreadStatsUid(uid);
- TrafficStats.tagFileDescriptor(fd);
- } finally {
- TrafficStats.clearThreadStatsUid();
- }
- });
+ this(
+ context,
+ config,
+ (fd, uid) -> {
+ try {
+ TrafficStats.setThreadStatsUid(uid);
+ TrafficStats.tagFileDescriptor(fd);
+ } finally {
+ TrafficStats.clearThreadStatsUid();
+ }
+ });
}
/** @hide */
@@ -837,8 +1056,8 @@
*/
private static void checkDirection(int direction) {
switch (direction) {
- case IpSecTransform.DIRECTION_OUT:
- case IpSecTransform.DIRECTION_IN:
+ case IpSecManager.DIRECTION_OUT:
+ case IpSecManager.DIRECTION_IN:
return;
}
throw new IllegalArgumentException("Invalid Direction: " + direction);
@@ -847,39 +1066,30 @@
/** Get a new SPI and maintain the reservation in the system server */
@Override
public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
- int direction, String remoteAddress, int requestedSpi, IBinder binder)
- throws RemoteException {
- checkDirection(direction);
- checkInetAddress(remoteAddress);
+ String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
+ checkInetAddress(destinationAddress);
/* requestedSpi can be anything in the int range, so no check is needed. */
checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- int resourceId = mNextResourceId.getAndIncrement();
+ final int resourceId = mNextResourceId++;
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
- String localAddress = "";
-
try {
if (!userRecord.mSpiQuotaTracker.isAvailable()) {
return new IpSecSpiResponse(
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
+
spi =
mSrvConfig
.getNetdInstance()
- .ipSecAllocateSpi(
- resourceId,
- direction,
- localAddress,
- remoteAddress,
- requestedSpi);
+ .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
userRecord.mSpiRecords.put(
resourceId,
new RefcountedResource<SpiRecord>(
- new SpiRecord(resourceId, direction, localAddress, remoteAddress, spi),
- binder));
+ new SpiRecord(resourceId, "", destinationAddress, spi), binder));
} catch (ServiceSpecificException e) {
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
return new IpSecSpiResponse(
@@ -978,7 +1188,7 @@
int callingUid = Binder.getCallingUid();
UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
- int resourceId = mNextResourceId.getAndIncrement();
+ final int resourceId = mNextResourceId++;
FileDescriptor sockFd = null;
try {
if (!userRecord.mSocketQuotaTracker.isAvailable()) {
@@ -1024,35 +1234,162 @@
}
/**
+ * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the
+ * tunnel interface and a record of its owner so that it can and must be freed when no longer
+ * needed.
+ */
+ @Override
+ public synchronized IpSecTunnelInterfaceResponse createTunnelInterface(
+ String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) {
+ checkNotNull(binder, "Null Binder passed to createTunnelInterface");
+ checkNotNull(underlyingNetwork, "No underlying network was specified");
+ checkInetAddress(localAddr);
+ checkInetAddress(remoteAddr);
+
+ // TODO: Check that underlying network exists, and IP addresses not assigned to a different
+ // network (b/72316676).
+
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ if (!userRecord.mTunnelQuotaTracker.isAvailable()) {
+ return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
+ }
+
+ final int resourceId = mNextResourceId++;
+ final int ikey = reserveNetId();
+ final int okey = reserveNetId();
+ String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId);
+
+ try {
+ // Calls to netd:
+ // Create VTI
+ // Add inbound/outbound global policies
+ // (use reqid = 0)
+ mSrvConfig
+ .getNetdInstance()
+ .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
+
+ for(String wildcardAddr : WILDCARD_ADDRESSES) {
+ for (int direction : DIRECTIONS) {
+ int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey;
+
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecAddSecurityPolicy(
+ 0, // Use 0 for reqId
+ direction,
+ wildcardAddr,
+ wildcardAddr,
+ 0,
+ mark,
+ 0xffffffff);
+ }
+ }
+
+ userRecord.mTunnelInterfaceRecords.put(
+ resourceId,
+ new RefcountedResource<TunnelInterfaceRecord>(
+ new TunnelInterfaceRecord(
+ resourceId,
+ intfName,
+ underlyingNetwork,
+ localAddr,
+ remoteAddr,
+ ikey,
+ okey),
+ binder));
+ return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+ } catch (RemoteException e) {
+ // Release keys if we got an error.
+ releaseNetId(ikey);
+ releaseNetId(okey);
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ }
+
+ // If we make it to here, then something has gone wrong and we couldn't create a VTI.
+ // Release the keys that we reserved, and return an error status.
+ releaseNetId(ikey);
+ releaseNetId(okey);
+ return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
+ }
+
+ /**
+ * Adds a new local address to the tunnel interface. This allows packets to be sent and received
+ * from multiple local IP addresses over the same tunnel.
+ */
+ @Override
+ public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) {
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+ // Get tunnelInterface record; if no such interface is found, will throw
+ // IllegalArgumentException
+ TunnelInterfaceRecord tunnelInterfaceInfo =
+ userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
+
+ // TODO: Add calls to netd:
+ // Add address to TunnelInterface
+ }
+
+ /**
+ * Remove a new local address from the tunnel interface. After removal, the address will no
+ * longer be available to send from, or receive on.
+ */
+ @Override
+ public synchronized void removeAddressFromTunnelInterface(
+ int tunnelResourceId, String localAddr) {
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+ // Get tunnelInterface record; if no such interface is found, will throw
+ // IllegalArgumentException
+ TunnelInterfaceRecord tunnelInterfaceInfo =
+ userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
+
+ // TODO: Add calls to netd:
+ // Remove address from TunnelInterface
+ }
+
+ /**
+ * Delete a TunnelInterface that has been been allocated by and registered with the system
+ * server
+ */
+ @Override
+ public synchronized void deleteTunnelInterface(int resourceId) throws RemoteException {
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ releaseResource(userRecord.mTunnelInterfaceRecords, resourceId);
+ }
+
+ @VisibleForTesting
+ void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException {
+ IpSecAlgorithm auth = config.getAuthentication();
+ IpSecAlgorithm crypt = config.getEncryption();
+ IpSecAlgorithm aead = config.getAuthenticatedEncryption();
+
+ // Validate the algorithm set
+ Preconditions.checkArgument(
+ aead != null || crypt != null || auth != null,
+ "No Encryption or Authentication algorithms specified");
+ Preconditions.checkArgument(
+ auth == null || auth.isAuthentication(),
+ "Unsupported algorithm for Authentication");
+ Preconditions.checkArgument(
+ crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
+ Preconditions.checkArgument(
+ aead == null || aead.isAead(),
+ "Unsupported algorithm for Authenticated Encryption");
+ Preconditions.checkArgument(
+ aead == null || (auth == null && crypt == null),
+ "Authenticated Encryption is mutually exclusive with other Authentication "
+ + "or Encryption algorithms");
+ }
+
+ /**
* Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
* IllegalArgumentException if they are not.
*/
private void checkIpSecConfig(IpSecConfig config) {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- if (config.getLocalAddress() == null) {
- throw new IllegalArgumentException("Invalid null Local InetAddress");
- }
-
- if (config.getRemoteAddress() == null) {
- throw new IllegalArgumentException("Invalid null Remote InetAddress");
- }
-
- switch (config.getMode()) {
- case IpSecTransform.MODE_TRANSPORT:
- if (!config.getLocalAddress().isEmpty()) {
- throw new IllegalArgumentException("Non-empty Local Address");
- }
- // Must be valid, and not a wildcard
- checkInetAddress(config.getRemoteAddress());
- break;
- case IpSecTransform.MODE_TUNNEL:
- break;
- default:
- throw new IllegalArgumentException(
- "Invalid IpSecTransform.mode: " + config.getMode());
- }
-
switch (config.getEncapType()) {
case IpSecTransform.ENCAP_NONE:
break;
@@ -1071,107 +1408,136 @@
throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType());
}
- for (int direction : DIRECTIONS) {
- IpSecAlgorithm crypt = config.getEncryption(direction);
- IpSecAlgorithm auth = config.getAuthentication(direction);
- IpSecAlgorithm authenticatedEncryption = config.getAuthenticatedEncryption(direction);
- if (authenticatedEncryption == null && crypt == null && auth == null) {
- throw new IllegalArgumentException(
- "No Encryption or Authentication algorithms specified");
- } else if (authenticatedEncryption != null && (auth != null || crypt != null)) {
- throw new IllegalArgumentException(
- "Authenticated Encryption is mutually"
- + " exclusive with other Authentication or Encryption algorithms");
- }
+ validateAlgorithms(config);
- // Retrieve SPI record; will throw IllegalArgumentException if not found
- userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId(direction));
+ // Retrieve SPI record; will throw IllegalArgumentException if not found
+ SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
+
+ // Check to ensure that SPI has not already been used.
+ if (s.getOwnedByTransform()) {
+ throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
+ }
+
+ // If no remote address is supplied, then use one from the SPI.
+ if (TextUtils.isEmpty(config.getDestinationAddress())) {
+ config.setDestinationAddress(s.getDestinationAddress());
+ }
+
+ // All remote addresses must match
+ if (!config.getDestinationAddress().equals(s.getDestinationAddress())) {
+ throw new IllegalArgumentException("Mismatched remote addresseses.");
+ }
+
+ // This check is technically redundant due to the chain of custody between the SPI and
+ // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in
+ // the transform, this will prevent us from messing up.
+ checkInetAddress(config.getDestinationAddress());
+
+ // Require a valid source address for all transforms.
+ checkInetAddress(config.getSourceAddress());
+
+ switch (config.getMode()) {
+ case IpSecTransform.MODE_TRANSPORT:
+ break;
+ case IpSecTransform.MODE_TUNNEL:
+ enforceNetworkStackPermission();
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid IpSecTransform.mode: " + config.getMode());
}
}
- /**
- * Create a transport mode transform, which represent two security associations (one in each
- * direction) in the kernel. The transform will be cached by the system server and must be freed
- * when no longer needed. It is possible to free one, deleting the SA from underneath sockets
- * that are using it, which will result in all of those sockets becoming unable to send or
- * receive data.
- */
- @Override
- public synchronized IpSecTransformResponse createTransportModeTransform(
- IpSecConfig c, IBinder binder) throws RemoteException {
- checkIpSecConfig(c);
- checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
- int resourceId = mNextResourceId.getAndIncrement();
+ private void enforceNetworkStackPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK,
+ "IpSecService");
+ }
- UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ private void createOrUpdateTransform(
+ IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
+ throws RemoteException {
- // Avoid resizing by creating a dependency array of min-size 3 (1 UDP encap + 2 SPIs)
- List<RefcountedResource> dependencies = new ArrayList<>(3);
-
- if (!userRecord.mTransformQuotaTracker.isAvailable()) {
- return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
- SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
-
- int encapType, encapLocalPort = 0, encapRemotePort = 0;
- EncapSocketRecord socketRecord = null;
- encapType = c.getEncapType();
+ int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0;
if (encapType != IpSecTransform.ENCAP_NONE) {
- RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
- userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
- c.getEncapSocketResourceId());
- dependencies.add(refcountedSocketRecord);
-
- socketRecord = refcountedSocketRecord.getResource();
encapLocalPort = socketRecord.getPort();
encapRemotePort = c.getEncapRemotePort();
}
- for (int direction : DIRECTIONS) {
- IpSecAlgorithm auth = c.getAuthentication(direction);
- IpSecAlgorithm crypt = c.getEncryption(direction);
- IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
+ IpSecAlgorithm auth = c.getAuthentication();
+ IpSecAlgorithm crypt = c.getEncryption();
+ IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
- RefcountedResource<SpiRecord> refcountedSpiRecord =
- userRecord.mSpiRecords.getRefcountedResourceOrThrow(
- c.getSpiResourceId(direction));
- dependencies.add(refcountedSpiRecord);
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecAddSecurityAssociation(
+ resourceId,
+ c.getMode(),
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+ spiRecord.getSpi(),
+ c.getMarkValue(),
+ c.getMarkMask(),
+ (auth != null) ? auth.getName() : "",
+ (auth != null) ? auth.getKey() : new byte[] {},
+ (auth != null) ? auth.getTruncationLengthBits() : 0,
+ (crypt != null) ? crypt.getName() : "",
+ (crypt != null) ? crypt.getKey() : new byte[] {},
+ (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+ (authCrypt != null) ? authCrypt.getName() : "",
+ (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+ (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+ encapType,
+ encapLocalPort,
+ encapRemotePort);
+ }
- spis[direction] = refcountedSpiRecord.getResource();
- int spi = spis[direction].getSpi();
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecAddSecurityAssociation(
- resourceId,
- c.getMode(),
- direction,
- c.getLocalAddress(),
- c.getRemoteAddress(),
- (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0,
- spi,
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- (crypt != null) ? crypt.getName() : "",
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort);
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
+ /**
+ * Create a IPsec transform, which represents a single security association in the kernel. The
+ * transform will be cached by the system server and must be freed when no longer needed. It is
+ * possible to free one, deleting the SA from underneath sockets that are using it, which will
+ * result in all of those sockets becoming unable to send or receive data.
+ */
+ @Override
+ public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
+ throws RemoteException {
+ checkIpSecConfig(c);
+ checkNotNull(binder, "Null Binder passed to createTransform");
+ final int resourceId = mNextResourceId++;
+
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ List<RefcountedResource> dependencies = new ArrayList<>();
+
+ if (!userRecord.mTransformQuotaTracker.isAvailable()) {
+ return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
- // Both SAs were created successfully, time to construct a record and lock it away
+
+ EncapSocketRecord socketRecord = null;
+ if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
+ RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
+ userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
+ c.getEncapSocketResourceId());
+ dependencies.add(refcountedSocketRecord);
+ socketRecord = refcountedSocketRecord.getResource();
+ }
+
+ RefcountedResource<SpiRecord> refcountedSpiRecord =
+ userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
+ dependencies.add(refcountedSpiRecord);
+ SpiRecord spiRecord = refcountedSpiRecord.getResource();
+
+ try {
+ createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
+ } catch (ServiceSpecificException e) {
+ // FIXME: get the error code and throw is at an IOException from Errno Exception
+ return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
+ }
+
+ // SA was created successfully, time to construct a record and lock it away
userRecord.mTransformRecords.put(
resourceId,
new RefcountedResource<TransformRecord>(
- new TransformRecord(resourceId, c, spis, socketRecord),
+ new TransformRecord(resourceId, c, spiRecord, socketRecord),
binder,
dependencies.toArray(new RefcountedResource[dependencies.size()])));
return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
@@ -1184,7 +1550,7 @@
* other reasons.
*/
@Override
- public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException {
+ public synchronized void deleteTransform(int resourceId) throws RemoteException {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
releaseResource(userRecord.mTransformRecords, resourceId);
}
@@ -1195,9 +1561,9 @@
*/
@Override
public synchronized void applyTransportModeTransform(
- ParcelFileDescriptor socket, int resourceId) throws RemoteException {
+ ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException {
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
+ checkDirection(direction);
// Get transform record; if no transform is found, will throw IllegalArgumentException
TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
@@ -1206,32 +1572,39 @@
throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
}
+ // Get config and check that to-be-applied transform has the correct mode
IpSecConfig c = info.getConfig();
+ Preconditions.checkArgument(
+ c.getMode() == IpSecTransform.MODE_TRANSPORT,
+ "Transform mode was not Transport mode; cannot be applied to a socket");
+
try {
- for (int direction : DIRECTIONS) {
- mSrvConfig
- .getNetdInstance()
- .ipSecApplyTransportModeTransform(
- socket.getFileDescriptor(),
- resourceId,
- direction,
- c.getLocalAddress(),
- c.getRemoteAddress(),
- info.getSpiRecord(direction).getSpi());
- }
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecApplyTransportModeTransform(
+ socket.getFileDescriptor(),
+ resourceId,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ info.getSpiRecord().getSpi());
} catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
+ if (e.errorCode == EINVAL) {
+ throw new IllegalArgumentException(e.toString());
+ } else {
+ throw e;
+ }
}
}
/**
- * Remove a transport mode transform from a socket, applying the default (empty) policy. This
- * will ensure that NO IPsec policy is applied to the socket (would be the equivalent of
- * applying a policy that performs no IPsec). Today the resourceId parameter is passed but not
- * used: reserved for future improved input validation.
+ * Remove transport mode transforms from a socket, applying the default (empty) policy. This
+ * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a
+ * policy that performs no IPsec). Today the resourceId parameter is passed but not used:
+ * reserved for future improved input validation.
*/
@Override
- public synchronized void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
+ public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
throws RemoteException {
try {
mSrvConfig
@@ -1242,6 +1615,79 @@
}
}
+ /**
+ * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec
+ * security association as a correspondent policy to the provided interface
+ */
+ @Override
+ public synchronized void applyTunnelModeTransform(
+ int tunnelResourceId, int direction, int transformResourceId) throws RemoteException {
+ enforceNetworkStackPermission();
+ checkDirection(direction);
+
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+ // Get transform record; if no transform is found, will throw IllegalArgumentException
+ TransformRecord transformInfo =
+ userRecord.mTransformRecords.getResourceOrThrow(transformResourceId);
+
+ // Get tunnelInterface record; if no such interface is found, will throw
+ // IllegalArgumentException
+ TunnelInterfaceRecord tunnelInterfaceInfo =
+ userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
+
+ // Get config and check that to-be-applied transform has the correct mode
+ IpSecConfig c = transformInfo.getConfig();
+ Preconditions.checkArgument(
+ c.getMode() == IpSecTransform.MODE_TUNNEL,
+ "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface");
+
+ EncapSocketRecord socketRecord = null;
+ if (c.getEncapType() != IpSecTransform.ENCAP_NONE) {
+ socketRecord =
+ userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
+ }
+ SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
+
+ int mark =
+ (direction == IpSecManager.DIRECTION_IN)
+ ? tunnelInterfaceInfo.getIkey()
+ : tunnelInterfaceInfo.getOkey();
+
+ try {
+ c.setMarkValue(mark);
+ c.setMarkMask(0xffffffff);
+
+ if (direction == IpSecManager.DIRECTION_OUT) {
+ // Set output mark via underlying network (output only)
+ c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
+
+ // If outbound, also add SPI to the policy.
+ for(String wildcardAddr : WILDCARD_ADDRESSES) {
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecUpdateSecurityPolicy(
+ 0, // Use 0 for reqId
+ direction,
+ wildcardAddr,
+ wildcardAddr,
+ transformInfo.getSpiRecord().getSpi(),
+ mark,
+ 0xffffffff);
+ }
+ }
+
+ // Update SA with tunnel mark (ikey or okey based on direction)
+ createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == EINVAL) {
+ throw new IllegalArgumentException(e.toString());
+ } else {
+ throw e;
+ }
+ }
+ }
+
@Override
protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8a15ded..88ae224 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -18,11 +18,9 @@
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.SHUTDOWN;
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
-import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
@@ -209,12 +207,6 @@
public static final int StrictCleartext = 617;
}
- /* Defaults for resolver parameters. */
- public static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
- public static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
- public static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
- public static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
-
/**
* String indicating a softap command.
*/
@@ -1768,6 +1760,8 @@
@Override
public boolean setDataSaverModeEnabled(boolean enable) {
+ mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
+
if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
synchronized (mQuotaLock) {
if (mDataSaverMode == enable) {
@@ -1947,80 +1941,19 @@
}
@Override
- public void setDnsConfigurationForNetwork(int netId, String[] servers, String domains) {
+ public void setDnsConfigurationForNetwork(int netId, String[] servers, String[] domains,
+ int[] params, boolean useTls, String tlsHostname) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final ContentResolver cr = mContext.getContentResolver();
-
- int sampleValidity = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
- DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
- if (sampleValidity < 0 || sampleValidity > 65535) {
- Slog.w(TAG, "Invalid sampleValidity=" + sampleValidity + ", using default=" +
- DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
- sampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
- }
-
- int successThreshold = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
- DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
- if (successThreshold < 0 || successThreshold > 100) {
- Slog.w(TAG, "Invalid successThreshold=" + successThreshold + ", using default=" +
- DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
- successThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
- }
-
- int minSamples = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
- int maxSamples = Settings.Global.getInt(cr,
- Settings.Global.DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
- if (minSamples < 0 || minSamples > maxSamples || maxSamples > 64) {
- Slog.w(TAG, "Invalid sample count (min, max)=(" + minSamples + ", " + maxSamples +
- "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
- DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
- minSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
- maxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
- }
-
- final String[] domainStrs = domains == null ? new String[0] : domains.split(" ");
- final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples };
- final boolean useTls = shouldUseTls(cr);
- // TODO: Populate tlsHostname once it's decided how the hostname's IP
- // addresses will be resolved:
- //
- // [1] network-provided DNS servers are included here with the
- // hostname and netd will use the network-provided servers to
- // resolve the hostname and fix up its internal structures, or
- //
- // [2] network-provided DNS servers are included here without the
- // hostname, the ConnectivityService layer resolves the given
- // hostname, and then reconfigures netd with this information.
- //
- // In practice, there will always be a need for ConnectivityService or
- // the captive portal app to use the network-provided services to make
- // some queries. This argues in favor of [1], in concert with another
- // mechanism, perhaps setting a high bit in the netid, to indicate
- // via existing DNS APIs which set of servers (network-provided or
- // non-network-provided private DNS) should be queried.
- final String tlsHostname = "";
final String[] tlsFingerprints = new String[0];
try {
- mNetdService.setResolverConfiguration(netId, servers, domainStrs, params,
- useTls, tlsHostname, tlsFingerprints);
+ mNetdService.setResolverConfiguration(
+ netId, servers, domains, params, useTls, tlsHostname, tlsFingerprints);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
- private static boolean shouldUseTls(ContentResolver cr) {
- String privateDns = Settings.Global.getString(cr, Settings.Global.PRIVATE_DNS_MODE);
- if (TextUtils.isEmpty(privateDns)) {
- privateDns = PRIVATE_DNS_DEFAULT_MODE;
- }
- return privateDns.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
- privateDns.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
- }
-
@Override
public void addVpnUidRanges(int netId, UidRange[] ranges) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -2563,12 +2496,16 @@
@Override
public void removeNetwork(int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
try {
- mConnector.execute("network", "destroy", netId);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkDestroy(netId);
+ } catch (ServiceSpecificException e) {
+ Log.w(TAG, "removeNetwork(" + netId + "): ", e);
+ throw e;
+ } catch (RemoteException e) {
+ Log.w(TAG, "removeNetwork(" + netId + "): ", e);
+ throw e.rethrowAsRuntimeException();
}
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 28481e2..81c905b 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,15 +1,12 @@
per-file ConnectivityService.java=ek@google.com
-per-file ConnectivityService.java=hugobenichi@google.com
per-file ConnectivityService.java=jchalard@google.com
per-file ConnectivityService.java=lorenzo@google.com
per-file ConnectivityService.java=satk@google.com
per-file NetworkManagementService.java=ek@google.com
-per-file NetworkManagementService.java=hugobenichi@google.com
per-file NetworkManagementService.java=jchalard@google.com
per-file NetworkManagementService.java=lorenzo@google.com
per-file NetworkManagementService.java=satk@google.com
per-file NsdService.java=ek@google.com
-per-file NsdService.java=hugobenichi@google.com
per-file NsdService.java=jchalard@google.com
per-file NsdService.java=lorenzo@google.com
per-file NsdService.java=satk@google.com
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 831c9cb..8b5176e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -35,6 +35,7 @@
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.DisconnectCause;
+import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneStateListener;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
@@ -55,6 +56,7 @@
import com.android.internal.telephony.PhoneConstantConversions;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.am.BatteryStatsService;
@@ -64,6 +66,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.NoSuchElementException;
/**
* Since phone process can be restarted, this class provides a centralized place
@@ -90,10 +93,13 @@
IBinder binder;
+ TelephonyRegistryDeathRecipient deathRecipient;
+
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
- int callerUserId;
+ int callerUid;
+ int callerPid;
int events;
@@ -117,7 +123,7 @@
+ " callback=" + callback
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
- + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
+ + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
+ " events=" + Integer.toHexString(events)
+ " canReadPhoneState=" + canReadPhoneState + "}";
}
@@ -147,6 +153,8 @@
private int[] mDataActivationState;
+ private boolean[] mUserMobileDataState;
+
private SignalStrength[] mSignalStrength;
private boolean[] mMessageWaiting;
@@ -249,6 +257,21 @@
}
};
+ private class TelephonyRegistryDeathRecipient implements IBinder.DeathRecipient {
+
+ private final IBinder binder;
+
+ TelephonyRegistryDeathRecipient(IBinder binder) {
+ this.binder = binder;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DBG) log("binderDied " + binder);
+ remove(binder);
+ }
+ }
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -304,6 +327,7 @@
mServiceState = new ServiceState[numPhones];
mVoiceActivationState = new int[numPhones];
mDataActivationState = new int[numPhones];
+ mUserMobileDataState = new boolean[numPhones];
mSignalStrength = new SignalStrength[numPhones];
mMessageWaiting = new boolean[numPhones];
mCallForwarding = new boolean[numPhones];
@@ -320,6 +344,7 @@
mCallIncomingNumber[i] = "";
mServiceState[i] = new ServiceState();
mSignalStrength[i] = new SignalStrength();
+ mUserMobileDataState[i] = false;
mMessageWaiting[i] = false;
mCallForwarding[i] = false;
mCellLocation[i] = new Bundle();
@@ -352,50 +377,33 @@
public void addOnSubscriptionsChangedListener(String callingPackage,
IOnSubscriptionsChangedListener callback) {
int callerUserId = UserHandle.getCallingUserId();
+ mContext.getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (VDBG) {
log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
+ " callerUserId=" + callerUserId + " callback=" + callback
+ " callback.asBinder=" + callback.asBinder());
}
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
- "addOnSubscriptionsChangedListener");
- // SKIP checking for run-time permission since caller or self has PRIVILEGED permission
- } catch (SecurityException e) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PHONE_STATE,
- "addOnSubscriptionsChangedListener");
-
- if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, callingPackage, "addOnSubscriptionsChangedListener")) {
+ return;
}
- Record r;
synchronized (mRecords) {
// register
- find_and_add: {
- IBinder b = callback.asBinder();
- final int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- r = mRecords.get(i);
- if (b == r.binder) {
- break find_and_add;
- }
- }
- r = new Record();
- r.binder = b;
- mRecords.add(r);
- if (DBG) log("listen oscl: add new record");
+ IBinder b = callback.asBinder();
+ Record r = add(b);
+
+ if (r == null) {
+ return;
}
r.onSubscriptionsChangedListenerCallback = callback;
r.callingPackage = callingPackage;
- r.callerUserId = callerUserId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
r.events = 0;
r.canReadPhoneState = true; // permission has been enforced above
if (DBG) {
@@ -466,6 +474,8 @@
private void listen(String callingPackage, IPhoneStateListener callback, int events,
boolean notifyNow, int subId) {
int callerUserId = UserHandle.getCallingUserId();
+ mContext.getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (VDBG) {
log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
+ " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
@@ -473,47 +483,31 @@
}
if (events != PhoneStateListener.LISTEN_NONE) {
- /* Checks permission and throws Security exception */
- checkListenerPermission(events);
-
- if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
- // SKIP checking for run-time permission since caller or self has PRIVILEGED
- // permission
- } catch (SecurityException e) {
- if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
- }
+ // Checks permission and throws SecurityException for disallowed operations. For pre-M
+ // apps whose runtime permission has been revoked, we return immediately to skip sending
+ // events to the app without crashing it.
+ if (!checkListenerPermission(events, callingPackage, "listen")) {
+ return;
}
+ int phoneId = SubscriptionManager.getPhoneId(subId);
synchronized (mRecords) {
// register
- Record r;
- find_and_add: {
- IBinder b = callback.asBinder();
- final int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- r = mRecords.get(i);
- if (b == r.binder) {
- break find_and_add;
- }
- }
- r = new Record();
- r.binder = b;
- mRecords.add(r);
- if (DBG) log("listen: add new record");
+ IBinder b = callback.asBinder();
+ Record r = add(b);
+
+ if (r == null) {
+ return;
}
r.callback = callback;
r.callingPackage = callingPackage;
- r.callerUserId = callerUserId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
| ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
- r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
+ r.canReadPhoneState =
+ isPhoneStateEvent && canReadPhoneState(callingPackage, "listen");
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
// force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -521,9 +515,7 @@
} else {//APP specify subID
r.subId = subId;
}
- r.phoneId = SubscriptionManager.getPhoneId(r.subId);
-
- int phoneId = r.phoneId;
+ r.phoneId = phoneId;
r.events = events;
if (DBG) {
log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
@@ -568,8 +560,10 @@
try {
if (DBG_LOC) log("listen: mCellLocation = "
+ mCellLocation[phoneId]);
- r.callback.onCellLocationChanged(
- new Bundle(mCellLocation[phoneId]));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellLocationChanged(
+ new Bundle(mCellLocation[phoneId]));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -615,7 +609,9 @@
try {
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -656,6 +652,13 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+ try {
+ r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -664,21 +667,13 @@
}
}
- private boolean canReadPhoneState(String callingPackage) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) ==
- PackageManager.PERMISSION_GRANTED) {
- // SKIP checking for run-time permission since caller or self has PRIVILEGED permission
- return true;
- }
- boolean canReadPhoneState = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED;
- if (canReadPhoneState &&
- mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ private boolean canReadPhoneState(String callingPackage, String message) {
+ try {
+ return TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, callingPackage, message);
+ } catch (SecurityException e) {
return false;
}
- return canReadPhoneState;
}
private String getCallIncomingNumber(Record record, int phoneId) {
@@ -686,16 +681,57 @@
return record.canReadPhoneState ? mCallIncomingNumber[phoneId] : "";
}
+ private Record add(IBinder binder) {
+ Record r;
+
+ synchronized (mRecords) {
+ final int N = mRecords.size();
+ for (int i = 0; i < N; i++) {
+ r = mRecords.get(i);
+ if (binder == r.binder) {
+ // Already existed.
+ return r;
+ }
+ }
+ r = new Record();
+ r.binder = binder;
+ r.deathRecipient = new TelephonyRegistryDeathRecipient(binder);
+
+ try {
+ binder.linkToDeath(r.deathRecipient, 0);
+ } catch (RemoteException e) {
+ if (VDBG) log("LinkToDeath remote exception sending to r=" + r + " e=" + e);
+ // Binder already died. Return null.
+ return null;
+ }
+
+ mRecords.add(r);
+ if (DBG) log("add new record");
+ }
+
+ return r;
+ }
+
private void remove(IBinder binder) {
synchronized (mRecords) {
final int recordCount = mRecords.size();
for (int i = 0; i < recordCount; i++) {
- if (mRecords.get(i).binder == binder) {
+ Record r = mRecords.get(i);
+ if (r.binder == binder) {
if (DBG) {
- Record r = mRecords.get(i);
- log("remove: binder=" + binder + "r.callingPackage" + r.callingPackage
- + "r.callback" + r.callback);
+ log("remove: binder=" + binder + " r.callingPackage " + r.callingPackage
+ + " r.callback " + r.callback);
}
+
+ if (r.deathRecipient != null) {
+ try {
+ binder.unlinkToDeath(r.deathRecipient, 0);
+ } catch (NoSuchElementException e) {
+ if (VDBG) log("UnlinkToDeath NoSuchElementException sending to r="
+ + r + " e=" + e);
+ }
+ }
+
mRecords.remove(i);
return;
}
@@ -961,14 +997,14 @@
log("notifyCellInfoForSubscriber: subId=" + subId
+ " cellInfo=" + cellInfo);
}
-
+ int phoneId = SubscriptionManager.getPhoneId(subId);
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
mCellInfo.set(phoneId, cellInfo);
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
- idMatch(r.subId, subId, phoneId)) {
+ idMatch(r.subId, subId, phoneId) &&
+ checkLocationAccess(r)) {
try {
if (DBG_LOC) {
log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1012,6 +1048,33 @@
}
}
+ public void notifyUserMobileDataStateChangedForPhoneId(int phoneId, int subId, boolean state) {
+ if (!checkNotifyPermission("notifyUserMobileDataStateChanged()")) {
+ return;
+ }
+ if (VDBG) {
+ log("notifyUserMobileDataStateChangedForSubscriberPhoneID: subId=" + phoneId
+ + " state=" + state);
+ }
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mMessageWaiting[phoneId] = state;
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) &&
+ idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onUserMobileDataStateChanged(state);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
public void notifyCallForwardingChanged(boolean cfi) {
notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cfi);
}
@@ -1024,8 +1087,8 @@
log("notifyCallForwardingChangedForSubscriber: subId=" + subId
+ " cfi=" + cfi);
}
+ int phoneId = SubscriptionManager.getPhoneId(subId);
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
mCallForwarding[phoneId] = cfi;
for (Record r : mRecords) {
@@ -1052,8 +1115,8 @@
if (!checkNotifyPermission("notifyDataActivity()" )) {
return;
}
+ int phoneId = SubscriptionManager.getPhoneId(subId);
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
mDataActivity[phoneId] = state;
for (Record r : mRecords) {
@@ -1094,8 +1157,8 @@
+ "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
+ " mRecords.size()=" + mRecords.size());
}
+ int phoneId = SubscriptionManager.getPhoneId(subId);
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
boolean modified = false;
if (state == TelephonyManager.DATA_CONNECTED) {
@@ -1218,13 +1281,14 @@
log("notifyCellLocationForSubscriber: subId=" + subId
+ " cellLocation=" + cellLocation);
}
+ int phoneId = SubscriptionManager.getPhoneId(subId);
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
mCellLocation[phoneId] = cellLocation;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
- idMatch(r.subId, subId, phoneId)) {
+ idMatch(r.subId, subId, phoneId) &&
+ checkLocationAccess(r)) {
try {
if (DBG_LOC) {
log("notifyCellLocation: cellLocation=" + cellLocation
@@ -1374,6 +1438,7 @@
pw.println("mServiceState=" + mServiceState[i]);
pw.println("mVoiceActivationState= " + mVoiceActivationState[i]);
pw.println("mDataActivationState= " + mDataActivationState[i]);
+ pw.println("mUserMobileDataState= " + mUserMobileDataState[i]);
pw.println("mSignalStrength=" + mSignalStrength[i]);
pw.println("mMessageWaiting=" + mMessageWaiting[i]);
pw.println("mCallForwarding=" + mCallForwarding[i]);
@@ -1580,11 +1645,12 @@
}
private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
- if (checkNotifyPermission()) {
+ if (checkNotifyPermission()) {
return;
}
- enforceCarrierPrivilege();
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ SubscriptionManager.getDefaultSubscriptionId(), method);
}
private boolean checkNotifyPermission(String method) {
@@ -1602,23 +1668,7 @@
== PackageManager.PERMISSION_GRANTED;
}
- private void enforceCarrierPrivilege() {
- TelephonyManager tm = TelephonyManager.getDefault();
- String[] pkgs = mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
- for (String pkg : pkgs) {
- if (tm.checkCarrierPrivilegesForPackage(pkg) ==
- TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- return;
- }
- }
-
- String msg = "Carrier Privilege Permission Denial: from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid();
- if (DBG) log(msg);
- throw new SecurityException(msg);
- }
-
- private void checkListenerPermission(int events) {
+ private boolean checkListenerPermission(int events, String callingPackage, String message) {
if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
@@ -1632,22 +1682,18 @@
}
if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
- // SKIP checking for run-time permission since caller or self has PRIVILEGED
- // permission
- } catch (SecurityException e) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PHONE_STATE, null);
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, callingPackage, message)) {
+ return false;
}
}
if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
-
}
+
+ return true;
}
private void handleRemoveListLocked() {
@@ -1667,10 +1713,11 @@
boolean valid = false;
try {
foregroundUser = ActivityManager.getCurrentUser();
- valid = r.callerUserId == foregroundUser && r.matchPhoneStateListenerEvent(events);
+ valid = UserHandle.getUserId(r.callerUid) == foregroundUser
+ && r.matchPhoneStateListenerEvent(events);
if (DBG | DBG_LOC) {
log("validateEventsAndUserLocked: valid=" + valid
- + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
+ + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
+ " r.events=" + r.events + " events=" + events);
}
} finally {
@@ -1702,6 +1749,16 @@
}
}
+ private boolean checkLocationAccess(Record r) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return LocationAccessPolicy.canAccessCellLocation(mContext,
+ r.callingPackage, r.callerUid, r.callerPid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void checkPossibleMissNotify(Record r, int phoneId) {
int events = r.events;
@@ -1749,7 +1806,21 @@
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
}
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ }
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+
+ if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+ try {
+ if (VDBG) {
+ log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId="
+ + phoneId + " umds=" + mUserMobileDataState[phoneId]);
+ }
+ r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1785,7 +1856,9 @@
try {
if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
+ mCellLocation[phoneId]);
- r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+ if (checkLocationAccess(r)) {
+ r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+ }
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 8d46d1e..53285e6 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -87,8 +87,10 @@
"/system/bin/sdcard",
"/system/bin/surfaceflinger",
"media.extractor", // system/bin/mediaextractor
+ "media.metrics", // system/bin/mediametrics
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
"com.android.bluetooth", // Bluetooth service
+ "statsd", // Stats daemon
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
@@ -564,14 +566,7 @@
Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
} else {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
- for (int i=0; i<blockedCheckers.size(); i++) {
- Slog.w(TAG, blockedCheckers.get(i).getName() + " stack trace:");
- StackTraceElement[] stackTrace
- = blockedCheckers.get(i).getThread().getStackTrace();
- for (StackTraceElement element: stackTrace) {
- Slog.w(TAG, " at " + element);
- }
- }
+ WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
Process.killProcess(Process.myPid());
System.exit(10);
diff --git a/services/core/java/com/android/server/WatchdogDiagnostics.java b/services/core/java/com/android/server/WatchdogDiagnostics.java
new file mode 100644
index 0000000..01db2d3
--- /dev/null
+++ b/services/core/java/com/android/server/WatchdogDiagnostics.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+import android.util.Log;
+import android.util.LogWriter;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.Watchdog.HandlerChecker;
+
+import dalvik.system.AnnotatedStackTraceElement;
+import dalvik.system.VMStack;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Class to give diagnostic messages for Watchdogs.
+ */
+class WatchdogDiagnostics {
+ private static String getBlockedOnString(Object blockedOn) {
+ return String.format("- waiting to lock <0x%08x> (a %s)",
+ System.identityHashCode(blockedOn), blockedOn.getClass().getName());
+ }
+
+ private static String getLockedString(Object heldLock) {
+ return String.format("- locked <0x%08x> (a %s)", System.identityHashCode(heldLock),
+ heldLock.getClass().getName());
+ }
+
+ /**
+ * Print the annotated stack for the given thread. If the annotated stack cannot be retrieved,
+ * returns false.
+ */
+ @VisibleForTesting
+ public static boolean printAnnotatedStack(Thread thread, PrintWriter out) {
+ AnnotatedStackTraceElement stack[] = VMStack.getAnnotatedThreadStackTrace(thread);
+ if (stack == null) {
+ return false;
+ }
+ out.println(thread.getName() + " annotated stack trace:");
+ for (AnnotatedStackTraceElement element : stack) {
+ out.println(" at " + element.getStackTraceElement());
+ if (element.getBlockedOn() != null) {
+ out.println(" " + getBlockedOnString(element.getBlockedOn()));
+ }
+ if (element.getHeldLocks() != null) {
+ for (Object held : element.getHeldLocks()) {
+ out.println(" " + getLockedString(held));
+ }
+ }
+ }
+ return true;
+ }
+
+ public static void diagnoseCheckers(final List<HandlerChecker> blockedCheckers) {
+ PrintWriter out = new PrintWriter(new LogWriter(Log.WARN, Watchdog.TAG, Log.LOG_ID_SYSTEM),
+ true);
+ for (int i=0; i<blockedCheckers.size(); i++) {
+ Thread blockedThread = blockedCheckers.get(i).getThread();
+ if (printAnnotatedStack(blockedThread, out)) {
+ continue;
+ }
+
+ // Fall back to "regular" stack trace, if necessary.
+ Slog.w(Watchdog.TAG, blockedThread.getName() + " stack trace:");
+ StackTraceElement[] stackTrace = blockedThread.getStackTrace();
+ for (StackTraceElement element : stackTrace) {
+ Slog.w(Watchdog.TAG, " at " + element);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2fe8f2a..6a6b5ce 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2007,11 +2007,11 @@
getAccountRemovedReceivers(accountToRename, accounts);
accounts.accountsDb.beginTransaction();
Account renamedAccount = new Account(newName, accountToRename.type);
- if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
- Log.e(TAG, "renameAccount failed - account with new name already exists");
- return null;
- }
try {
+ if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
+ Log.e(TAG, "renameAccount failed - account with new name already exists");
+ return null;
+ }
final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
if (accountId >= 0) {
accounts.accountsDb.renameCeAccount(accountId, newName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dce0c38..6c60b74 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -63,6 +63,7 @@
import static android.os.Process.SCHED_FIFO;
import static android.os.Process.SCHED_OTHER;
import static android.os.Process.SCHED_RESET_ON_FORK;
+import static android.os.Process.SE_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SIGNAL_QUIT;
import static android.os.Process.SIGNAL_USR1;
@@ -200,6 +201,7 @@
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManager.TaskThumbnailInfo;
import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -531,6 +533,10 @@
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
+ // Disable hidden API checks for the newly started instrumentation.
+ // Must be kept in sync with Am.
+ private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
// How long to wait in getAssistContextExtras for the activity and foreground services
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
@@ -1492,6 +1498,14 @@
String mProfileApp = null;
ProcessRecord mProfileProc = null;
ProfilerInfo mProfilerInfo = null;
+
+ /**
+ * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+ * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+ * A non-null agent in mProfileInfo overrides this.
+ */
+ private @Nullable Map<String, String> mAppAgentMap = null;
+
int mProfileType = 0;
final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
@@ -1548,6 +1562,8 @@
}
}
+ final List<ScreenObserver> mScreenObservers = new ArrayList<>();
+
final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@@ -1689,6 +1705,8 @@
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+ static final int DISPATCH_SCREEN_AWAKE_MSG = 71;
+ static final int DISPATCH_SCREEN_KEYGUARD_MSG = 72;
static final int START_USER_SWITCH_FG_MSG = 712;
static final int NOTIFY_VR_KEYGUARD_MSG = 74;
@@ -1724,6 +1742,9 @@
final ActivityManagerConstants mConstants;
+ // Encapsulates the global setting "hidden_api_blacklist_exemptions"
+ final HiddenApiBlacklist mHiddenApiBlacklist;
+
PackageManagerInternal mPackageManagerInt;
// VoiceInteraction session ID that changes for each new request except when
@@ -2412,11 +2433,17 @@
}
}
} break;
- case NOTIFY_VR_SLEEPING_MSG: {
- notifyVrManagerOfSleepState(msg.arg1 != 0);
+ case DISPATCH_SCREEN_AWAKE_MSG: {
+ final boolean isAwake = msg.arg1 != 0;
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+ }
} break;
- case NOTIFY_VR_KEYGUARD_MSG: {
- notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+ case DISPATCH_SCREEN_KEYGUARD_MSG: {
+ final boolean isShowing = msg.arg1 != 0;
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
+ }
} break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
@@ -2667,6 +2694,42 @@
}
}
+ /**
+ * Encapsulates the globla setting "hidden_api_blacklist_exemptions", including tracking the
+ * latest value via a content observer.
+ */
+ static class HiddenApiBlacklist extends ContentObserver {
+
+ private final Context mContext;
+ private boolean mBlacklistDisabled;
+
+ public HiddenApiBlacklist(Handler handler, Context context) {
+ super(handler);
+ mContext = context;
+ }
+
+ public void registerObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
+ false,
+ this);
+ update();
+ }
+
+ private void update() {
+ mBlacklistDisabled = "*".equals(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS));
+ }
+
+ boolean isDisabled() {
+ return mBlacklistDisabled;
+ }
+
+ public void onChange(boolean selfChange) {
+ update();
+ }
+ }
+
@VisibleForTesting
public ActivityManagerService(Injector injector) {
mInjector = injector;
@@ -2696,6 +2759,7 @@
mUiHandler = injector.getUiHandler(null);
mUserController = null;
mVrController = null;
+ mHiddenApiBlacklist = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2828,6 +2892,8 @@
}
};
+ mHiddenApiBlacklist = new HiddenApiBlacklist(mHandler, mContext);
+
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
}
@@ -3260,32 +3326,6 @@
mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
}
- private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
- mHandler.sendMessage(
- mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
- }
-
- private void notifyVrManagerOfSleepState(boolean isSleeping) {
- final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- if (vrService == null) {
- return;
- }
- vrService.onSleepStateChanged(isSleeping);
- }
-
- private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
- mHandler.sendMessage(
- mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
- }
-
- private void notifyVrManagerOfKeyguardState(boolean isShowing) {
- final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- if (vrService == null) {
- return;
- }
- vrService.onKeyguardStateChanged(isShowing);
- }
-
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -3634,6 +3674,7 @@
info.className = entryPoint;
info.packageName = "android";
info.seInfoUser = SELinuxUtil.COMPLETE_STR;
+ info.targetSdkVersion = Build.VERSION.SDK_INT;
ProcessRecord proc = startProcessLocked(processName, info /* info */,
false /* knownToBeDead */, 0 /* intentFlags */, "" /* hostingType */,
null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
@@ -3776,6 +3817,13 @@
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
+ startProcessLocked(app, hostingType, hostingNameStr, false /* disableHiddenApiChecks */,
+ null /* abiOverride */, null /* entryPoint */, null /* entryPointArgs */);
+ }
+
+ private final void startProcessLocked(ProcessRecord app, String hostingType,
+ String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride,
+ String entryPoint, String[] entryPointArgs) {
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != MY_PID) {
checkTime(startTime, "startProcess: removing from pids map");
@@ -3869,9 +3917,13 @@
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("true".equals(genDebugInfoProperty)) {
+ if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
+ String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+ if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
@@ -3892,6 +3944,14 @@
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
+ if (!app.info.isAllowedToUseHiddenApi() &&
+ !disableHiddenApiChecks &&
+ !mHiddenApiBlacklist.isDisabled()) {
+ // This app is not allowed to use undocumented and private APIs, or blacklisting is
+ // enabled. Set up its runtime with the appropriate flag.
+ runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
+ }
+
String invokeWith = null;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
@@ -7026,18 +7086,6 @@
}
}
- ProfilerInfo profilerInfo = null;
- String agent = null;
- if (mProfileApp != null && mProfileApp.equals(processName)) {
- mProfileProc = app;
- profilerInfo = (mProfilerInfo != null && mProfilerInfo.profileFile != null) ?
- new ProfilerInfo(mProfilerInfo) : null;
- agent = mProfilerInfo != null ? mProfilerInfo.agent : null;
- } else if (app.instr != null && app.instr.mProfileFile != null) {
- profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
- null);
- }
-
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
@@ -7062,6 +7110,39 @@
ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
+ ProfilerInfo profilerInfo = null;
+ String preBindAgent = null;
+ if (mProfileApp != null && mProfileApp.equals(processName)) {
+ mProfileProc = app;
+ if (mProfilerInfo != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfilerInfo.profileFile != null
+ || mProfilerInfo.attachAgentDuringBind;
+ profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+ if (mProfilerInfo.agent != null) {
+ preBindAgent = mProfilerInfo.agent;
+ }
+ }
+ } else if (app.instr != null && app.instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+ null, false);
+ }
+ if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+ // We need to do a debuggable check here. See setAgentApp for why the check is
+ // postponed to here.
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ String agent = mAppAgentMap.get(processName);
+ // Do not overwrite already requested agent.
+ if (profilerInfo == null) {
+ profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+ mAppAgentMap.get(processName), true);
+ } else if (profilerInfo.agent == null) {
+ profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+ }
+ }
+ }
+
if (profilerInfo != null && profilerInfo.profileFd != null) {
profilerInfo.profileFd = profilerInfo.profileFd.dup();
}
@@ -7102,8 +7183,8 @@
// If we were asked to attach an agent on startup, do so now, before we're binding
// application code.
- if (agent != null) {
- thread.attachAgent(agent);
+ if (preBindAgent != null) {
+ thread.attachAgent(preBindAgent);
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
@@ -12388,6 +12469,12 @@
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
+ return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
+ abiOverride);
+ }
+
+ final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
+ boolean disableHiddenApiChecks, String abiOverride) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -12419,8 +12506,8 @@
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application",
- customProcess != null ? customProcess : app.processName, abiOverride,
- null /* entryPoint */, null /* entryPointArgs */);
+ customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
+ abiOverride, null /* entryPoint */, null /* entryPointArgs */);
}
return app;
@@ -12497,7 +12584,8 @@
if (wasAwake != isAwake) {
// Also update state in a special way for running foreground services UI.
mServices.updateScreenStateLocked(isAwake);
- sendNotifyVrManagerOfSleepState(!isAwake);
+ mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
+ .sendToTarget();
}
}
}
@@ -12650,7 +12738,9 @@
Binder.restoreCallingIdentity(ident);
}
}
- sendNotifyVrManagerOfKeyguardState(showing);
+
+ mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+ .sendToTarget();
}
@Override
@@ -12792,6 +12882,52 @@
}
}
+ /**
+ * Set or remove an agent to be run whenever an app with the given process name starts.
+ *
+ * This method will not check whether the given process name matches a debuggable app. That
+ * would require scanning all current packages, and a rescan when new packages are installed
+ * or updated.
+ *
+ * Instead, do the check when an application is started and matched to a stored agent.
+ *
+ * @param packageName the process name of the app.
+ * @param agent the agent string to be used, or null to remove any previously set agent.
+ */
+ @Override
+ public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(
+ android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
+ PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (agent == null) {
+ if (mAppAgentMap != null) {
+ mAppAgentMap.remove(packageName);
+ if (mAppAgentMap.isEmpty()) {
+ mAppAgentMap = null;
+ }
+ }
+ } else {
+ if (mAppAgentMap == null) {
+ mAppAgentMap = new HashMap<>();
+ }
+ if (mAppAgentMap.size() >= 100) {
+ // Limit the size of the map, to avoid OOMEs.
+ Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+ + "/" + agent);
+ return;
+ }
+ mAppAgentMap.put(packageName, agent);
+ }
+ }
+ }
+
void setTrackAllocationApp(ApplicationInfo app, String processName) {
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
@@ -14090,6 +14226,7 @@
NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
final boolean supportsLeanbackOnly =
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");
@@ -17406,6 +17543,7 @@
final long myTotalPss = mi.getTotalPss();
final long myTotalSwapPss = mi.getTotalSwappedOutPss();
totalPss += myTotalPss;
+ totalSwapPss += myTotalSwapPss;
nativeProcTotalPss += myTotalPss;
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
@@ -19189,6 +19327,7 @@
case PHONE_UID:
case BLUETOOTH_UID:
case NFC_UID:
+ case SE_UID:
isCallerSystem = true;
break;
default:
@@ -20033,7 +20172,10 @@
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
"start instr");
- ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride);
+ boolean disableHiddenApiChecks =
+ (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+ ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
+ abiOverride);
app.instr = activeInstr;
activeInstr.mFinished = false;
activeInstr.mRunningProcesses.add(app);
@@ -24258,6 +24400,11 @@
}
}
}
+
+ @Override
+ public void registerScreenObserver(ScreenObserver observer) {
+ mScreenObservers.add(observer);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8488e52..254f403 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -114,6 +114,7 @@
private boolean mAutoStop;
private boolean mStreaming; // Streaming the profiling output to a file.
private String mAgent; // Agent to attach on startup.
+ private boolean mAttachAgentDuringBind; // Whether agent should be attached late.
private int mDisplayId;
private int mStackId;
private int mTaskId;
@@ -163,6 +164,8 @@
return runDumpHeap(pw);
case "set-debug-app":
return runSetDebugApp(pw);
+ case "set-agent-app":
+ return runSetAgentApp(pw);
case "clear-debug-app":
return runClearDebugApp(pw);
case "set-watch-heap":
@@ -295,7 +298,21 @@
} else if (opt.equals("--streaming")) {
mStreaming = true;
} else if (opt.equals("--attach-agent")) {
+ if (mAgent != null) {
+ cmd.getErrPrintWriter().println(
+ "Multiple --attach-agent(-bind) not supported");
+ return false;
+ }
mAgent = getNextArgRequired();
+ mAttachAgentDuringBind = false;
+ } else if (opt.equals("--attach-agent-bind")) {
+ if (mAgent != null) {
+ cmd.getErrPrintWriter().println(
+ "Multiple --attach-agent(-bind) not supported");
+ return false;
+ }
+ mAgent = getNextArgRequired();
+ mAttachAgentDuringBind = true;
} else if (opt.equals("-R")) {
mRepeat = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("-S")) {
@@ -381,7 +398,7 @@
}
}
profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
- mStreaming, mAgent);
+ mStreaming, mAgent, mAttachAgentDuringBind);
}
pw.println("Starting: " + intent);
@@ -755,7 +772,7 @@
return -1;
}
profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
- null);
+ null, false);
}
try {
@@ -847,6 +864,13 @@
return 0;
}
+ int runSetAgentApp(PrintWriter pw) throws RemoteException {
+ String pkg = getNextArgRequired();
+ String agent = getNextArg();
+ mInterface.setAgentApp(pkg, agent);
+ return 0;
+ }
+
int runClearDebugApp(PrintWriter pw) throws RemoteException {
mInterface.setDebugApp(null, false, true);
return 0;
@@ -2679,6 +2703,7 @@
pw.println(" (use with --start-profiler)");
pw.println(" -P <FILE>: like above, but profiling stops when app goes idle");
pw.println(" --attach-agent <agent>: attach the given agent before binding");
+ pw.println(" --attach-agent-bind <agent>: attach the given agent during binding");
pw.println(" -R: repeat the activity launch <COUNT> times. Prior to each repeat,");
pw.println(" the top activity will be finished.");
pw.println(" -S: force stop the target app before starting the activity");
@@ -2704,7 +2729,7 @@
pw.println(" specified then send to all users.");
pw.println(" --receiver-permission <PERMISSION>: Require receiver to hold permission.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
- pw.println(" [--user <USER_ID> | current]");
+ pw.println(" [--user <USER_ID> | current] [--no-hidden-api-checks]");
pw.println(" [--no-window-animation] [--abi <ABI>] <COMPONENT>");
pw.println(" Start an Instrumentation. Typically this target <COMPONENT> is in the");
pw.println(" form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there");
@@ -2719,6 +2744,7 @@
pw.println(" test runners.");
pw.println(" --user <USER_ID> | current: Specify user instrumentation runs in;");
pw.println(" current user if not specified.");
+ pw.println(" --no-hidden-api-checks: disable restrictions on use of hidden API.");
pw.println(" --no-window-animation: turn off window animations while running.");
pw.println(" --abi <ABI>: Launch the instrumented process with the selected ABI.");
pw.println(" This assumes that the process supports the selected ABI.");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5eb2a8d..503dfe9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -136,7 +136,6 @@
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -768,7 +767,7 @@
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
- intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2929,14 +2928,28 @@
}
public void setBluetoothScoOnInt(boolean on, String eventSource) {
+ if (DEBUG_DEVICES) {
+ Log.d(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
+ }
if (on) {
// do not accept SCO ON if SCO audio is not connected
- synchronized(mScoClients) {
- if ((mBluetoothHeadset != null) &&
- (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
- != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
- mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
- return;
+ synchronized (mScoClients) {
+ if (mBluetoothHeadset != null) {
+ if (mBluetoothHeadsetDevice == null) {
+ BluetoothDevice activeDevice = mBluetoothHeadset.getActiveDevice();
+ if (activeDevice != null) {
+ // setBtScoActiveDevice() might trigger resetBluetoothSco() which
+ // will call setBluetoothScoOnInt(false, "resetBluetoothSco")
+ setBtScoActiveDevice(activeDevice);
+ }
+ }
+ if (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ != BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
+ Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
+ + mBluetoothHeadsetDevice + " is not in audio connected mode");
+ return;
+ }
}
}
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
@@ -3324,24 +3337,23 @@
}
}
- void setBtScoDeviceConnectionState(BluetoothDevice btDevice, int state) {
+ private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
if (btDevice == null) {
- return;
+ return true;
}
-
String address = btDevice.getAddress();
BluetoothClass btClass = btDevice.getBluetoothClass();
int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
if (btClass != null) {
switch (btClass.getDeviceClass()) {
- case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
- case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
- break;
- case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
- break;
+ case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+ case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ break;
+ case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+ outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ break;
}
}
@@ -3349,34 +3361,33 @@
address = "";
}
- boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
-
String btDeviceName = btDevice.getName();
- boolean success =
- handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
- handleDeviceConnection(connected, inDevice, address, btDeviceName);
+ boolean result = handleDeviceConnection(isActive, outDevice, address, btDeviceName);
+ // handleDeviceConnection() && result to make sure the method get executed
+ result = handleDeviceConnection(isActive, inDevice, address, btDeviceName) && result;
+ return result;
+ }
- if (!success) {
- return;
+ void setBtScoActiveDevice(BluetoothDevice btDevice) {
+ if (DEBUG_DEVICES) {
+ Log.d(TAG, "setBtScoActiveDevice(" + btDevice + ")");
}
-
- /* When one BT headset is disconnected while another BT headset
- * is connected, don't mess with the headset device.
- */
- if ((state == BluetoothProfile.STATE_DISCONNECTED ||
- state == BluetoothProfile.STATE_DISCONNECTING) &&
- mBluetoothHeadset != null &&
- mBluetoothHeadset.getAudioState(btDevice) == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
- Log.w(TAG, "SCO connected through another device, returning");
- return;
- }
-
synchronized (mScoClients) {
- if (connected) {
+ final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
+ if (!Objects.equals(btDevice, previousActiveDevice)) {
+ if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
+ Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
+ + previousActiveDevice);
+ }
+ if (!handleBtScoActiveDeviceChange(btDevice, true)) {
+ Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
+ // set mBluetoothHeadsetDevice to null when failing to add new device
+ btDevice = null;
+ }
mBluetoothHeadsetDevice = btDevice;
- } else {
- mBluetoothHeadsetDevice = null;
- resetBluetoothSco();
+ if (mBluetoothHeadsetDevice == null) {
+ resetBluetoothSco();
+ }
}
}
}
@@ -3431,12 +3442,7 @@
// Discard timeout message
mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
mBluetoothHeadset = (BluetoothHeadset) proxy;
- deviceList = mBluetoothHeadset.getConnectedDevices();
- if (deviceList.size() > 0) {
- mBluetoothHeadsetDevice = deviceList.get(0);
- } else {
- mBluetoothHeadsetDevice = null;
- }
+ setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
// Refresh SCO audio state
checkScoAudioState();
// Continue pending action if any
@@ -3557,10 +3563,7 @@
void disconnectHeadset() {
synchronized (mScoClients) {
- if (mBluetoothHeadsetDevice != null) {
- setBtScoDeviceConnectionState(mBluetoothHeadsetDevice,
- BluetoothProfile.STATE_DISCONNECTED);
- }
+ setBtScoActiveDevice(null);
mBluetoothHeadset = null;
}
}
@@ -4101,24 +4104,37 @@
}
}
+ @Override
+ public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
+ {
+ }
+
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
{
+ return setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ device, state, profile, false /* suppressNoisyIntent */);
+ }
+
+ public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
+ int state, int profile, boolean suppressNoisyIntent)
+ {
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
return 0;
}
return setBluetoothA2dpDeviceConnectionStateInt(
- device, state, profile, AudioSystem.DEVICE_NONE);
+ device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE);
}
public int setBluetoothA2dpDeviceConnectionStateInt(
- BluetoothDevice device, int state, int profile, int musicDevice)
+ BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
+ int musicDevice)
{
int delay;
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
throw new IllegalArgumentException("invalid profile " + profile);
}
synchronized (mConnectedDevices) {
- if (profile == BluetoothProfile.A2DP) {
+ if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
intState, musicDevice);
@@ -5368,7 +5384,7 @@
// consistent with audio policy manager state
setBluetoothA2dpDeviceConnectionStateInt(
btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
- musicDevice);
+ false /* suppressNoisyIntent */, musicDevice);
}
}
}
@@ -5724,11 +5740,9 @@
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
}
mDockState = dockState;
- } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
- state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
- BluetoothProfile.STATE_DISCONNECTED);
+ } else if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- setBtScoDeviceConnectionState(btDevice, state);
+ setBtScoActiveDevice(btDevice);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
new file mode 100644
index 0000000..24865bc
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
@@ -0,0 +1,52 @@
+/*
+ * 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.connectivity;
+
+/**
+ * A class encapsulating various constants used by Connectivity.
+ * @hide
+ */
+public class ConnectivityConstants {
+ // IPC constants
+ public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
+ "android.net.conn.NETWORK_CONDITIONS_MEASURED";
+ public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
+ public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
+ public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
+ public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
+ public static final String EXTRA_CELL_ID = "extra_cellid";
+ public static final String EXTRA_SSID = "extra_ssid";
+ public static final String EXTRA_BSSID = "extra_bssid";
+ /** real time since boot */
+ public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
+ public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
+
+ public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
+ "android.permission.ACCESS_NETWORK_CONDITIONS";
+
+ // Penalty applied to scores of Networks that have not been validated.
+ public static final int UNVALIDATED_SCORE_PENALTY = 40;
+
+ // Score for explicitly connected network.
+ //
+ // This ensures that a) the explicitly selected network is never trumped by anything else, and
+ // b) the explicitly selected network is never torn down.
+ public static final int MAXIMUM_NETWORK_SCORE = 100;
+ // VPNs typically have priority over other networks. Give them a score that will
+ // let them win every single time.
+ public static final int VPN_DEFAULT_SCORE = 101;
+}
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index bd2e96e..e43d152 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -150,7 +150,8 @@
fillLinkInfo(ev, newNai);
ev.initialScore = newNai.getCurrentScore();
if (newNai.lastValidated) {
- logDefaultNetworkValidity(timeMs, true);
+ mIsCurrentlyValid = true;
+ mLastValidationTimeMs = timeMs;
}
} else {
mIsCurrentlyValid = false;
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
new file mode 100644
index 0000000..a1c54bd
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -0,0 +1,323 @@
+/*
+ * 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.connectivity;
+
+import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
+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.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
+import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
+import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkUtils;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.INetworkManagementService;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.system.GaiException;
+import android.system.OsConstants;
+import android.system.StructAddrinfo;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.server.connectivity.MockableSystemProperties;
+
+import libcore.io.Libcore;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.StringJoiner;
+
+
+/**
+ * Encapsulate the management of DNS settings for networks.
+ *
+ * This class it NOT designed for concurrent access. Furthermore, all non-static
+ * methods MUST be called from ConnectivityService's thread.
+ *
+ * @hide
+ */
+public class DnsManager {
+ private static final String TAG = DnsManager.class.getSimpleName();
+
+ /* Defaults for resolver parameters. */
+ private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
+ private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
+ private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
+ private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
+
+ public static class PrivateDnsConfig {
+ public final boolean useTls;
+ public final String hostname;
+ public final InetAddress[] ips;
+
+ public PrivateDnsConfig() {
+ this(false);
+ }
+
+ public PrivateDnsConfig(boolean useTls) {
+ this.useTls = useTls;
+ this.hostname = "";
+ this.ips = new InetAddress[0];
+ }
+
+ public PrivateDnsConfig(String hostname, InetAddress[] ips) {
+ this.useTls = !TextUtils.isEmpty(hostname);
+ this.hostname = useTls ? hostname : "";
+ this.ips = (ips != null) ? ips : new InetAddress[0];
+ }
+
+ public PrivateDnsConfig(PrivateDnsConfig cfg) {
+ useTls = cfg.useTls;
+ hostname = cfg.hostname;
+ ips = cfg.ips;
+ }
+
+ public boolean inStrictMode() {
+ return useTls && !TextUtils.isEmpty(hostname);
+ }
+
+ public String toString() {
+ return PrivateDnsConfig.class.getSimpleName() +
+ "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}";
+ }
+ }
+
+ public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) {
+ final String mode = getPrivateDnsMode(cr);
+
+ final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode);
+
+ if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) {
+ final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER);
+ return new PrivateDnsConfig(specifier, null);
+ }
+
+ return new PrivateDnsConfig(useTls);
+ }
+
+ public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
+ final StructAddrinfo hints = new StructAddrinfo();
+ // Unnecessary, but expressly no AI_ADDRCONFIG.
+ hints.ai_flags = 0;
+ // Fetch all IP addresses at once to minimize re-resolution.
+ hints.ai_family = OsConstants.AF_UNSPEC;
+ hints.ai_socktype = OsConstants.SOCK_DGRAM;
+
+ try {
+ final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId);
+ if (ips != null && ips.length > 0) {
+ return new PrivateDnsConfig(name, ips);
+ }
+ } catch (GaiException ignored) {}
+
+ return null;
+ }
+
+ public static Uri[] getPrivateDnsSettingsUris() {
+ final Uri[] uris = new Uri[2];
+ uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE);
+ uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER);
+ return uris;
+ }
+
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final INetworkManagementService mNMS;
+ private final MockableSystemProperties mSystemProperties;
+ private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
+
+ private int mNumDnsEntries;
+ private int mSampleValidity;
+ private int mSuccessThreshold;
+ private int mMinSamples;
+ private int mMaxSamples;
+ private String mPrivateDnsMode;
+ private String mPrivateDnsSpecifier;
+
+ public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
+ mContext = ctx;
+ mContentResolver = mContext.getContentResolver();
+ mNMS = nms;
+ mSystemProperties = sp;
+ mPrivateDnsMap = new HashMap<>();
+
+ // TODO: Create and register ContentObservers to track every setting
+ // used herein, posting messages to respond to changes.
+ }
+
+ public PrivateDnsConfig getPrivateDnsConfig() {
+ return getPrivateDnsConfig(mContentResolver);
+ }
+
+ public void removeNetwork(Network network) {
+ mPrivateDnsMap.remove(network.netId);
+ }
+
+ public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
+ Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
+ return (cfg != null)
+ ? mPrivateDnsMap.put(network.netId, cfg)
+ : mPrivateDnsMap.remove(network);
+ }
+
+ public void setDnsConfigurationForNetwork(
+ int netId, LinkProperties lp, boolean isDefaultNetwork) {
+ // We only use the PrivateDnsConfig data pushed to this class instance
+ // from ConnectivityService because it works in coordination with
+ // NetworkMonitor to decide which networks need validation and runs the
+ // blocking calls to resolve Private DNS strict mode hostnames.
+ //
+ // At this time we do attempt to enable Private DNS on non-Internet
+ // networks like IMS.
+ final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
+
+ final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
+ final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
+ final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
+
+ final String[] serverStrs = NetworkUtils.makeStrings(
+ strictMode ? Arrays.stream(privateDnsCfg.ips)
+ .filter((ip) -> lp.isReachable(ip))
+ .collect(Collectors.toList())
+ : lp.getDnsServers());
+ final String[] domainStrs = getDomainStrings(lp.getDomains());
+
+ updateParametersSettings();
+ final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
+
+ Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
+ netId, Arrays.toString(serverStrs), Arrays.toString(domainStrs),
+ Arrays.toString(params), useTls, tlsHostname));
+ try {
+ mNMS.setDnsConfigurationForNetwork(
+ netId, serverStrs, domainStrs, params, useTls, tlsHostname);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error setting DNS configuration: " + e);
+ return;
+ }
+
+ // TODO: netd should listen on [::1]:53 and proxy queries to the current
+ // default network, and we should just set net.dns1 to ::1, not least
+ // because applications attempting to use net.dns resolvers will bypass
+ // the privacy protections of things like DNS-over-TLS.
+ if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
+ flushVmDnsCache();
+ }
+
+ public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
+ int last = 0;
+ for (InetAddress dns : dnses) {
+ ++last;
+ setNetDnsProperty(last, dns.getHostAddress());
+ }
+ for (int i = last + 1; i <= mNumDnsEntries; ++i) {
+ setNetDnsProperty(i, "");
+ }
+ mNumDnsEntries = last;
+ }
+
+ private void flushVmDnsCache() {
+ /*
+ * Tell the VMs to toss their DNS caches
+ */
+ final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ /*
+ * Connectivity events can happen before boot has completed ...
+ */
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void updateParametersSettings() {
+ mSampleValidity = getIntSetting(
+ DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
+ DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+ if (mSampleValidity < 0 || mSampleValidity > 65535) {
+ Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
+ DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+ mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
+ }
+
+ mSuccessThreshold = getIntSetting(
+ DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
+ DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+ if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
+ Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
+ DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+ mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
+ }
+
+ mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
+ mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
+ if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
+ Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
+ "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
+ DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
+ mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
+ mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
+ }
+ }
+
+ private int getIntSetting(String which, int dflt) {
+ return Settings.Global.getInt(mContentResolver, which, dflt);
+ }
+
+ private void setNetDnsProperty(int which, String value) {
+ final String key = "net.dns" + which;
+ // Log and forget errors setting unsupported properties.
+ try {
+ mSystemProperties.set(key, value);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
+ }
+ }
+
+ private static String getPrivateDnsMode(ContentResolver cr) {
+ final String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
+ return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE;
+ }
+
+ private static String getStringSetting(ContentResolver cr, String which) {
+ return Settings.Global.getString(cr, which);
+ }
+
+ private static String[] getDomainStrings(String domains) {
+ return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 9e1f6b8..0f8fc17 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -18,10 +18,10 @@
import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.connectivity.KeepalivePacketData;
import com.android.server.connectivity.NetworkAgentInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.PacketKeepalive;
+import android.net.KeepalivePacketData;
import android.net.LinkAddress;
import android.net.NetworkAgent;
import android.net.NetworkUtils;
@@ -129,7 +129,7 @@
.append("->")
.append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort))
.append(" interval=" + mInterval)
- .append(" data=" + HexDump.toHexString(mPacket.data))
+ .append(" packetData=" + HexDump.toHexString(mPacket.getPacket()))
.append(" uid=").append(mUid).append(" pid=").append(mPid)
.append(" ]")
.toString();
@@ -172,7 +172,7 @@
}
private int checkInterval() {
- return mInterval >= 20 ? SUCCESS : ERROR_INVALID_INTERVAL;
+ return mInterval >= MIN_INTERVAL ? SUCCESS : ERROR_INVALID_INTERVAL;
}
private int isValid() {
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 25b52da..e786bed 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -252,6 +252,29 @@
addWakeupEvent(event);
}
+ @Override
+ public synchronized void onTcpSocketStatsEvent(int[] networkIds,
+ int[] sentPackets, int[] lostPackets, int[] rttsUs, int[] sentAckDiffsMs) {
+ if (networkIds.length != sentPackets.length
+ || networkIds.length != lostPackets.length
+ || networkIds.length != rttsUs.length
+ || networkIds.length != sentAckDiffsMs.length) {
+ Log.e(TAG, "Mismatched lengths of TCP socket stats data arrays");
+ return;
+ }
+
+ long timestamp = System.currentTimeMillis();
+ for (int i = 0; i < networkIds.length; i++) {
+ int netId = networkIds[i];
+ int sent = sentPackets[i];
+ int lost = lostPackets[i];
+ int rttUs = rttsUs[i];
+ int sentAckDiffMs = sentAckDiffsMs[i];
+ getMetricsForNetwork(timestamp, netId)
+ .addTcpStatsResult(sent, lost, rttUs, sentAckDiffMs);
+ }
+ }
+
private void addWakeupEvent(WakeupEvent event) {
String iface = event.iface;
mWakeupEvents.append(event);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index a4d7242..505480e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -223,14 +223,6 @@
// This represents the last score received from the NetworkAgent.
private int currentScore;
- // Penalty applied to scores of Networks that have not been validated.
- private static final int UNVALIDATED_SCORE_PENALTY = 40;
-
- // Score for explicitly connected network.
- //
- // This ensures that a) the explicitly selected network is never trumped by anything else, and
- // b) the explicitly selected network is never torn down.
- private static final int MAXIMUM_NETWORK_SCORE = 100;
// The list of NetworkRequests being satisfied by this Network.
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
@@ -293,7 +285,6 @@
int delta = add ? +1 : -1;
switch (request.type) {
case REQUEST:
- case TRACK_DEFAULT:
mNumRequestNetworkRequests += delta;
break;
@@ -302,6 +293,7 @@
mNumBackgroundNetworkRequests += delta;
break;
+ case TRACK_DEFAULT:
case LISTEN:
break;
@@ -392,12 +384,24 @@
/**
* Returns whether the network is a background network. A network is a background network if it
- * is satisfying no foreground requests and at least one background request. (If it did not have
- * a background request, it would be a speculative network that is only being kept up because
- * it might satisfy a request if it validated).
+ * does not have the NET_CAPABILITY_FOREGROUND capability, which implies it is satisfying no
+ * foreground request, is not lingering (i.e. kept for a while after being outscored), and is
+ * not a speculative network (i.e. kept pending validation when validation would have it
+ * outscore another foreground network). That implies it is being kept up by some background
+ * request (otherwise it would be torn down), maybe the mobile always-on request.
*/
public boolean isBackgroundNetwork() {
- return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0;
+ return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0
+ && !isLingering();
+ }
+
+ /**
+ * Returns whether this network is currently suspended. A network is suspended if it is still
+ * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
+ * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
+ */
+ public boolean isSuspended() {
+ return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
}
// Does this network satisfy request?
@@ -428,12 +432,12 @@
// down an explicitly selected network before the user gets a chance to prefer it when
// a higher-scoring network (e.g., Ethernet) is available.
if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
- return MAXIMUM_NETWORK_SCORE;
+ return ConnectivityConstants.MAXIMUM_NETWORK_SCORE;
}
int score = currentScore;
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
- score -= UNVALIDATED_SCORE_PENALTY;
+ score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
if (score < 0) score = 0;
return score;
@@ -466,7 +470,7 @@
public NetworkState getNetworkState() {
synchronized (this) {
- // Network objects are outwardly immutable so there is no point to duplicating.
+ // Network objects are outwardly immutable so there is no point in duplicating.
// Duplicating also precludes sharing socket factories and connection pools.
final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
return new NetworkState(new NetworkInfo(networkInfo),
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 85d1d1e..c471f0c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -24,6 +24,7 @@
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.TrafficStats;
+import android.net.util.NetworkConstants;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
@@ -421,8 +422,6 @@
private class IcmpCheck extends SimpleSocketCheck implements Runnable {
private static final int TIMEOUT_SEND = 100;
private static final int TIMEOUT_RECV = 300;
- private static final int ICMPV4_ECHO_REQUEST = 8;
- private static final int ICMPV6_ECHO_REQUEST = 128;
private static final int PACKET_BUFSIZE = 512;
private final int mProtocol;
private final int mIcmpType;
@@ -432,11 +431,11 @@
if (mAddressFamily == AF_INET6) {
mProtocol = IPPROTO_ICMPV6;
- mIcmpType = ICMPV6_ECHO_REQUEST;
+ mIcmpType = NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
mMeasurement.description = "ICMPv6";
} else {
mProtocol = IPPROTO_ICMP;
- mIcmpType = ICMPV4_ECHO_REQUEST;
+ mIcmpType = NetworkConstants.ICMPV4_ECHO_REQUEST_TYPE;
mMeasurement.description = "ICMPv4";
}
@@ -504,7 +503,6 @@
private class DnsUdpCheck extends SimpleSocketCheck implements Runnable {
private static final int TIMEOUT_SEND = 100;
private static final int TIMEOUT_RECV = 500;
- private static final int DNS_SERVER_PORT = 53;
private static final int RR_TYPE_A = 1;
private static final int RR_TYPE_AAAA = 28;
private static final int PACKET_BUFSIZE = 512;
@@ -546,7 +544,8 @@
}
try {
- setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV, DNS_SERVER_PORT);
+ setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV,
+ NetworkConstants.DNS_SERVER_PORT);
} catch (ErrnoException | IOException e) {
mMeasurement.recordFailure(e.toString());
return;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 7684030..8a2e71c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -29,6 +29,7 @@
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.ProxyInfo;
import android.net.TrafficStats;
@@ -121,22 +122,6 @@
}
}
- public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
- "android.net.conn.NETWORK_CONDITIONS_MEASURED";
- public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type";
- public static final String EXTRA_NETWORK_TYPE = "extra_network_type";
- public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received";
- public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal";
- public static final String EXTRA_CELL_ID = "extra_cellid";
- public static final String EXTRA_SSID = "extra_ssid";
- public static final String EXTRA_BSSID = "extra_bssid";
- /** real time since boot */
- public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms";
- public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms";
-
- private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
- "android.permission.ACCESS_NETWORK_CONDITIONS";
-
// After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
// The network should be used as a default internet connection. It was found to be:
// 1. a functioning network providing internet access, or
@@ -215,6 +200,15 @@
*/
private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12;
+ /**
+ * ConnectivityService notifies NetworkMonitor of settings changes to
+ * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in
+ * strict mode, then an event is sent back to ConnectivityService with the
+ * result of the resolution attempt.
+ */
+ private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
+ public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
+
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -230,6 +224,12 @@
private static final int NUM_VALIDATION_LOG_LINES = 20;
+ public static boolean isValidationRequired(
+ NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
+ // TODO: Consider requiring validation for DUN networks.
+ return dfltNetCap.satisfiedByNetworkCapabilities(nc);
+ }
+
private final Context mContext;
private final Handler mConnectivityServiceHandler;
private final NetworkAgentInfo mNetworkAgentInfo;
@@ -261,6 +261,8 @@
public boolean systemReady = false;
+ private DnsManager.PrivateDnsConfig mPrivateDnsCfg = null;
+
private final State mDefaultState = new DefaultState();
private final State mValidatedState = new ValidatedState();
private final State mMaybeNotifyState = new MaybeNotifyState();
@@ -342,6 +344,11 @@
return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
}
+ private boolean isValidationRequired() {
+ return isValidationRequired(
+ mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
+ }
+
// DefaultState is the parent of all States. It exists only to handle CMD_* messages but
// does not entail any real state (hence no enter() or exit() routines).
private class DefaultState extends State {
@@ -405,6 +412,18 @@
break;
}
return HANDLED;
+ case CMD_PRIVATE_DNS_SETTINGS_CHANGED:
+ if (isValidationRequired()) {
+ // This performs a blocking DNS resolution of the
+ // strict mode hostname, if required.
+ resolvePrivateDnsConfig((DnsManager.PrivateDnsConfig) message.obj);
+ if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode()) {
+ mConnectivityServiceHandler.sendMessage(obtainMessage(
+ EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId,
+ new DnsManager.PrivateDnsConfig(mPrivateDnsCfg)));
+ }
+ }
+ return HANDLED;
default:
return HANDLED;
}
@@ -421,7 +440,7 @@
maybeLogEvaluationResult(
networkEventType(validationStage(), EvaluationResult.VALIDATED));
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_VALID, mNetId, null));
+ NETWORK_TEST_RESULT_VALID, mNetId, mPrivateDnsCfg));
mValidations++;
}
@@ -567,9 +586,9 @@
// the network so don't bother validating here. Furthermore sending HTTP
// packets over the network may be undesirable, for example an extremely
// expensive metered network, or unwanted leaking of the User Agent string.
- if (!mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities(
- mNetworkAgentInfo.networkCapabilities)) {
+ if (!isValidationRequired()) {
validationLog("Network would not satisfy default request, not validating");
+ mPrivateDnsCfg = null;
transitionTo(mValidatedState);
return HANDLED;
}
@@ -582,6 +601,7 @@
// if this is found to cause problems.
CaptivePortalProbeResult probeResult = isCaptivePortal();
if (probeResult.isSuccessful()) {
+ resolvePrivateDnsConfig();
transitionTo(mValidatedState);
} else if (probeResult.isPortal()) {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
@@ -1045,6 +1065,44 @@
return null;
}
+ public void notifyPrivateDnsSettingsChanged(DnsManager.PrivateDnsConfig newCfg) {
+ // Cancel any outstanding resolutions.
+ removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
+ // Send the update to the proper thread.
+ sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
+ }
+
+ private void resolvePrivateDnsConfig() {
+ resolvePrivateDnsConfig(DnsManager.getPrivateDnsConfig(mContext.getContentResolver()));
+ }
+
+ private void resolvePrivateDnsConfig(DnsManager.PrivateDnsConfig cfg) {
+ // Nothing to do.
+ if (cfg == null) {
+ mPrivateDnsCfg = null;
+ return;
+ }
+
+ // No DNS resolution required.
+ if (!cfg.inStrictMode()) {
+ mPrivateDnsCfg = cfg;
+ return;
+ }
+
+ if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode() &&
+ (mPrivateDnsCfg.ips.length > 0) && mPrivateDnsCfg.hostname.equals(cfg.hostname)) {
+ // We have already resolved this strict mode hostname. Assume that
+ // Private DNS services won't be changing serving IP addresses very
+ // frequently and save ourselves one re-resolve.
+ return;
+ }
+
+ mPrivateDnsCfg = cfg;
+ final DnsManager.PrivateDnsConfig resolvedCfg = DnsManager.tryBlockingResolveOf(
+ mNetwork, mPrivateDnsCfg.hostname);
+ if (resolvedCfg != null) mPrivateDnsCfg = resolvedCfg;
+ }
+
/**
* @param responseReceived - whether or not we received a valid HTTP response to our request.
* If false, isCaptivePortal and responseTimestampMs are ignored
@@ -1062,7 +1120,8 @@
return;
}
- Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED);
+ Intent latencyBroadcast =
+ new Intent(ConnectivityConstants.ACTION_NETWORK_CONDITIONS_MEASURED);
switch (mNetworkAgentInfo.networkInfo.getType()) {
case ConnectivityManager.TYPE_WIFI:
WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
@@ -1074,15 +1133,18 @@
// not change it here as it would become impossible to tell whether the SSID is
// simply being surrounded by quotes due to the API, or whether those quotes
// are actually part of the SSID.
- latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID());
- latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID());
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_SSID,
+ currentWifiInfo.getSSID());
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_BSSID,
+ currentWifiInfo.getBSSID());
} else {
if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
return;
}
break;
case ConnectivityManager.TYPE_MOBILE:
- latencyBroadcast.putExtra(EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType());
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_NETWORK_TYPE,
+ mTelephonyManager.getNetworkType());
List<CellInfo> info = mTelephonyManager.getAllCellInfo();
if (info == null) return;
int numRegisteredCellInfo = 0;
@@ -1096,16 +1158,16 @@
}
if (cellInfo instanceof CellInfoCdma) {
CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
} else if (cellInfo instanceof CellInfoGsm) {
CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
} else if (cellInfo instanceof CellInfoLte) {
CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
} else if (cellInfo instanceof CellInfoWcdma) {
CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId);
} else {
if (VDBG) logw("Registered cellinfo is unrecognized");
return;
@@ -1116,16 +1178,21 @@
default:
return;
}
- latencyBroadcast.putExtra(EXTRA_CONNECTIVITY_TYPE, mNetworkAgentInfo.networkInfo.getType());
- latencyBroadcast.putExtra(EXTRA_RESPONSE_RECEIVED, responseReceived);
- latencyBroadcast.putExtra(EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CONNECTIVITY_TYPE,
+ mNetworkAgentInfo.networkInfo.getType());
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_RECEIVED,
+ responseReceived);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_REQUEST_TIMESTAMP_MS,
+ requestTimestampMs);
if (responseReceived) {
- latencyBroadcast.putExtra(EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal);
- latencyBroadcast.putExtra(EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_IS_CAPTIVE_PORTAL,
+ isCaptivePortal);
+ latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_TIMESTAMP_MS,
+ responseTimestampMs);
}
mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
- PERMISSION_ACCESS_NETWORK_CONDITIONS);
+ ConnectivityConstants.PERMISSION_ACCESS_NETWORK_CONDITIONS);
}
private void logNetworkEvent(int evtype) {
diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS
index 6f77e04..ce50558 100644
--- a/services/core/java/com/android/server/connectivity/OWNERS
+++ b/services/core/java/com/android/server/connectivity/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index d56fb1a..3a27fcb3 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -54,12 +54,12 @@
* @hide
*/
public class PacManager {
- public static final String PAC_PACKAGE = "com.android.pacprocessor";
- public static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
- public static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
+ private static final String PAC_PACKAGE = "com.android.pacprocessor";
+ private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
+ private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
- public static final String PROXY_PACKAGE = "com.android.proxyhandler";
- public static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
+ private static final String PROXY_PACKAGE = "com.android.proxyhandler";
+ private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
private static final String TAG = "PacManager";
@@ -71,8 +71,6 @@
private static final int DELAY_LONG = 4;
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
- /** Keep these values up-to-date with ProxyService.java */
- public static final String KEY_PROXY = "keyProxy";
private String mCurrentPac;
@GuardedBy("mProxyLock")
private volatile Uri mPacUrl = Uri.EMPTY;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d1bab89..69dec2d 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,7 +19,6 @@
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
-import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -43,7 +42,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
@@ -53,9 +51,7 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
-import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
@@ -76,7 +72,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -85,8 +80,6 @@
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.IccCardConstants;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
@@ -110,12 +103,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -193,8 +182,6 @@
private int mLastNotificationId;
private boolean mRndisEnabled; // track the RNDIS function enabled state
- private boolean mUsbTetherRequested; // true if USB tethering should be started
- // when RNDIS is enabled
// True iff. WiFi tethering should be started when soft AP is ready.
private boolean mWifiTetherRequested;
@@ -508,6 +495,7 @@
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final long ident = Binder.clearCallingIdentity();
try {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
@@ -867,33 +855,18 @@
//
// For more explanation, see b/62552150 .
synchronized (Tethering.this.mPublicSync) {
- // Always record the state of RNDIS.
- // TODO: consider:
- // final boolean disconnected = !usbConnected;
- // if (disconnected) {
- // mRndisEnabled = false;
- // mUsbTetherRequested = false;
- // return;
- // }
- // final boolean configured = usbConnected && usbConfigured;
- // mRndisEnabled = configured ? rndisEnabled : false;
- // if (!configured) return;
- mRndisEnabled = rndisEnabled;
-
- if (usbConnected && !usbConfigured) {
- // Nothing to do here (only CONNECTED, not yet CONFIGURED).
- return;
- }
-
- // start tethering if we have a request pending
- if (usbConfigured && mRndisEnabled && mUsbTetherRequested) {
+ if (!usbConnected && mRndisEnabled) {
+ // Turn off tethering if it was enabled and there is a disconnect.
+ tetherMatchingInterfaces(
+ IControlsTethering.STATE_AVAILABLE,
+ ConnectivityManager.TETHERING_USB);
+ } else if (usbConfigured && rndisEnabled) {
+ // Tether if rndis is enabled and usb is configured.
tetherMatchingInterfaces(
IControlsTethering.STATE_TETHERED,
ConnectivityManager.TETHERING_USB);
}
-
- // TODO: Figure out how to remove the need for this variable.
- mUsbTetherRequested = false;
+ mRndisEnabled = usbConfigured && rndisEnabled;
}
}
@@ -1065,34 +1038,8 @@
public int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
-
synchronized (mPublicSync) {
- if (enable) {
- if (mRndisEnabled) {
- final long ident = Binder.clearCallingIdentity();
- try {
- tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED,
- ConnectivityManager.TETHERING_USB);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- } else {
- mUsbTetherRequested = true;
- usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
- }
- } else {
- final long ident = Binder.clearCallingIdentity();
- try {
- tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE,
- ConnectivityManager.TETHERING_USB);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- if (mRndisEnabled) {
- usbManager.setCurrentFunction(null, false);
- }
- mUsbTetherRequested = false;
- }
+ usbManager.setCurrentFunction(enable ? UsbManager.USB_FUNCTION_RNDIS : null, false);
}
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
@@ -1149,7 +1096,7 @@
if (!mForwardedDownstreams.isEmpty()) return true;
synchronized (mPublicSync) {
- return mUsbTetherRequested || mWifiTetherRequested;
+ return mWifiTetherRequested;
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7715727..bb46d5e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.BIND_VPN_SERVICE;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.RouteInfo.RTN_THROW;
@@ -128,7 +129,7 @@
// Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
// the device idle whitelist during service launch and VPN bootstrap.
- private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION = 60 * 1000;
+ private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS = 60 * 1000;
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -163,19 +164,6 @@
private boolean mLockdown = false;
/**
- * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is
- * added to this set but that can be changed by adding allowed or disallowed applications. It
- * is non-null iff the VPN is connected.
- *
- * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN.
- *
- * @see VpnService.Builder#addAllowedApplication(String)
- * @see VpnService.Builder#addDisallowedApplication(String)
- */
- @GuardedBy("this")
- private Set<UidRange> mVpnUsers = null;
-
- /**
* List of UIDs for which networking should be blocked until VPN is ready, during brief periods
* when VPN is not running. For example, during system startup or after a crash.
* @see mLockdown
@@ -183,10 +171,10 @@
@GuardedBy("this")
private Set<UidRange> mBlockedUsers = new ArraySet<>();
- // Handle of user initiating VPN.
+ // Handle of the user initiating VPN.
private final int mUserHandle;
- // Listen to package remove and change event in this user
+ // Listen to package removal and change events (update/uninstall) for this user
private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -197,14 +185,14 @@
}
synchronized (Vpn.this) {
- // Avoid race that always-on package has been unset
+ // Avoid race where always-on package has been unset
if (!packageName.equals(getAlwaysOnPackage())) {
return;
}
final String action = intent.getAction();
- Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
- + " in user " + mUserHandle);
+ Log.i(TAG, "Received broadcast " + action + " for always-on VPN package "
+ + packageName + " in user " + mUserHandle);
switch(action) {
case Intent.ACTION_PACKAGE_REPLACED:
@@ -248,7 +236,8 @@
Log.wtf(TAG, "Problem registering observer", e);
}
- mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE,
+ "" /* subtypeName */);
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
@@ -258,7 +247,7 @@
}
/**
- * Set if this object is responsible for watching for {@link NetworkInfo}
+ * Set whether this object is responsible for watching for {@link NetworkInfo}
* teardown. When {@code false}, teardown is handled externally by someone
* else.
*/
@@ -297,14 +286,17 @@
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
boolean metered = false;
boolean roaming = false;
+ boolean congested = false;
if (ArrayUtils.isEmpty(underlyingNetworks)) {
// No idea what the underlying networks are; assume sane defaults
metered = true;
roaming = false;
+ congested = false;
} else {
for (Network underlying : underlyingNetworks) {
final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
+ if (underlyingCaps == null) continue;
for (int underlyingType : underlyingCaps.getTransportTypes()) {
transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
}
@@ -317,22 +309,16 @@
underlyingCaps.getLinkUpstreamBandwidthKbps());
metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
}
}
caps.setTransportTypes(transportTypes);
caps.setLinkDownstreamBandwidthKbps(downKbps);
caps.setLinkUpstreamBandwidthKbps(upKbps);
- if (metered) {
- caps.removeCapability(NET_CAPABILITY_NOT_METERED);
- } else {
- caps.addCapability(NET_CAPABILITY_NOT_METERED);
- }
- if (roaming) {
- caps.removeCapability(NET_CAPABILITY_NOT_ROAMING);
- } else {
- caps.addCapability(NET_CAPABILITY_NOT_ROAMING);
- }
+ caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+ caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+ caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
}
/**
@@ -480,7 +466,6 @@
}
private void unregisterPackageChangeReceiverLocked() {
- // register previous intent filter
if (mIsPackageIntentReceiverRegistered) {
mContext.unregisterReceiver(mPackageIntentReceiver);
mIsPackageIntentReceiverRegistered = false;
@@ -581,7 +566,7 @@
DeviceIdleController.LocalService idleController =
LocalServices.getService(DeviceIdleController.LocalService.class);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
- VPN_LAUNCH_IDLE_WHITELIST_DURATION, mUserHandle, false, "vpn");
+ VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS, mUserHandle, false, "vpn");
// Start the VPN service declared in the app's manifest.
Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
@@ -611,9 +596,10 @@
* It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
* it can be revoked by itself.
*
- * Note: when we added VPN pre-consent in http://ag/522961 the names oldPackage
- * and newPackage become misleading, because when an app is pre-consented, we
- * actually prepare oldPackage, not newPackage.
+ * Note: when we added VPN pre-consent in
+ * https://android.googlesource.com/platform/frameworks/base/+/0554260
+ * the names oldPackage and newPackage became misleading, because when
+ * an app is pre-consented, we actually prepare oldPackage, not newPackage.
*
* Their meanings actually are:
*
@@ -629,7 +615,7 @@
* @param oldPackage The package name of the old VPN application
* @param newPackage The package name of the new VPN application
*
- * @return true if the operation is succeeded.
+ * @return true if the operation succeeded.
*/
public synchronized boolean prepare(String oldPackage, String newPackage) {
if (oldPackage != null) {
@@ -638,7 +624,7 @@
return false;
}
- // Package is not same or old package was reinstalled.
+ // Package is not the same or old package was reinstalled.
if (!isCurrentPreparedPackage(oldPackage)) {
// The package doesn't match. We return false (to obtain user consent) unless the
// user has already consented to that VPN package.
@@ -689,7 +675,7 @@
agentDisconnect();
jniReset(mInterface);
mInterface = null;
- mVpnUsers = null;
+ mNetworkCapabilities.setUids(null);
}
// Revoke the connection or stop LegacyVpnRunner.
@@ -858,10 +844,14 @@
NetworkMisc networkMisc = new NetworkMisc();
networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
+ mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid());
+ mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
+ mConfig.allowedApplications, mConfig.disallowedApplications));
long token = Binder.clearCallingIdentity();
try {
- mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
- mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
+ mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
+ mNetworkInfo, mNetworkCapabilities, lp,
+ ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc) {
@Override
public void unwanted() {
// We are user controlled, not driven by NetworkRequest.
@@ -870,11 +860,6 @@
} finally {
Binder.restoreCallingIdentity(token);
}
-
- mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle,
- mConfig.allowedApplications, mConfig.disallowedApplications);
- mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
-
mNetworkInfo.setIsAvailable(true);
updateState(DetailedState.CONNECTED, "agentConnect");
}
@@ -935,7 +920,7 @@
}
ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
- null, 0, mUserHandle);
+ null, 0, mUserHandle);
if (info == null) {
throw new SecurityException("Cannot find " + config.user);
}
@@ -943,7 +928,7 @@
throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
}
} catch (RemoteException e) {
- throw new SecurityException("Cannot find " + config.user);
+ throw new SecurityException("Cannot find " + config.user);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -954,7 +939,7 @@
Connection oldConnection = mConnection;
NetworkAgent oldNetworkAgent = mNetworkAgent;
mNetworkAgent = null;
- Set<UidRange> oldUsers = mVpnUsers;
+ Set<UidRange> oldUsers = mNetworkCapabilities.getUids();
// Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
@@ -1012,7 +997,7 @@
// restore old state
mConfig = oldConfig;
mConnection = oldConnection;
- mVpnUsers = oldUsers;
+ mNetworkCapabilities.setUids(oldUsers);
mNetworkAgent = oldNetworkAgent;
mInterface = oldInterface;
throw e;
@@ -1132,10 +1117,12 @@
// Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
// apply to userHandle.
- private List<UidRange> uidRangesForUser(int userHandle) {
+ static private List<UidRange> uidRangesForUser(int userHandle, Set<UidRange> existingRanges) {
+ // UidRange#createForUser returns the entire range of UIDs available to a macro-user.
+ // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
final UidRange userRange = UidRange.createForUser(userHandle);
final List<UidRange> ranges = new ArrayList<UidRange>();
- for (UidRange range : mVpnUsers) {
+ for (UidRange range : existingRanges) {
if (userRange.containsRange(range)) {
ranges.add(range);
}
@@ -1143,30 +1130,18 @@
return ranges;
}
- private void removeVpnUserLocked(int userHandle) {
- if (mVpnUsers == null) {
- throw new IllegalStateException("VPN is not active");
- }
- final List<UidRange> ranges = uidRangesForUser(userHandle);
- if (mNetworkAgent != null) {
- mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
- }
- mVpnUsers.removeAll(ranges);
- }
-
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);
if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
synchronized(Vpn.this) {
- if (mVpnUsers != null) {
+ final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
+ if (existingRanges != null) {
try {
- addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications,
+ addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
mConfig.disallowedApplications);
- if (mNetworkAgent != null) {
- final List<UidRange> ranges = uidRangesForUser(userHandle);
- mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
- }
+ mNetworkCapabilities.setUids(existingRanges);
+ updateCapabilities();
} catch (Exception e) {
Log.wtf(TAG, "Failed to add restricted user to owner", e);
}
@@ -1181,9 +1156,14 @@
UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
synchronized(Vpn.this) {
- if (mVpnUsers != null) {
+ final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
+ if (existingRanges != null) {
try {
- removeVpnUserLocked(userHandle);
+ final List<UidRange> removedRanges =
+ uidRangesForUser(userHandle, existingRanges);
+ existingRanges.removeAll(removedRanges);
+ mNetworkCapabilities.setUids(existingRanges);
+ updateCapabilities();
} catch (Exception e) {
Log.wtf(TAG, "Failed to remove restricted user to owner", e);
}
@@ -1227,15 +1207,6 @@
private void setVpnForcedLocked(boolean enforce) {
final List<String> exemptedPackages =
isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage);
- setVpnForcedWithExemptionsLocked(enforce, exemptedPackages);
- }
-
- /**
- * @see #setVpnForcedLocked
- */
- @GuardedBy("this")
- private void setVpnForcedWithExemptionsLocked(boolean enforce,
- @Nullable List<String> exemptedPackages) {
final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
Set<UidRange> addedRanges = Collections.emptySet();
@@ -1315,7 +1286,7 @@
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
mStatusIntent = null;
- mVpnUsers = null;
+ mNetworkCapabilities.setUids(null);
mConfig = null;
mInterface = null;
if (mConnection != null) {
@@ -1336,7 +1307,7 @@
}
private void enforceControlPermissionOrInternalCaller() {
- // Require caller to be either an application with CONTROL_VPN permission or a process
+ // Require the caller to be either an application with CONTROL_VPN permission or a process
// in the system server.
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
"Unauthorized Caller");
@@ -1416,7 +1387,7 @@
}
/**
- * This method should only be called by ConnectivityService. Because it doesn't
+ * This method should only be called by ConnectivityService because it doesn't
* have enough data to fill VpnInfo.primaryUnderlyingIface field.
*/
public synchronized VpnInfo getVpnInfo() {
@@ -1434,12 +1405,7 @@
if (!isRunningLocked()) {
return false;
}
- for (UidRange uidRange : mVpnUsers) {
- if (uidRange.contains(uid)) {
- return true;
- }
- }
- return false;
+ return mNetworkCapabilities.appliesToUid(uid);
}
/**
@@ -1767,7 +1733,7 @@
* Bringing up a VPN connection takes time, and that is all this thread
* does. Here we have plenty of time. The only thing we need to take
* care of is responding to interruptions as soon as possible. Otherwise
- * requests will be piled up. This can be done in a Handler as a state
+ * requests will pile up. This could be done in a Handler as a state
* machine, but it is much easier to read in the current form.
*/
private class LegacyVpnRunner extends Thread {
@@ -1780,7 +1746,7 @@
private final AtomicInteger mOuterConnection =
new AtomicInteger(ConnectivityManager.TYPE_NONE);
- private long mTimer = -1;
+ private long mBringupStartTime = -1;
/**
* Watch for the outer connection (passing in the constructor) going away.
@@ -1860,8 +1826,8 @@
synchronized (TAG) {
Log.v(TAG, "Executing");
try {
- execute();
- monitorDaemons();
+ bringup();
+ waitForDaemonsToStop();
interrupted(); // Clear interrupt flag if execute called exit.
} catch (InterruptedException e) {
} finally {
@@ -1882,30 +1848,27 @@
}
}
- private void checkpoint(boolean yield) throws InterruptedException {
+ private void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException {
long now = SystemClock.elapsedRealtime();
- if (mTimer == -1) {
- mTimer = now;
- Thread.sleep(1);
- } else if (now - mTimer <= 60000) {
- Thread.sleep(yield ? 200 : 1);
+ if (now - mBringupStartTime <= 60000) {
+ Thread.sleep(sleepLonger ? 200 : 1);
} else {
updateState(DetailedState.FAILED, "checkpoint");
- throw new IllegalStateException("Time is up");
+ throw new IllegalStateException("VPN bringup took too long");
}
}
- private void execute() {
- // Catch all exceptions so we can clean up few things.
+ private void bringup() {
+ // Catch all exceptions so we can clean up a few things.
boolean initFinished = false;
try {
// Initialize the timer.
- checkpoint(false);
+ mBringupStartTime = SystemClock.elapsedRealtime();
// Wait for the daemons to stop.
for (String daemon : mDaemons) {
while (!SystemService.isStopped(daemon)) {
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
}
@@ -1942,7 +1905,7 @@
// Wait for the daemon to start.
while (!SystemService.isRunning(daemon)) {
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
// Create the control socket.
@@ -1958,7 +1921,7 @@
} catch (Exception e) {
// ignore
}
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
mSockets[i].setSoTimeout(500);
@@ -1972,7 +1935,7 @@
out.write(bytes.length >> 8);
out.write(bytes.length);
out.write(bytes);
- checkpoint(false);
+ checkInterruptAndDelay(false);
}
out.write(0xFF);
out.write(0xFF);
@@ -1988,7 +1951,7 @@
} catch (Exception e) {
// ignore
}
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
}
@@ -2001,7 +1964,7 @@
throw new IllegalStateException(daemon + " is dead");
}
}
- checkpoint(true);
+ checkInterruptAndDelay(true);
}
// Now we are connected. Read and parse the new state.
@@ -2057,8 +2020,8 @@
// Set the start time
mConfig.startTime = SystemClock.elapsedRealtime();
- // Check if the thread is interrupted while we are waiting.
- checkpoint(false);
+ // Check if the thread was interrupted while we were waiting on the lock.
+ checkInterruptAndDelay(false);
// Check if the interface is gone while we are waiting.
if (jniCheck(mConfig.interfaze) == 0) {
@@ -2081,10 +2044,11 @@
}
/**
- * Monitor the daemons we started, moving to disconnected state if the
- * underlying services fail.
+ * Check all daemons every two seconds. Return when one of them is stopped.
+ * The caller will move to the disconnected state when this function returns,
+ * which can happen if a daemon failed or if the VPN was torn down.
*/
- private void monitorDaemons() throws InterruptedException{
+ private void waitForDaemonsToStop() throws InterruptedException {
if (!mNetworkInfo.isConnected()) {
return;
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 17adb1a..2224913 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -30,6 +30,7 @@
import android.net.ip.InterfaceController;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.net.util.InterfaceParams;
import android.net.util.NetdService;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
@@ -48,7 +49,6 @@
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -120,8 +120,7 @@
private int mLastError;
private int mServingMode;
private String mMyUpstreamIfaceName; // may change over time
- private NetworkInterface mNetworkInterface;
- private byte[] mHwAddr;
+ private InterfaceParams mInterfaceParams;
// TODO: De-duplicate this with mLinkProperties above. Currently, these link
// properties are those selected by the IPv6TetheringCoordinator and relayed
// to us. By comparison, mLinkProperties contains the addresses and directly
@@ -247,31 +246,16 @@
}
private boolean startIPv6() {
- // TODO: Refactor for testability (perhaps passing an android.system.Os
- // instance and calling getifaddrs() directly).
- try {
- mNetworkInterface = NetworkInterface.getByName(mIfaceName);
- } catch (SocketException e) {
- mLog.e("Error looking up NetworkInterfaces: " + e);
- stopIPv6();
- return false;
- }
- if (mNetworkInterface == null) {
- mLog.e("Failed to find NetworkInterface");
+ // TODO: Refactor for better testability. This is one of the things
+ // that prohibits unittesting IPv6 tethering setup.
+ mInterfaceParams = InterfaceParams.getByName(mIfaceName);
+ if (mInterfaceParams == null) {
+ mLog.e("Failed to find InterfaceParams");
stopIPv6();
return false;
}
- try {
- mHwAddr = mNetworkInterface.getHardwareAddress();
- } catch (SocketException e) {
- mLog.e("Failed to find hardware address: " + e);
- stopIPv6();
- return false;
- }
-
- final int ifindex = mNetworkInterface.getIndex();
- mRaDaemon = new RouterAdvertisementDaemon(mIfaceName, ifindex, mHwAddr);
+ mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
if (!mRaDaemon.start()) {
stopIPv6();
return false;
@@ -281,8 +265,7 @@
}
private void stopIPv6() {
- mNetworkInterface = null;
- mHwAddr = null;
+ mInterfaceParams = null;
setRaParams(null);
if (mRaDaemon != null) {
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 acbc10b..09bce7f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -28,6 +28,8 @@
import android.telephony.TelephonyManager;
import android.net.util.SharedLog;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -49,6 +51,7 @@
public class TetheringConfiguration {
private static final String TAG = TetheringConfiguration.class.getSimpleName();
+ @VisibleForTesting
public static final int DUN_NOT_REQUIRED = 0;
public static final int DUN_REQUIRED = 1;
public static final int DUN_UNSPECIFIED = 2;
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index b35ed75..3413291 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IpPrefix;
@@ -476,6 +477,7 @@
ConnectivityManager.getNetworkTypeName(type));
continue;
}
+ nc.setSingleUid(Process.myUid());
for (NetworkState value : netStates) {
if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 205e828..581e8c7 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -323,7 +323,7 @@
private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- EndPoint target = new EndPoint(null, null, context.getUserId());
+ EndPoint target = new EndPoint(null, null, getSendingUserId());
updateRunningAccounts(target /* sync targets for user */);
}
};
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
old mode 100644
new mode 100755
index 97a6e85..db8dedb
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -228,12 +228,20 @@
if (cmd.getOpcode() == Constants.MESSAGE_SET_OSD_NAME) {
handleSetOsdName(cmd);
return true;
+ } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) &&
+ ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_GIVE_OSD_NAME)) {
+ handleSetOsdName(cmd);
+ return true;
}
return false;
case STATE_WAITING_FOR_VENDOR_ID:
if (cmd.getOpcode() == Constants.MESSAGE_DEVICE_VENDOR_ID) {
handleVendorId(cmd);
return true;
+ } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) &&
+ ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID)) {
+ handleVendorId(cmd);
+ return true;
}
return false;
case STATE_WAITING_FOR_DEVICE_POLLING:
@@ -281,7 +289,11 @@
String displayName = null;
try {
- displayName = new String(cmd.getParams(), "US-ASCII");
+ if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
+ displayName = HdmiUtils.getDefaultDeviceName(current.mLogicalAddress);
+ } else {
+ displayName = new String(cmd.getParams(), "US-ASCII");
+ }
} catch (UnsupportedEncodingException e) {
Slog.w(TAG, "Failed to decode display name: " + cmd.toString());
// If failed to get display name, use the default name of device.
@@ -302,9 +314,12 @@
return;
}
- byte[] params = cmd.getParams();
- int vendorId = HdmiUtils.threeBytesToInt(params);
- current.mVendorId = vendorId;
+ if (cmd.getOpcode() != Constants.MESSAGE_FEATURE_ABORT) {
+ byte[] params = cmd.getParams();
+ int vendorId = HdmiUtils.threeBytesToInt(params);
+ current.mVendorId = vendorId;
+ }
+
increaseProcessedDeviceCount();
checkAndProceedStage();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
old mode 100644
new mode 100755
index e5f4282..0cba76b
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -660,7 +660,8 @@
@ServiceThreadOnly
void startQueuedActions() {
assertRunOnServiceThread();
- for (HdmiCecFeatureAction action : mActions) {
+ // Use copied action list in that start() may remove itself.
+ for (HdmiCecFeatureAction action : new ArrayList<>(mActions)) {
if (!action.started()) {
Slog.i(TAG, "Starting queued action:" + action);
action.start();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 81bccdc..1e09383 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -698,10 +698,9 @@
protected boolean handleReportAudioStatus(HdmiCecMessage message) {
assertRunOnServiceThread();
- byte params[] = message.getParams();
- int mute = params[0] & 0x80;
- int volume = params[0] & 0x7F;
- setAudioStatus(mute == 0x80, volume);
+ boolean mute = HdmiUtils.isAudioStatusMute(message);
+ int volume = HdmiUtils.getAudioStatusVolume(message);
+ setAudioStatus(mute, volume);
return true;
}
@@ -1004,6 +1003,9 @@
}
void setAudioStatus(boolean mute, int volume) {
+ if (!isSystemAudioActivated()) {
+ return;
+ }
synchronized (mLock) {
mSystemAudioMute = mute;
mSystemAudioVolume = volume;
@@ -1019,6 +1021,10 @@
@ServiceThreadOnly
void changeVolume(int curVolume, int delta, int maxVolume) {
assertRunOnServiceThread();
+ if (getAvrDeviceInfo() == null) {
+ // On initialization process, getAvrDeviceInfo() may return null and cause exception
+ return;
+ }
if (delta == 0 || !isSystemAudioActivated()) {
return;
}
@@ -1048,6 +1054,10 @@
@ServiceThreadOnly
void changeMute(boolean mute) {
assertRunOnServiceThread();
+ if (getAvrDeviceInfo() == null) {
+ // On initialization process, getAvrDeviceInfo() may return null and cause exception
+ return;
+ }
HdmiLogger.debug("[A]:Change mute:%b", mute);
synchronized (mLock) {
if (mSystemAudioMute == mute) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 807b1b1..3d079cc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -989,8 +989,12 @@
}
// FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
// volume change notification back to hdmi control service.
- audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
- AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
+ int flag = AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME;
+ if (0 <= volume && volume <= 100) {
+ Slog.i(TAG, "volume: " + volume);
+ flag |= AudioManager.FLAG_SHOW_UI;
+ audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, flag);
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 8b16411..4ac3bba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -152,6 +152,32 @@
}
/**
+ * Parse the <Report Audio Status> message and check if it is mute
+ *
+ * @param cmd the CEC message to parse
+ * @return true if the given parameter has [MUTE]
+ */
+ static boolean isAudioStatusMute(HdmiCecMessage cmd) {
+ byte params[] = cmd.getParams();
+ return (params[0] & 0x80) == 0x80;
+ }
+
+ /**
+ * Parse the <Report Audio Status> message and extract the volume
+ *
+ * @param cmd the CEC message to parse
+ * @return device's volume. Constants.UNKNOWN_VOLUME in case it is out of range
+ */
+ static int getAudioStatusVolume(HdmiCecMessage cmd) {
+ byte params[] = cmd.getParams();
+ int volume = params[0] & 0x7F;
+ if (volume < 0x00 || 0x64 < volume) {
+ volume = Constants.UNKNOWN_VOLUME;
+ }
+ return volume;
+ }
+
+ /**
* Convert integer array to list of {@link Integer}.
*
* <p>The result is immutable.
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
index cab8439..d41a36c 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -92,8 +92,8 @@
private void handleReportAudioStatus(HdmiCecMessage cmd) {
byte[] params = cmd.getParams();
- boolean mute = (params[0] & 0x80) == 0x80;
- int volume = params[0] & 0x7F;
+ boolean mute = HdmiUtils.isAudioStatusMute(cmd);
+ int volume = HdmiUtils.getAudioStatusVolume(cmd);
tv().setAudioStatus(mute, volume);
if (!(tv().isSystemAudioActivated() ^ mute)) {
diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
index cd38b1f..0011387 100644
--- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java
+++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
@@ -139,8 +139,8 @@
private boolean handleReportAudioStatus(HdmiCecMessage cmd) {
byte params[] = cmd.getParams();
- boolean mute = (params[0] & 0x80) == 0x80;
- int volume = params[0] & 0x7F;
+ boolean mute = HdmiUtils.isAudioStatusMute(cmd);
+ int volume = HdmiUtils.getAudioStatusVolume(cmd);
mLastAvrVolume = volume;
mLastAvrMute = mute;
if (shouldUpdateAudioVolume(mute)) {
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 78367fe..1644956 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -201,7 +201,7 @@
}
};
- private final INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() {
+ private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
if (DEBUG) {
@@ -211,11 +211,6 @@
}
@Override
- public void onMeteredIfacesChanged(String[] meteredIfaces) {
- // We track this via our NetworkCallback
- }
-
- @Override
public void onRestrictBackgroundChanged(boolean restrictBackground) {
if (DEBUG) {
Slog.v(TAG, "Background restriction change to " + restrictBackground);
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 4d56468..e3e02e3 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -24,11 +24,11 @@
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.net.DelayedDiskWrite;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
@@ -38,8 +38,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.net.InetAddress;
import java.net.Inet4Address;
+import java.net.InetAddress;
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
@@ -60,7 +60,7 @@
protected static final String EXCLUSION_LIST_KEY = "exclusionList";
protected static final String EOS = "eos";
- protected static final int IPCONFIG_FILE_VERSION = 2;
+ protected static final int IPCONFIG_FILE_VERSION = 3;
public IpConfigStore(DelayedDiskWrite writer) {
mWriter = writer;
@@ -70,9 +70,14 @@
this(new DelayedDiskWrite());
}
+ private static boolean writeConfig(DataOutputStream out, String configKey,
+ IpConfiguration config) throws IOException {
+ return writeConfig(out, configKey, config, IPCONFIG_FILE_VERSION);
+ }
+
@VisibleForTesting
- public static boolean writeConfig(DataOutputStream out, int configKey,
- IpConfiguration config) throws IOException {
+ public static boolean writeConfig(DataOutputStream out, String configKey,
+ IpConfiguration config, int version) throws IOException {
boolean written = false;
try {
@@ -153,7 +158,11 @@
if (written) {
out.writeUTF(ID_KEY);
- out.writeInt(configKey);
+ if (version < 3) {
+ out.writeInt(Integer.valueOf(configKey));
+ } else {
+ out.writeUTF(configKey);
+ }
}
} catch (NullPointerException e) {
loge("Failure in writing " + config + e);
@@ -163,18 +172,47 @@
return written;
}
- public void writeIpAndProxyConfigurations(String filePath,
+ /**
+ * @Deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
+ * New method uses string as network identifier which could be interface name or MAC address or
+ * other token.
+ */
+ @Deprecated
+ public void writeIpAndProxyConfigurationsToFile(String filePath,
final SparseArray<IpConfiguration> networks) {
- mWriter.write(filePath, new DelayedDiskWrite.Writer() {
- public void onWriteCalled(DataOutputStream out) throws IOException{
- out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
- writeConfig(out, networks.keyAt(i), networks.valueAt(i));
- }
+ mWriter.write(filePath, out -> {
+ out.writeInt(IPCONFIG_FILE_VERSION);
+ for(int i = 0; i < networks.size(); i++) {
+ writeConfig(out, String.valueOf(networks.keyAt(i)), networks.valueAt(i));
}
});
}
+ public void writeIpConfigurations(String filePath,
+ ArrayMap<String, IpConfiguration> networks) {
+ mWriter.write(filePath, out -> {
+ out.writeInt(IPCONFIG_FILE_VERSION);
+ for(int i = 0; i < networks.size(); i++) {
+ writeConfig(out, networks.keyAt(i), networks.valueAt(i));
+ }
+ });
+ }
+
+ public static ArrayMap<String, IpConfiguration> readIpConfigurations(String filePath) {
+ BufferedInputStream bufferedInputStream;
+ try {
+ bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
+ } catch (FileNotFoundException e) {
+ // Return an empty array here because callers expect an empty array when the file is
+ // not present.
+ loge("Error opening configuration file: " + e);
+ return new ArrayMap<>(0);
+ }
+ return readIpConfigurations(bufferedInputStream);
+ }
+
+ /** @Deprecated use {@link #readIpConfigurations(String)} */
+ @Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
try {
@@ -188,21 +226,40 @@
return readIpAndProxyConfigurations(bufferedInputStream);
}
+ /** @Deprecated use {@link #readIpConfigurations(InputStream)} */
+ @Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(
InputStream inputStream) {
- SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
+ ArrayMap<String, IpConfiguration> networks = readIpConfigurations(inputStream);
+ if (networks == null) {
+ return null;
+ }
+
+ SparseArray<IpConfiguration> networksById = new SparseArray<>();
+ for (int i = 0; i < networks.size(); i++) {
+ int id = Integer.valueOf(networks.keyAt(i));
+ networksById.put(id, networks.valueAt(i));
+ }
+
+ return networksById;
+ }
+
+ /** Returns a map of network identity token and {@link IpConfiguration}. */
+ public static ArrayMap<String, IpConfiguration> readIpConfigurations(
+ InputStream inputStream) {
+ ArrayMap<String, IpConfiguration> networks = new ArrayMap<>();
DataInputStream in = null;
try {
in = new DataInputStream(inputStream);
int version = in.readInt();
- if (version != 2 && version != 1) {
+ if (version != 3 && version != 2 && version != 1) {
loge("Bad version on IP configuration file, ignore read");
return null;
}
while (true) {
- int id = -1;
+ String uniqueToken = null;
// Default is DHCP with no proxy
IpAssignment ipAssignment = IpAssignment.DHCP;
ProxySettings proxySettings = ProxySettings.NONE;
@@ -217,7 +274,12 @@
key = in.readUTF();
try {
if (key.equals(ID_KEY)) {
- id = in.readInt();
+ if (version < 3) {
+ int id = in.readInt();
+ uniqueToken = String.valueOf(id);
+ } else {
+ uniqueToken = in.readUTF();
+ }
} else if (key.equals(IP_ASSIGNMENT_KEY)) {
ipAssignment = IpAssignment.valueOf(in.readUTF());
} else if (key.equals(LINK_ADDRESS_KEY)) {
@@ -280,9 +342,9 @@
}
} while (true);
- if (id != -1) {
+ if (uniqueToken != null) {
IpConfiguration config = new IpConfiguration();
- networks.put(id, config);
+ networks.put(uniqueToken, config);
switch (ipAssignment) {
case STATIC:
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index ee00fdc..68cd5e7 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -39,6 +39,7 @@
private static final int VERSION_ADD_ROAMING = 2;
private static final int VERSION_ADD_NETWORK_ID = 3;
private static final int VERSION_ADD_METERED = 4;
+ private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
public NetworkIdentitySet() {
}
@@ -76,12 +77,20 @@
metered = (type == TYPE_MOBILE);
}
- add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered));
+ final boolean defaultNetwork;
+ if (version >= VERSION_ADD_DEFAULT_NETWORK) {
+ defaultNetwork = in.readBoolean();
+ } else {
+ defaultNetwork = true;
+ }
+
+ add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+ defaultNetwork));
}
}
public void writeToStream(DataOutputStream out) throws IOException {
- out.writeInt(VERSION_ADD_METERED);
+ out.writeInt(VERSION_ADD_DEFAULT_NETWORK);
out.writeInt(size());
for (NetworkIdentity ident : this) {
out.writeInt(ident.getType());
@@ -90,6 +99,7 @@
writeOptionalString(out, ident.getNetworkId());
out.writeBoolean(ident.getRoaming());
out.writeBoolean(ident.getMetered());
+ out.writeBoolean(ident.getDefaultNetwork());
}
}
@@ -119,6 +129,20 @@
return false;
}
+ /** @return whether any {@link NetworkIdentity} in this set is considered on the default
+ network. */
+ public boolean areAllMembersOnDefaultNetwork() {
+ if (isEmpty()) {
+ return true;
+ }
+ for (NetworkIdentity ident : this) {
+ if (!ident.getDefaultNetwork()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
if (value != null) {
out.writeByte(1);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5159c70..948e75e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1103,7 +1103,8 @@
for (int subId : subIds) {
final String subscriberId = tele.getSubscriberId(subId);
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+ true);
if (template.matches(probeIdent)) {
return true;
}
@@ -1304,7 +1305,7 @@
// find and update the mobile NetworkPolicy for this subscriber id
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -1511,7 +1512,8 @@
for (int subId : subIds) {
final String subscriberId = tm.getSubscriberId(subId);
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+ true);
// Template is matched when subscriber id matches.
if (template.matches(probeIdent)) {
tm.setPolicyDataEnabled(enabled, subId);
@@ -1557,7 +1559,8 @@
final ArrayMap<NetworkState, NetworkIdentity> identified = new ArrayMap<>();
for (NetworkState state : states) {
if (state.networkInfo != null && state.networkInfo.isConnected()) {
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+ true);
identified.put(state, ident);
}
}
@@ -1692,7 +1695,7 @@
private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) {
// Poke around to see if we already have a policy
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 4ceb592..961a451 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -17,6 +17,8 @@
package com.android.server.net;
import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkStats.ROAMING_NO;
@@ -364,6 +366,8 @@
entry.uid = key.uid;
entry.set = key.set;
entry.tag = key.tag;
+ entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork() ?
+ DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
entry.rxBytes = historyEntry.rxBytes;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index db61ef5..bfc150e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -25,6 +25,7 @@
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.ROAMING_ALL;
@@ -83,6 +84,7 @@
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
@@ -231,14 +233,24 @@
private final Object mStatsLock = new Object();
/** Set of currently active ifaces. */
+ @GuardedBy("mStatsLock")
private final ArrayMap<String, NetworkIdentitySet> mActiveIfaces = new ArrayMap<>();
+
/** Set of currently active ifaces for UID stats. */
+ @GuardedBy("mStatsLock")
private final ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces = new ArrayMap<>();
+
/** Current default active iface. */
private String mActiveIface;
+
/** Set of any ifaces associated with mobile networks since boot. */
+ @GuardedBy("mStatsLock")
private String[] mMobileIfaces = new String[0];
+ /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
+ @GuardedBy("mStatsLock")
+ private Network[] mDefaultNetworks = new Network[0];
+
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
new DropBoxNonMonotonicObserver();
@@ -666,9 +678,9 @@
final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
final NetworkStats stats = new NetworkStats(end - start, 1);
- stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL,
- ROAMING_ALL, entry.rxBytes, entry.rxPackets, entry.txBytes, entry.txPackets,
- entry.operations));
+ stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
+ entry.txBytes, entry.txPackets, entry.operations));
return stats;
}
@@ -779,13 +791,13 @@
}
@Override
- public void forceUpdateIfaces() {
+ public void forceUpdateIfaces(Network[] defaultNetworks) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
assertBandwidthControlEnabled();
final long token = Binder.clearCallingIdentity();
try {
- updateIfaces();
+ updateIfaces(defaultNetworks);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -875,17 +887,21 @@
@Override
public long getUidStats(int uid, int type) {
- return nativeGetUidStat(uid, type);
+ return nativeGetUidStat(uid, type, checkBpfStatsEnable());
}
@Override
public long getIfaceStats(String iface, int type) {
- return nativeGetIfaceStat(iface, type);
+ return nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
}
@Override
public long getTotalStats(int type) {
- return nativeGetTotalStat(type);
+ return nativeGetTotalStat(type, checkBpfStatsEnable());
+ }
+
+ private boolean checkBpfStatsEnable() {
+ return new File("/sys/fs/bpf/traffic_uid_stats_map").exists();
}
/**
@@ -996,11 +1012,11 @@
}
};
- private void updateIfaces() {
+ private void updateIfaces(Network[] defaultNetworks) {
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
- updateIfacesLocked();
+ updateIfacesLocked(defaultNetworks);
} finally {
mWakeLock.release();
}
@@ -1013,7 +1029,7 @@
* are active on a single {@code iface}, they are combined under a single
* {@link NetworkIdentitySet}.
*/
- private void updateIfacesLocked() {
+ private void updateIfacesLocked(Network[] defaultNetworks) {
if (!mSystemReady) return;
if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
@@ -1040,12 +1056,18 @@
// Rebuild active interfaces based on connected networks
mActiveIfaces.clear();
mActiveUidIfaces.clear();
+ if (defaultNetworks != null) {
+ // Caller is ConnectivityService. Update the list of default networks.
+ mDefaultNetworks = defaultNetworks;
+ }
final ArraySet<String> mobileIfaces = new ArraySet<>();
for (NetworkState state : states) {
if (state.networkInfo.isConnected()) {
final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
+ final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+ isDefault);
// Traffic occurring on the base interface is always counted for
// both total usage and UID details.
@@ -1065,7 +1087,8 @@
// Copy the identify from IMS one but mark it as metered.
NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
- ident.getRoaming(), true);
+ ident.getRoaming(), true /* metered */,
+ true /* onDefaultNetwork */);
findOrCreateNetworkIdentitySet(mActiveIfaces, VT_INTERFACE).add(vtIdent);
findOrCreateNetworkIdentitySet(mActiveUidIfaces, VT_INTERFACE).add(vtIdent);
}
@@ -1511,7 +1534,7 @@
return true;
}
case MSG_UPDATE_IFACES: {
- mService.updateIfaces();
+ mService.updateIfaces(null);
return true;
}
case MSG_REGISTER_GLOBAL_ALERT: {
@@ -1649,7 +1672,7 @@
private static int TYPE_TCP_RX_PACKETS;
private static int TYPE_TCP_TX_PACKETS;
- private static native long nativeGetTotalStat(int type);
- private static native long nativeGetIfaceStat(String iface, int type);
- private static native long nativeGetUidStat(int uid, int type);
+ private static native long nativeGetTotalStat(int type, boolean useBpfStats);
+ private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats);
+ private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats);
}
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 5d4287b..64dc98e 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
jsharkey@android.com
lorenzo@google.com
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b7b91a7..625764c 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -39,8 +39,10 @@
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Build;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -82,6 +84,7 @@
protected final String TAG = getClass().getSimpleName();
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
/**
@@ -101,12 +104,15 @@
private final IPackageManager mPm;
private final UserManager mUm;
private final Config mConfig;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
// contains connections to all connected services, including app services
// and system services
private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
// things that will be put into mServices as soon as they're ready
private final ArrayList<String> mServicesBinding = new ArrayList<>();
+ private final ArraySet<String> mServicesRebinding = new ArraySet<>();
+
// lists the component names of all enabled (and therefore potentially connected)
// app services for current profiles.
private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
@@ -823,6 +829,7 @@
final String servicesBindingTag = name.toString() + "/" + userid;
if (mServicesBinding.contains(servicesBindingTag)) {
+ Slog.v(TAG, "Not registering " + name + " as bind is already in progress");
// stop registering this thing already! we're working on it
return;
}
@@ -871,6 +878,7 @@
boolean added = false;
ManagedServiceInfo info = null;
synchronized (mMutex) {
+ mServicesRebinding.remove(servicesBindingTag);
mServicesBinding.remove(servicesBindingTag);
try {
mService = asInterface(binder);
@@ -892,6 +900,27 @@
mServicesBinding.remove(servicesBindingTag);
Slog.v(TAG, getCaption() + " connection lost: " + name);
}
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ Slog.w(TAG, getCaption() + " binding died: " + name);
+ synchronized (mMutex) {
+ mServicesBinding.remove(servicesBindingTag);
+ mContext.unbindService(this);
+ if (!mServicesRebinding.contains(servicesBindingTag)) {
+ mServicesRebinding.add(servicesBindingTag);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ registerService(name, userid);
+ }
+ }, ON_BINDING_DIED_REBIND_DELAY_MS);
+ } else {
+ Slog.v(TAG, getCaption() + " not rebinding as "
+ + "a previous rebind attempt was made: " + name);
+ }
+ }
+ }
};
if (!mContext.bindServiceAsUser(intent,
serviceConnection,
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 210eb13..ab3c999 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,7 +16,9 @@
package com.android.server.pm;
+import android.annotation.AppIdInt;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.Build;
@@ -58,6 +60,8 @@
public static final int DEXOPT_STORAGE_DE = 1 << 8;
/** Indicates that dexopt is invoked from the background service. */
public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+ /** Indicates that dexopt should restrict access to private APIs. */
+ public static final int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
@@ -281,42 +285,45 @@
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
- @Nullable String seInfo, boolean downgrade)
- throws InstallerException {
+ @Nullable String seInfo, boolean downgrade, int targetSdkVersion,
+ @Nullable String profileName, @Nullable String dexMetadataPath,
+ @Nullable String compilationReason) throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
- dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
+ dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
+ targetSdkVersion, profileName, dexMetadataPath, compilationReason);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public boolean mergeProfiles(int uid, String packageName) throws InstallerException {
- if (!checkBeforeRemote()) return false;
- try {
- return mInstalld.mergeProfiles(uid, packageName);
- } catch (Exception e) {
- throw InstallerException.from(e);
- }
- }
-
- public boolean dumpProfiles(int uid, String packageName, String codePaths)
+ public boolean mergeProfiles(int uid, String packageName, String profileName)
throws InstallerException {
if (!checkBeforeRemote()) return false;
try {
- return mInstalld.dumpProfiles(uid, packageName, codePaths);
+ return mInstalld.mergeProfiles(uid, packageName, profileName);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public boolean copySystemProfile(String systemProfile, int uid, String packageName)
+ public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath)
throws InstallerException {
if (!checkBeforeRemote()) return false;
try {
- return mInstalld.copySystemProfile(systemProfile, uid, packageName);
+ return mInstalld.dumpProfiles(uid, packageName, profileName, codePath);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ public boolean copySystemProfile(String systemProfile, int uid, String packageName,
+ String profileName) throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+ try {
+ return mInstalld.copySystemProfile(systemProfile, uid, packageName, profileName);
} catch (Exception e) {
throw InstallerException.from(e);
}
@@ -360,10 +367,10 @@
}
}
- public void clearAppProfiles(String packageName) throws InstallerException {
+ public void clearAppProfiles(String packageName, String profileName) throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.clearAppProfiles(packageName);
+ mInstalld.clearAppProfiles(packageName, profileName);
} catch (Exception e) {
throw InstallerException.from(e);
}
@@ -486,6 +493,36 @@
}
}
+ public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
+ @Nullable String volumeUuid, int flags) throws InstallerException {
+ if (!checkBeforeRemote()) return new byte[0];
+ try {
+ return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ public boolean createProfileSnapshot(int appId, String packageName, String profileName,
+ String classpath) throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+ try {
+ return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ public void destroyProfileSnapshot(String packageName, String profileName)
+ throws InstallerException {
+ if (!checkBeforeRemote()) return;
+ try {
+ mInstalld.destroyProfileSnapshot(packageName, profileName);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
public void invalidateMounts() throws InstallerException {
if (!checkBeforeRemote()) return;
try {
@@ -504,6 +541,17 @@
}
}
+ public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
+ String profileName, String codePath, String dexMetadataPath) throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+ try {
+ return mInstalld.prepareAppProfile(pkg, userId, appId, profileName, codePath,
+ dexMetadataPath);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
private static void assertValidInstructionSet(String instructionSet)
throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 6253857..b6804ba 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -260,12 +260,14 @@
public void dexopt(String apkPath, int uid, @Nullable String pkgName,
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
- @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
+ @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
+ int targetSdkVersion, @Nullable String profileName,
+ @Nullable String dexMetadataPath, @Nullable String dexoptCompilationReason)
throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 3.
- builder.append("3 ");
+ // The version. Right now it's 7.
+ builder.append("7 ");
builder.append("dexopt");
@@ -281,6 +283,10 @@
encodeParameter(builder, sharedLibraries);
encodeParameter(builder, seInfo);
encodeParameter(builder, downgrade);
+ encodeParameter(builder, targetSdkVersion);
+ encodeParameter(builder, profileName);
+ encodeParameter(builder, dexMetadataPath);
+ encodeParameter(builder, dexoptCompilationReason);
commands.add(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 300f15f..51e035b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
+import android.content.pm.dex.ArtManager;
+import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -32,7 +34,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
@@ -55,12 +56,14 @@
import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
import static dalvik.system.DexFile.getSafeModeCompilerFilter;
import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
@@ -210,12 +213,21 @@
}
}
+ String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]);
+
+ String dexMetadataPath = null;
+ if (options.isDexoptInstallWithDexMetadata()) {
+ File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
+ dexMetadataPath = dexMetadataFile == null
+ ? null : dexMetadataFile.getAbsolutePath();
+ }
+
final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
|| packageUseInfo.isUsedByOtherApps(path);
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
options.getCompilerFilter(), isUsedByOtherApps);
final boolean profileUpdated = options.isCheckForProfileUpdates() &&
- isProfileUpdated(pkg, sharedGid, compilerFilter);
+ isProfileUpdated(pkg, sharedGid, profileName, compilerFilter);
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
// flags.
@@ -224,7 +236,8 @@
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
- packageStats, options.isDowngrade());
+ packageStats, options.isDowngrade(), profileName, dexMetadataPath,
+ options.getCompilationReason());
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
@@ -248,7 +261,8 @@
@GuardedBy("mInstallLock")
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
String compilerFilter, boolean profileUpdated, String classLoaderContext,
- int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
+ int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
+ String profileName, String dexMetadataPath, int compilationReason) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -274,7 +288,8 @@
// primary dex files.
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
- false /* downgrade*/);
+ false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
+ profileName, dexMetadataPath, getReasonName(compilationReason));
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -385,7 +400,7 @@
// Note this trades correctness for performance since the resulting slow down is
// unacceptable in some cases until b/64530081 is fixed.
String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
-
+ int reason = options.getCompilationReason();
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
@@ -395,7 +410,8 @@
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
- options.isDowngrade());
+ options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
+ /*dexMetadataPath*/ null, getReasonName(reason));
}
return DEX_OPT_PERFORMED;
@@ -505,16 +521,24 @@
private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- // Profile guide compiled oat files should not be public.
+ // Profile guide compiled oat files should not be public unles they are based
+ // on profiles from dex metadata archives.
+ // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
+ // the user does not have an existing profile.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
+ boolean isPublic = !info.isForwardLocked() &&
+ (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
+ // Some apps are executed with restrictions on hidden API usage. If this app is one
+ // of them, pass a flag to dexopt to enable the same restrictions during compilation.
+ int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? 0 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
int dexFlags =
(isPublic ? DEXOPT_PUBLIC : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
| profileFlag
| (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
- | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
+ | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
+ | hiddenApiFlag;
return adjustDexoptFlags(dexFlags);
}
@@ -543,14 +567,15 @@
* current profile and the reference profile will be merged and subsequent calls
* may return a different result.
*/
- private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String compilerFilter) {
+ private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String profileName,
+ String compilerFilter) {
// Check if we are allowed to merge and if the compiler filter is profile guided.
if (!isProfileGuidedCompilerFilter(compilerFilter)) {
return false;
}
// Merge profiles. It returns whether or not there was an updated in the profile info.
try {
- return mInstaller.mergeProfiles(uid, pkg.packageName);
+ return mInstaller.mergeProfiles(uid, pkg.packageName, profileName);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to merge profiles", e);
}
@@ -629,6 +654,9 @@
if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
flagsList.add("idle_background_job");
}
+ if ((flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) == DEXOPT_ENABLE_HIDDEN_API_CHECKS) {
+ flagsList.add("enable_hidden_api_checks");
+ }
return String.join(",", flagsList);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index bf86300..7204aa1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,10 +17,12 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
@@ -94,6 +96,7 @@
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
+import android.content.pm.dex.DexMetadataHelper;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -259,6 +262,7 @@
// entries like "lost+found".
if (file.isDirectory()) return false;
if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+ if (DexMetadataHelper.isDexMetadataFile(file)) return false;
return true;
}
};
@@ -944,6 +948,15 @@
mInstallerPackageName, mInstallerUid, user, mCertificates);
}
+ private static void maybeRenameFile(File from, File to) throws PackageManagerException {
+ if (!from.equals(to)) {
+ if (!from.renameTo(to)) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Could not rename file " + from + " to " + to);
+ }
+ }
+ }
+
/**
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
@@ -988,6 +1001,7 @@
if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
+
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
for (File addedFile : addedFiles) {
@@ -1022,9 +1036,9 @@
// Take this opportunity to enforce uniform naming
final String targetName;
if (apk.splitName == null) {
- targetName = "base.apk";
+ targetName = "base" + APK_FILE_EXTENSION;
} else {
- targetName = "split_" + apk.splitName + ".apk";
+ targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
}
if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -1032,9 +1046,7 @@
}
final File targetFile = new File(mResolvedStageDir, targetName);
- if (!addedFile.equals(targetFile)) {
- addedFile.renameTo(targetFile);
- }
+ maybeRenameFile(addedFile, targetFile);
// Base is coming from session
if (apk.splitName == null) {
@@ -1042,6 +1054,18 @@
}
mResolvedStagedFiles.add(targetFile);
+
+ final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
+ if (dexMetadataFile != null) {
+ if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Invalid filename: " + dexMetadataFile);
+ }
+ final File targetDexMetadataFile = new File(mResolvedStageDir,
+ DexMetadataHelper.buildDexMetadataPathForApk(targetName));
+ mResolvedStagedFiles.add(targetDexMetadataFile);
+ maybeRenameFile(dexMetadataFile, targetDexMetadataFile);
+ }
}
if (removeSplitList.size() > 0) {
@@ -1099,6 +1123,12 @@
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
mResolvedInheritedFiles.add(mResolvedBaseFile);
+ // Inherit the dex metadata if present.
+ final File baseDexMetadataFile =
+ DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
+ if (baseDexMetadataFile != null) {
+ mResolvedInheritedFiles.add(baseDexMetadataFile);
+ }
}
// Inherit splits if not overridden
@@ -1109,6 +1139,12 @@
final boolean splitRemoved = removeSplitList.contains(splitName);
if (!stagedSplits.contains(splitName) && !splitRemoved) {
mResolvedInheritedFiles.add(splitFile);
+ // Inherit the dex metadata if present.
+ final File splitDexMetadataFile =
+ DexMetadataHelper.findDexMetadataForFile(splitFile);
+ if (splitDexMetadataFile != null) {
+ mResolvedInheritedFiles.add(splitDexMetadataFile);
+ }
}
}
}
@@ -1167,7 +1203,7 @@
/**
* Calculate the final install footprint size, combining both staged and
* existing APKs together and including unpacked native code from both.
- */
+ */
private long calculateInstalledSize() throws PackageManagerException {
Preconditions.checkNotNull(mResolvedBaseFile);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f182c05cb..900e188 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -106,8 +106,6 @@
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
-
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -179,6 +177,9 @@
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
+import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.dex.IArtManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -285,13 +286,14 @@
import com.android.server.pm.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
+import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.DexLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.storage.DeviceStorageMonitorInternal;
import dalvik.system.CloseGuard;
-import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -426,6 +428,7 @@
private static final int NFC_UID = Process.NFC_UID;
private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
private static final int SHELL_UID = Process.SHELL_UID;
+ private static final int SE_UID = Process.SE_UID;
// Cap the size of permission trees that 3rd party apps can define
private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
@@ -572,6 +575,7 @@
}
// Compilation reasons.
+ public static final int REASON_UNKNOWN = -1;
public static final int REASON_FIRST_BOOT = 0;
public static final int REASON_BOOT = 1;
public static final int REASON_INSTALL = 2;
@@ -610,7 +614,8 @@
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_NUMBERS,
- Manifest.permission.ANSWER_PHONE_CALLS);
+ Manifest.permission.ANSWER_PHONE_CALLS,
+ Manifest.permission.ACCEPT_HANDOVER);
/**
@@ -958,6 +963,8 @@
final PackageInstallerService mInstallerService;
+ final ArtManagerService mArtManagerService;
+
private final PackageDexOptimizer mPackageDexOptimizer;
// DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
// is used by other apps).
@@ -2436,6 +2443,8 @@
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ mSettings.addSharedUserLPw("android.uid.se", SE_UID,
+ ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -2457,7 +2466,11 @@
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
- mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
+ DexManager.Listener dexManagerListener = DexLogger.getListener(this,
+ installer, mInstallLock);
+ mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
+ dexManagerListener);
+ mArtManagerService = new ArtManagerService(this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -9698,7 +9711,7 @@
final long startTime = System.nanoTime();
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
- getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT),
+ causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
false /* bootComplete */);
final int elapsedTimeSeconds =
@@ -9725,7 +9738,7 @@
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
- final String compilerFilter, boolean bootComplete) {
+ final int compilationReason, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
@@ -9749,7 +9762,8 @@
// PackageDexOptimizer to prevent this happening on first boot. The issue
// is that we don't have a good way to say "do this only once".
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
- pkg.applicationInfo.uid, pkg.packageName)) {
+ pkg.applicationInfo.uid, pkg.packageName,
+ ArtManager.getProfileName(null))) {
Log.e(TAG, "Installer failed to copy system profile!");
} else {
// Disabled as this causes speed-profile compilation during first boot
@@ -9784,7 +9798,8 @@
// issue is that we don't have a good way to say "do this only
// once".
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
- pkg.applicationInfo.uid, pkg.packageName)) {
+ pkg.applicationInfo.uid, pkg.packageName,
+ ArtManager.getProfileName(null))) {
Log.e(TAG, "Failed to copy system profile for stub package!");
} else {
useProfileForDexopt = true;
@@ -9823,13 +9838,11 @@
}
}
- String pkgCompilerFilter = compilerFilter;
+ int pkgCompilationReason = compilationReason;
if (useProfileForDexopt) {
// Use background dexopt mode to try and use the profile. Note that this does not
// guarantee usage of the profile.
- pkgCompilerFilter =
- PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
- PackageManagerService.REASON_BACKGROUND_DEXOPT);
+ pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
// checkProfiles is false to avoid merging profiles during boot which
@@ -9838,9 +9851,13 @@
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
+ if (compilationReason == REASON_FIRST_BOOT) {
+ // TODO: This doesn't cover the upgrade case, we should check for this too.
+ dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
+ }
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
- pkgCompilerFilter,
+ pkgCompilationReason,
dexoptFlags));
switch (primaryDexOptStaus) {
@@ -9940,8 +9957,8 @@
int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
(force ? DexoptOptions.DEXOPT_FORCE : 0) |
(bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
- return performDexOpt(new DexoptOptions(packageName, targetCompilerFilter,
- splitName, flags));
+ return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN,
+ targetCompilerFilter, splitName, flags));
}
/**
@@ -10050,7 +10067,8 @@
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
if (!deps.isEmpty()) {
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
- options.getCompilerFilter(), options.getSplitName(),
+ options.getCompilationReason(), options.getCompilerFilter(),
+ options.getSplitName(),
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
@@ -10209,14 +10227,7 @@
synchronized (mInstallLock) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- try {
- List<String> allCodePaths = pkg.getAllCodePathsExcludingResourceOnly();
- String codePaths = TextUtils.join(";", allCodePaths);
- mInstaller.dumpProfiles(sharedGid, packageName, codePaths);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to dump profiles", e);
- }
+ mArtManagerService.dumpProfiles(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@@ -10292,6 +10303,8 @@
for (int i = 0; i < childCount; i++) {
clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
}
+
+ clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
@@ -10364,18 +10377,10 @@
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
- clearAppProfilesLeafLIF(pkg);
+ mArtManagerService.clearAppProfiles(pkg);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
- clearAppProfilesLeafLIF(pkg.childPackages.get(i));
- }
- }
-
- private void clearAppProfilesLeafLIF(PackageParser.Package pkg) {
- try {
- mInstaller.clearAppProfiles(pkg.packageName);
- } catch (InstallerException e) {
- Slog.w(TAG, String.valueOf(e));
+ mArtManagerService.clearAppProfiles(pkg.childPackages.get(i));
}
}
@@ -17897,7 +17902,6 @@
clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- clearAppProfilesLIF(deletedPackage, UserHandle.USER_ALL);
try {
final PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags,
@@ -18033,7 +18037,6 @@
// Successfully disabled the old package. Now proceed with re-installation
clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- clearAppProfilesLIF(deletedPackage, UserHandle.USER_ALL);
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
@@ -18459,6 +18462,7 @@
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
+ DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
@@ -18833,6 +18837,11 @@
}
}
+ // Prepare the application profiles for the new code paths.
+ // This needs to be done before invoking dexopt so that any install-time profile
+ // can be used for optimizations.
+ mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
+
// Check whether we need to dexopt the app.
//
// NOTE: it is IMPORTANT to call dexopt:
@@ -18867,7 +18876,8 @@
// Also, don't fail application installs if the dexopt step fails.
DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE);
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
@@ -22047,7 +22057,6 @@
}
clearAppDataLIF(newPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
| FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- clearAppProfilesLIF(newPkg, UserHandle.USER_ALL);
mDexManager.notifyPackageUpdated(newPkg.packageName,
newPkg.baseCodePath, newPkg.splitCodePaths);
}
@@ -24207,6 +24216,8 @@
Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
}
}
+ // Prepare the application profiles.
+ mArtManagerService.prepareAppProfiles(pkg, userId);
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
// TODO: mark this structure as dirty so we persist it!
@@ -24903,6 +24914,11 @@
return mInstallerService;
}
+ @Override
+ public IArtManager getArtManager() {
+ return mArtManagerService;
+ }
+
private boolean userNeedsBadging(int userId) {
int index = mUserNeedsBadging.indexOfKey(userId);
if (index < 0) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 19b0d9b..fce8285 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -123,4 +123,14 @@
return value;
}
+
+ public static String getReasonName(int reason) {
+ if (reason == PackageManagerService.REASON_UNKNOWN) {
+ return "unknown";
+ }
+ if (reason < 0 || reason >= REASON_STRINGS.length) {
+ throw new IllegalArgumentException("reason " + reason + " invalid");
+ }
+ return REASON_STRINGS[reason];
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 806abec..3857c96 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -42,6 +42,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.DexMetadataHelper;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
@@ -1480,6 +1481,14 @@
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
+ // Sanity check that all .dm files match an apk.
+ // (The installer does not support standalone .dm files and will not process them.)
+ try {
+ DexMetadataHelper.validateDexPaths(session.getNames());
+ } catch (IllegalStateException | IOException e) {
+ pw.println("Warning [Could not validate the dex paths: " + e.getMessage() + "]");
+ }
+
final LocalIntentReceiver receiver = new LocalIntentReceiver();
session.commit(receiver.getIntentSender());
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index f0ce3c9..1268464 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -60,10 +60,8 @@
// to synchronize access during policy load and access attempts.
private static List<Policy> sPolicies = new ArrayList<>();
- /** Path to MAC permissions on system image */
- private static final File[] MAC_PERMISSIONS =
- { new File(Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"),
- new File(Environment.getVendorDirectory(), "/etc/selinux/nonplat_mac_permissions.xml") };
+ /** Required MAC permissions files */
+ private static List<File> sMacPermissions = new ArrayList<>();
// Append privapp to existing seinfo label
private static final String PRIVILEGED_APP_STR = ":privapp";
@@ -74,13 +72,40 @@
// Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
+ // Only initialize sMacPermissions once.
+ static {
+ // Platform mac permissions.
+ sMacPermissions.add(new File(
+ Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
+
+ // Vendor mac permissions.
+ // The filename has been renamed from nonplat_mac_permissions to
+ // vendor_mac_permissions. Either of them should exist.
+ final File vendorMacPermission = new File(
+ Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml");
+ if (vendorMacPermission.exists()) {
+ sMacPermissions.add(vendorMacPermission);
+ } else {
+ // For backward compatibility.
+ sMacPermissions.add(new File(Environment.getVendorDirectory(),
+ "/etc/selinux/nonplat_mac_permissions.xml"));
+ }
+
+ // ODM mac permissions (optional).
+ final File odmMacPermission = new File(
+ Environment.getOdmDirectory(), "/etc/selinux/odm_mac_permissions.xml");
+ if (odmMacPermission.exists()) {
+ sMacPermissions.add(odmMacPermission);
+ }
+ }
+
/**
* Load the mac_permissions.xml file containing all seinfo assignments used to
- * label apps. The loaded mac_permissions.xml file is determined by the
- * MAC_PERMISSIONS class variable which is set at class load time which itself
- * is based on the USE_OVERRIDE_POLICY class variable. For further guidance on
+ * label apps. The loaded mac_permissions.xml files are plat_mac_permissions.xml and
+ * vendor_mac_permissions.xml, on /system and /vendor partitions, respectively.
+ * odm_mac_permissions.xml on /odm partition is optional. For further guidance on
* the proper structure of a mac_permissions.xml file consult the source code
- * located at system/sepolicy/mac_permissions.xml.
+ * located at system/sepolicy/private/mac_permissions.xml.
*
* @return boolean indicating if policy was correctly loaded. A value of false
* typically indicates a structural problem with the xml or incorrectly
@@ -93,10 +118,13 @@
FileReader policyFile = null;
XmlPullParser parser = Xml.newPullParser();
- for (int i = 0; i < MAC_PERMISSIONS.length; i++) {
+
+ final int count = sMacPermissions.size();
+ for (int i = 0; i < count; ++i) {
+ final File macPermission = sMacPermissions.get(i);
try {
- policyFile = new FileReader(MAC_PERMISSIONS[i]);
- Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS[i]);
+ policyFile = new FileReader(macPermission);
+ Slog.d(TAG, "Using policy file " + macPermission);
parser.setInput(policyFile);
parser.nextTag();
@@ -120,13 +148,13 @@
StringBuilder sb = new StringBuilder("Exception @");
sb.append(parser.getPositionDescription());
sb.append(" while parsing ");
- sb.append(MAC_PERMISSIONS[i]);
+ sb.append(macPermission);
sb.append(":");
sb.append(ex);
Slog.w(TAG, sb.toString());
return false;
} catch (IOException ioe) {
- Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS[i], ioe);
+ Slog.w(TAG, "Exception parsing " + macPermission, ioe);
return false;
} finally {
IoUtils.closeQuietly(policyFile);
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
new file mode 100644
index 0000000..e290272
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.Manifest;
+import android.annotation.UserIdInt;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.dex.ArtManager;
+import android.content.pm.dex.ArtManager.ProfileType;
+import android.content.pm.dex.DexMetadataHelper;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.content.pm.IPackageManager;
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.system.Os;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import libcore.io.IoUtils;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
+/**
+ * A system service that provides access to runtime and compiler artifacts.
+ *
+ * This service is not accessed by users directly, instead one uses an instance of
+ * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
+ * <p/>
+ * {@code context().getPackageManager().getArtManager();}
+ * <p class="note">
+ * Note: Accessing runtime artifacts may require extra permissions. For example querying the
+ * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
+ * which is a system-level permission that will not be granted to normal apps.
+ */
+public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
+ private static final String TAG = "ArtManagerService";
+
+ private static boolean DEBUG = false;
+ private static boolean DEBUG_IGNORE_PERMISSIONS = false;
+
+ // Package name used to create the profile directory layout when
+ // taking a snapshot of the boot image profile.
+ private static final String BOOT_IMAGE_ANDROID_PACKAGE = "android";
+ // Profile name used for the boot image profile.
+ private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof";
+
+ private final IPackageManager mPackageManager;
+ private final Object mInstallLock;
+ @GuardedBy("mInstallLock")
+ private final Installer mInstaller;
+
+ private final Handler mHandler;
+
+ public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
+ mPackageManager = pm;
+ mInstaller = installer;
+ mInstallLock = installLock;
+ mHandler = new Handler(BackgroundThread.getHandler().getLooper());
+ }
+
+ @Override
+ public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
+ @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) {
+ // Sanity checks on the arguments.
+ Preconditions.checkNotNull(callback);
+
+ boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE;
+ if (!bootImageProfile) {
+ Preconditions.checkStringNotEmpty(codePath);
+ Preconditions.checkStringNotEmpty(packageName);
+ }
+
+ // Verify that the caller has the right permissions and that the runtime profiling is
+ // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission.
+ if (!isRuntimeProfilingEnabled(profileType)) {
+ throw new IllegalStateException("Runtime profiling is not enabled for " + profileType);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
+ }
+
+ if (bootImageProfile) {
+ snapshotBootImageProfile(callback);
+ } else {
+ snapshotAppProfile(packageName, codePath, callback);
+ }
+ }
+
+ private void snapshotAppProfile(String packageName, String codePath,
+ ISnapshotRuntimeProfileCallback callback) {
+ PackageInfo info = null;
+ try {
+ // Note that we use the default user 0 to retrieve the package info.
+ // This doesn't really matter because for user 0 we always get a package back (even if
+ // it's not installed for the user 0). It is ok because we only care about the code
+ // paths and not if the package is enabled or not for the user.
+
+ // TODO(calin): consider adding an API to PMS which can retrieve the
+ // PackageParser.Package.
+ info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
+ } catch (RemoteException ignored) {
+ // Should not happen.
+ }
+ if (info == null) {
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
+ return;
+ }
+
+ boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
+ String splitName = null;
+ String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
+ if (!pathFound && (splitCodePaths != null)) {
+ for (int i = splitCodePaths.length - 1; i >= 0; i--) {
+ if (splitCodePaths[i].equals(codePath)) {
+ pathFound = true;
+ splitName = info.applicationInfo.splitNames[i];
+ break;
+ }
+ }
+ }
+ if (!pathFound) {
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
+ return;
+ }
+
+ // All good, create the profile snapshot.
+ int appId = UserHandle.getAppId(info.applicationInfo.uid);
+ if (appId < 0) {
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ Slog.wtf(TAG, "AppId is -1 for package: " + packageName);
+ return;
+ }
+
+ createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath,
+ appId, callback);
+ // Destroy the snapshot, we no longer need it.
+ destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName));
+ }
+
+ private void createProfileSnapshot(String packageName, String profileName, String classpath,
+ int appId, ISnapshotRuntimeProfileCallback callback) {
+ // Ask the installer to snapshot the profile.
+ synchronized (mInstallLock) {
+ try {
+ if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) {
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ return;
+ }
+ } catch (InstallerException e) {
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ return;
+ }
+ }
+
+ // Open the snapshot and invoke the callback.
+ File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName);
+
+ ParcelFileDescriptor fd = null;
+ try {
+ fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
+ postSuccess(packageName, fd, callback);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
+ + snapshotProfile, e);
+ postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ private void destroyProfileSnapshot(String packageName, String profileName) {
+ if (DEBUG) {
+ Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName);
+ }
+
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.destroyProfileSnapshot(packageName, profileName);
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Failed to destroy profile snapshot for " +
+ packageName + ":" + profileName, e);
+ }
+ }
+ }
+
+ @Override
+ public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
+ // Verify that the caller has the right permissions.
+ checkReadRuntimeProfilePermission();
+
+ switch (profileType) {
+ case ArtManager.PROFILE_APPS :
+ return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+ case ArtManager.PROFILE_BOOT_IMAGE:
+ return (Build.IS_USERDEBUG || Build.IS_ENG) &&
+ SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) &&
+ SystemProperties.getBoolean("dalvik.vm.profilebootimage", false);
+ default:
+ throw new IllegalArgumentException("Invalid profile type:" + profileType);
+ }
+ }
+
+ private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) {
+ // Combine the profiles for boot classpath and system server classpath.
+ // This avoids having yet another type of profiles and simplifies the processing.
+ String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"),
+ Os.getenv("SYSTEMSERVERCLASSPATH"));
+
+ // Create the snapshot.
+ createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, classpath,
+ /*appId*/ -1, callback);
+ // Destroy the snapshot, we no longer need it.
+ destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME);
+ }
+
+ /**
+ * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
+ * on the internal {@code mHandler}.
+ */
+ private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
+ int errCode) {
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " +
+ errCode);
+ }
+ mHandler.post(() -> {
+ try {
+ callback.onError(errCode);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
+ }
+ });
+ }
+
+ private void postSuccess(String packageName, ParcelFileDescriptor fd,
+ ISnapshotRuntimeProfileCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "Successfully snapshot profile for " + packageName);
+ }
+ mHandler.post(() -> {
+ try {
+ callback.onSuccess(fd);
+ } catch (RemoteException e) {
+ Slog.w(TAG,
+ "Failed to call onSuccess after profile snapshot for " + packageName, e);
+ }
+ });
+ }
+
+ /**
+ * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
+ * If not, it throws a {@link SecurityException}.
+ */
+ private void checkReadRuntimeProfilePermission() {
+ if (DEBUG_IGNORE_PERMISSIONS) {
+ return;
+ }
+ try {
+ int result = mPackageManager.checkUidPermission(
+ Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need "
+ + Manifest.permission.READ_RUNTIME_PROFILES
+ + " permission to snapshot profiles.");
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ }
+ }
+
+ /**
+ * Prepare the application profiles.
+ * For all code paths:
+ * - create the current primary profile to save time at app startup time.
+ * - copy the profiles from the associated dex metadata file to the reference profile.
+ */
+ public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) {
+ final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+ if (user < 0) {
+ Slog.wtf(TAG, "Invalid user id: " + user);
+ return;
+ }
+ if (appId < 0) {
+ Slog.wtf(TAG, "Invalid app id: " + appId);
+ return;
+ }
+ try {
+ ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg);
+ for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
+ String codePath = codePathsProfileNames.keyAt(i);
+ String profileName = codePathsProfileNames.valueAt(i);
+ File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
+ String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
+ synchronized (mInstaller) {
+ boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
+ profileName, codePath, dexMetadataPath);
+ if (!result) {
+ Slog.e(TAG, "Failed to prepare profile for " +
+ pkg.packageName + ":" + codePath);
+ }
+ }
+ }
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e);
+ }
+ }
+
+ /**
+ * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
+ */
+ public void prepareAppProfiles(PackageParser.Package pkg, int[] user) {
+ for (int i = 0; i < user.length; i++) {
+ prepareAppProfiles(pkg, user[i]);
+ }
+ }
+
+ /**
+ * Clear the profiles for the given package.
+ */
+ public void clearAppProfiles(PackageParser.Package pkg) {
+ try {
+ ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
+ for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
+ String profileName = packageProfileNames.valueAt(i);
+ mInstaller.clearAppProfiles(pkg.packageName, profileName);
+ }
+ } catch (InstallerException e) {
+ Slog.w(TAG, String.valueOf(e));
+ }
+ }
+
+ /**
+ * Dumps the profiles for the given package.
+ */
+ public void dumpProfiles(PackageParser.Package pkg) {
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ try {
+ ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
+ for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
+ String codePath = packageProfileNames.keyAt(i);
+ String profileName = packageProfileNames.valueAt(i);
+ synchronized (mInstallLock) {
+ mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath);
+ }
+ }
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to dump profiles", e);
+ }
+ }
+
+ /**
+ * Build the profiles names for all the package code paths (excluding resource only paths).
+ * Return the map [code path -> profile name].
+ */
+ private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) {
+ ArrayMap<String, String> result = new ArrayMap<>();
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ result.put(pkg.baseCodePath, ArtManager.getProfileName(null));
+ }
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+ if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i]));
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
new file mode 100644
index 0000000..c7bbf1c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+
+import android.util.ArraySet;
+import android.util.ByteStringUtils;
+import android.util.EventLog;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+
+import java.io.File;
+import java.util.Set;
+
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+
+/**
+ * This class is responsible for logging data about secondary dex files.
+ * The data logged includes hashes of the name and content of each file.
+ */
+public class DexLogger implements DexManager.Listener {
+ private static final String TAG = "DexLogger";
+
+ // Event log tag & subtag used for SafetyNet logging of dynamic
+ // code loading (DCL) - see b/63927552.
+ private static final int SNET_TAG = 0x534e4554;
+ private static final String DCL_SUBTAG = "dcl";
+
+ private final IPackageManager mPackageManager;
+ private final Object mInstallLock;
+ @GuardedBy("mInstallLock")
+ private final Installer mInstaller;
+
+ public static DexManager.Listener getListener(IPackageManager pms,
+ Installer installer, Object installLock) {
+ return new DexLogger(pms, installer, installLock);
+ }
+
+ private DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+ mPackageManager = pms;
+ mInstaller = installer;
+ mInstallLock = installLock;
+ }
+
+ /**
+ * Compute and log hashes of the name and content of a secondary dex file.
+ */
+ @Override
+ public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+ String dexPath, int storageFlags) {
+ int ownerUid = appInfo.uid;
+
+ byte[] hash = null;
+ synchronized(mInstallLock) {
+ try {
+ hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
+ ownerUid, appInfo.volumeUuid, storageFlags);
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
+ " : " + e.getMessage());
+ }
+ }
+ if (hash == null) {
+ return;
+ }
+
+ String dexFileName = new File(dexPath).getName();
+ String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
+ // Valid SHA256 will be 256 bits, 32 bytes.
+ if (hash.length == 32) {
+ message = message + ' ' + ByteStringUtils.toHexString(hash);
+ }
+
+ EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message);
+
+ if (dexUseInfo.isUsedByOtherApps()) {
+ Set<String> otherPackages = dexUseInfo.getLoadingPackages();
+ Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
+ for (String otherPackageName : otherPackages) {
+ try {
+ int otherUid = mPackageManager.getPackageUid(
+ otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
+ if (otherUid != -1 && otherUid != ownerUid) {
+ otherUids.add(otherUid);
+ }
+ } catch (RemoteException ignore) {
+ // Can't happen, we're local.
+ }
+ }
+ for (int otherUid : otherUids) {
+ EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6274754..3e63fb4 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -76,6 +76,7 @@
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
+ private final Listener mListener;
// Possible outcomes of a dex search.
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
@@ -96,14 +97,24 @@
*/
private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
+ public interface Listener {
+ /**
+ * Invoked just before the secondary dex file {@code dexPath} for the specified application
+ * is reconciled.
+ */
+ void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+ String dexPath, int storageFlags);
+ }
+
public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
- Installer installer, Object installLock) {
+ Installer installer, Object installLock, Listener listener) {
mPackageCodeLocationsCache = new HashMap<>();
mPackageDexUsage = new PackageDexUsage();
mPackageManager = pms;
mPackageDexOptimizer = pdo;
mInstaller = installer;
mInstallLock = installLock;
+ mListener = listener;
}
/**
@@ -389,7 +400,7 @@
: mPackageDexOptimizer;
String packageName = options.getPackageName();
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
- if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
@@ -433,7 +444,7 @@
*/
public void reconcileSecondaryDexFiles(String packageName) {
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
- if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
@@ -481,12 +492,16 @@
continue;
}
+ if (mListener != null) {
+ mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
+ }
+
boolean dexStillExists = true;
synchronized(mInstallLock) {
try {
String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
- pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
+ info.uid, isas, info.volumeUuid, flags);
} catch (InstallerException e) {
Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
" : " + e.getMessage());
@@ -534,13 +549,12 @@
mPackageDexUsage.maybeWriteAsync();
}
- // Try to optimize the package according to the install reason.
- String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
- PackageManagerService.REASON_INSTALL);
DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName)
.getDexUseInfoMap().get(dexPath);
- DexoptOptions options = new DexoptOptions(info.packageName, compilerFilter, /*flags*/0);
+ // Try to optimize the package according to the install reason.
+ DexoptOptions options = new DexoptOptions(info.packageName,
+ PackageManagerService.REASON_INSTALL, /*flags*/0);
int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo,
options);
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index 0966770..a7a7686 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -59,6 +59,10 @@
// When set, indicates that dexopt is invoked from the background service.
public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+ // When set, indicates that dexopt is invoked from the install time flow and
+ // should get the dex metdata file if present.
+ public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10;
+
// The name of package to optimize.
private final String mPackageName;
@@ -73,15 +77,21 @@
// It only applies for primary apk and it's always null if mOnlySecondaryDex is true.
private final String mSplitName;
+ // The reason for invoking dexopt (see PackageManagerService.REASON_* constants).
+ // A -1 value denotes an unknown reason.
+ private final int mCompilationReason;
+
public DexoptOptions(String packageName, String compilerFilter, int flags) {
- this(packageName, compilerFilter, /*splitName*/ null, flags);
+ this(packageName, /*compilationReason*/ -1, compilerFilter, /*splitName*/ null, flags);
}
- public DexoptOptions(String packageName, int compilerReason, int flags) {
- this(packageName, getCompilerFilterForReason(compilerReason), flags);
+ public DexoptOptions(String packageName, int compilationReason, int flags) {
+ this(packageName, compilationReason, getCompilerFilterForReason(compilationReason),
+ /*splitName*/ null, flags);
}
- public DexoptOptions(String packageName, String compilerFilter, String splitName, int flags) {
+ public DexoptOptions(String packageName, int compilationReason, String compilerFilter,
+ String splitName, int flags) {
int validityMask =
DEXOPT_CHECK_FOR_PROFILES_UPDATES |
DEXOPT_FORCE |
@@ -90,7 +100,8 @@
DEXOPT_ONLY_SHARED_DEX |
DEXOPT_DOWNGRADE |
DEXOPT_AS_SHARED_LIBRARY |
- DEXOPT_IDLE_BACKGROUND_JOB;
+ DEXOPT_IDLE_BACKGROUND_JOB |
+ DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
if ((flags & (~validityMask)) != 0) {
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
}
@@ -99,6 +110,7 @@
mCompilerFilter = compilerFilter;
mFlags = flags;
mSplitName = splitName;
+ mCompilationReason = compilationReason;
}
public String getPackageName() {
@@ -141,6 +153,10 @@
return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
}
+ public boolean isDexoptInstallWithDexMetadata() {
+ return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0;
+ }
+
public String getSplitName() {
return mSplitName;
}
@@ -148,4 +164,8 @@
public int getFlags() {
return mFlags;
}
+
+ public int getCompilationReason() {
+ return mCompilationReason;
+ }
}
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 6c8c9b2..ffc4731 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -5,3 +5,4 @@
per-file DefaultPermissionGrantPolicy.java = svetoslavganov@google.com
per-file DefaultPermissionGrantPolicy.java = toddke@google.com
per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
+per-file DefaultPermissionGrantPolicy.java = patb@google.com
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index da14c360..d219476 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6791,7 +6791,7 @@
mWindowManagerDrawComplete = false;
mScreenOnListener = screenOnListener;
- if (mKeyguardDelegate != null) {
+ if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
getKeyguardDrawnTimeout());
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 5a5471b..3345649 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -235,6 +235,10 @@
return false;
}
+ public boolean hasKeyguard() {
+ return mKeyguardState.deviceHasKeyguard;
+ }
+
public boolean isInputRestricted() {
if (mKeyguardService != null) {
mKeyguardState.inputRestricted = mKeyguardService.isInputRestricted();
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 853e1b2..3aca330 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -24,8 +24,6 @@
import android.app.ProgressDialog;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.IBluetoothManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -34,8 +32,6 @@
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioAttributes;
-import android.nfc.INfcAdapter;
-import android.nfc.NfcAdapter;
import android.os.FileUtils;
import android.os.Handler;
import android.os.PowerManager;
@@ -122,9 +118,7 @@
private static String METRIC_AM = "shutdown_activity_manager";
private static String METRIC_PM = "shutdown_package_manager";
private static String METRIC_RADIOS = "shutdown_radios";
- private static String METRIC_BT = "shutdown_bt";
private static String METRIC_RADIO = "shutdown_radio";
- private static String METRIC_NFC = "shutdown_nfc";
private static String METRIC_SM = "shutdown_storage_manager";
private final Object mActionDoneSync = new Object();
@@ -418,7 +412,7 @@
/**
* Makes sure we handle the shutdown gracefully.
- * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
+ * Shuts off power regardless of radio state if the allotted time has passed.
*/
public void run() {
TimingsTraceLog shutdownTimingLog = newTimingsLog();
@@ -630,42 +624,10 @@
Thread t = new Thread() {
public void run() {
TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
- boolean nfcOff;
- boolean bluetoothReadyForShutdown;
boolean radioOff;
- final INfcAdapter nfc =
- INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
final ITelephony phone =
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- final IBluetoothManager bluetooth =
- IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
- BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
- try {
- nfcOff = nfc == null ||
- nfc.getState() == NfcAdapter.STATE_OFF;
- if (!nfcOff) {
- Log.w(TAG, "Turning off NFC...");
- metricStarted(METRIC_NFC);
- nfc.disable(false); // Don't persist new state
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during NFC shutdown", ex);
- nfcOff = true;
- }
-
- try {
- bluetoothReadyForShutdown = bluetooth == null ||
- bluetooth.getState() == BluetoothAdapter.STATE_OFF;
- if (!bluetoothReadyForShutdown) {
- Log.w(TAG, "Disabling Bluetooth...");
- metricStarted(METRIC_BT);
- bluetooth.disable(mContext.getPackageName(), false); // disable but don't persist new state
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
- bluetoothReadyForShutdown = true;
- }
try {
radioOff = phone == null || !phone.needMobileRadioShutdown();
@@ -679,7 +641,7 @@
radioOff = true;
}
- Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
+ Log.i(TAG, "Waiting for Radio...");
long delay = endTime - SystemClock.elapsedRealtime();
while (delay > 0) {
@@ -690,25 +652,6 @@
sInstance.setRebootProgress(status, null);
}
- if (!bluetoothReadyForShutdown) {
- try {
- // BLE only mode can happen when BT is turned off
- // We will continue shutting down in such case
- bluetoothReadyForShutdown =
- bluetooth.getState() == BluetoothAdapter.STATE_OFF ||
- bluetooth.getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF ||
- bluetooth.getState() == BluetoothAdapter.STATE_BLE_ON;
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
- bluetoothReadyForShutdown = true;
- }
- if (bluetoothReadyForShutdown) {
- Log.i(TAG, "Bluetooth turned off.");
- metricEnded(METRIC_BT);
- shutdownTimingsTraceLog
- .logDuration("ShutdownBt", TRON_METRICS.get(METRIC_BT));
- }
- }
if (!radioOff) {
try {
radioOff = !phone.needMobileRadioShutdown();
@@ -723,23 +666,9 @@
.logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
}
}
- if (!nfcOff) {
- try {
- nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during NFC shutdown", ex);
- nfcOff = true;
- }
- if (nfcOff) {
- Log.i(TAG, "NFC turned off.");
- metricEnded(METRIC_NFC);
- shutdownTimingsTraceLog
- .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC));
- }
- }
- if (radioOff && bluetoothReadyForShutdown && nfcOff) {
- Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
+ if (radioOff) {
+ Log.i(TAG, "Radio shutdown complete.");
done[0] = true;
break;
}
@@ -756,7 +685,7 @@
} catch (InterruptedException ex) {
}
if (!done[0]) {
- Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
+ Log.w(TAG, "Timed out waiting for Radio shutdown.");
}
}
diff --git a/services/core/java/com/android/server/updates/CarrierIdInstallReceiver.java b/services/core/java/com/android/server/updates/CarrierIdInstallReceiver.java
new file mode 100644
index 0000000..0450816
--- /dev/null
+++ b/services/core/java/com/android/server/updates/CarrierIdInstallReceiver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.updates;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.util.Log;
+
+public class CarrierIdInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public CarrierIdInstallReceiver() {
+ super("/data/misc/carrierid", "carrier_list.pb", "metadata/", "version");
+ }
+
+ @Override
+ protected void postInstall(Context context, Intent intent) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.update(Uri.withAppendedPath(Telephony.CarrierIdentification.All.CONTENT_URI,
+ "update_db"), new ContentValues(), null, null);
+ }
+}
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 7b1e12e..35b6ad3 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -59,13 +59,6 @@
int userId, int processId, @NonNull ComponentName calling);
/**
- * Set whether the system has acquired a sleep token.
- *
- * @param isAsleep is {@code true} if the device is asleep, or {@code false} otherwise.
- */
- public abstract void onSleepStateChanged(boolean isAsleep);
-
- /**
* Set whether the display used for VR output is on.
*
* @param isScreenOn is {@code true} if the display is on and can receive commands,
@@ -74,13 +67,6 @@
public abstract void onScreenStateChanged(boolean isScreenOn);
/**
- * Set whether the keyguard is currently active/showing.
- *
- * @param isShowing is {@code true} if the keyguard is active/showing.
- */
- public abstract void onKeyguardStateChanged(boolean isShowing);
-
- /**
* Return NO_ERROR if the given package is installed on the device and enabled as a
* VrListenerService for the given current user, or a negative error code indicating a failure.
*
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b0fd248..56cacf4 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.INotificationManager;
@@ -104,7 +105,8 @@
*
* @hide
*/
-public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
+public class VrManagerService extends SystemService
+ implements EnabledComponentChangeListener, ScreenObserver {
public static final String TAG = "VrManagerService";
static final boolean DBG = false;
@@ -231,15 +233,17 @@
}
}
- private void setSleepState(boolean isAsleep) {
- setSystemState(FLAG_AWAKE, !isAsleep);
- }
-
private void setScreenOn(boolean isScreenOn) {
setSystemState(FLAG_SCREEN_ON, isScreenOn);
}
- private void setKeyguardShowing(boolean isShowing) {
+ @Override
+ public void onAwakeStateChanged(boolean isAwake) {
+ setSystemState(FLAG_AWAKE, isAwake);
+ }
+
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
}
@@ -675,21 +679,11 @@
}
@Override
- public void onSleepStateChanged(boolean isAsleep) {
- VrManagerService.this.setSleepState(isAsleep);
- }
-
- @Override
public void onScreenStateChanged(boolean isScreenOn) {
VrManagerService.this.setScreenOn(isScreenOn);
}
@Override
- public void onKeyguardStateChanged(boolean isShowing) {
- VrManagerService.this.setKeyguardShowing(isShowing);
- }
-
- @Override
public boolean isCurrentVrListener(String packageName, int userId) {
return VrManagerService.this.isCurrentVrListener(packageName, userId);
}
@@ -740,6 +734,9 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ LocalServices.getService(ActivityManagerInternal.class)
+ .registerScreenObserver(this);
+
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b888ec2..f6e863b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -847,6 +847,17 @@
}
}
+ public void scheduleTimeoutLocked() {
+ // If we didn't reset it right away, do so after we couldn't connect to
+ // it for an extended amount of time to avoid having a black wallpaper.
+ final Handler fgHandler = FgThread.getHandler();
+ fgHandler.removeCallbacks(mResetRunnable);
+ fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
+ if (DEBUG_LIVE) {
+ Slog.i(TAG, "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+ }
+ }
+
private void processDisconnect(final ServiceConnection connection) {
synchronized (mLock) {
// The wallpaper disappeared. If this isn't a system-default one, track
@@ -871,13 +882,13 @@
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
- // If we didn't reset it right away, do so after we couldn't connect to
- // it for an extended amount of time to avoid having a black wallpaper.
- final Handler fgHandler = FgThread.getHandler();
- fgHandler.removeCallbacks(mResetRunnable);
- fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
- if (DEBUG_LIVE) {
- Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
+ clearWallpaperComponentLocked(mWallpaper);
+ if (bindWallpaperComponentLocked(
+ wpService, false, false, mWallpaper, null)) {
+ mWallpaper.connection.scheduleTimeoutLocked();
+ } else {
+ Slog.w(TAG, "Reverting to built-in wallpaper!");
+ clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
}
}
final String flattened = wpService.flattenToString();
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index bf769ed..1e334b8 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -304,6 +304,6 @@
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
- | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
- | PackageManager.MATCH_ANY_USER;
+ | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER;
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 04fd3e3..fc2ea60 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -95,6 +95,8 @@
"libhwbinder",
"libutils",
"libhwui",
+ "libbpf",
+ "libnetdutils",
"android.hardware.audio.common@2.0",
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 8de24e5..3302dea 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -29,6 +29,15 @@
#include <utils/misc.h>
#include <utils/Log.h>
+#include "android-base/unique_fd.h"
+#include "bpf/BpfNetworkStats.h"
+#include "bpf/BpfUtils.h"
+
+using android::bpf::Stats;
+using android::bpf::hasBpfSupport;
+using android::bpf::bpfGetUidStats;
+using android::bpf::bpfGetIfaceStats;
+
namespace android {
static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt";
@@ -46,15 +55,6 @@
TCP_TX_PACKETS = 5
};
-struct Stats {
- uint64_t rxBytes;
- uint64_t rxPackets;
- uint64_t txBytes;
- uint64_t txPackets;
- uint64_t tcpRxPackets;
- uint64_t tcpTxPackets;
-};
-
static uint64_t getStatsType(struct Stats* stats, StatsType type) {
switch (type) {
case RX_BYTES:
@@ -150,9 +150,18 @@
return 0;
}
-static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
+static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) {
struct Stats stats;
memset(&stats, 0, sizeof(Stats));
+
+ if (useBpfStats) {
+ if (bpfGetIfaceStats(NULL, &stats) == 0) {
+ return getStatsType(&stats, (StatsType) type);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
if (parseIfaceStats(NULL, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
@@ -160,7 +169,8 @@
}
}
-static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
+static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type,
+ jboolean useBpfStats) {
ScopedUtfChars iface8(env, iface);
if (iface8.c_str() == NULL) {
return UNKNOWN;
@@ -168,6 +178,15 @@
struct Stats stats;
memset(&stats, 0, sizeof(Stats));
+
+ if (useBpfStats) {
+ if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
+ return getStatsType(&stats, (StatsType) type);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
if (parseIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
@@ -175,9 +194,18 @@
}
}
-static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
+static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) {
struct Stats stats;
memset(&stats, 0, sizeof(Stats));
+
+ if (useBpfStats) {
+ if (bpfGetUidStats(uid, &stats) == 0) {
+ return getStatsType(&stats, (StatsType) type);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
if (parseUidStats(uid, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
@@ -186,9 +214,9 @@
}
static const JNINativeMethod gMethods[] = {
- {"nativeGetTotalStat", "(I)J", (void*) getTotalStat},
- {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat},
- {"nativeGetUidStat", "(II)J", (void*) getUidStat},
+ {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat},
+ {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat},
+ {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat},
};
int register_android_server_net_NetworkStatsService(JNIEnv* env) {
diff --git a/services/net/OWNERS b/services/net/OWNERS
index 6f77e04..ce50558 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 31a1abb..7d9736e 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -38,6 +38,7 @@
import android.net.metrics.ApfStats;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
+import android.net.util.InterfaceParams;
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
@@ -56,7 +57,6 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
@@ -247,7 +247,7 @@
private final ApfCapabilities mApfCapabilities;
private final IpClient.Callback mIpClientCallback;
- private final NetworkInterface mNetworkInterface;
+ private final InterfaceParams mInterfaceParams;
private final IpConnectivityLog mMetricsLog;
@VisibleForTesting
@@ -269,11 +269,11 @@
private int mIPv4PrefixLength;
@VisibleForTesting
- ApfFilter(ApfConfiguration config, NetworkInterface networkInterface,
+ ApfFilter(ApfConfiguration config, InterfaceParams ifParams,
IpClient.Callback ipClientCallback, IpConnectivityLog log) {
mApfCapabilities = config.apfCapabilities;
mIpClientCallback = ipClientCallback;
- mNetworkInterface = networkInterface;
+ mInterfaceParams = ifParams;
mMulticastFilter = config.multicastFilter;
mDrop802_3Frames = config.ieee802_3Filter;
@@ -287,7 +287,7 @@
}
private void log(String s) {
- Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s);
+ Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
}
@GuardedBy("this")
@@ -332,14 +332,14 @@
void maybeStartFilter() {
FileDescriptor socket;
try {
- mHardwareAddress = mNetworkInterface.getHardwareAddress();
+ mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
synchronized(this) {
// Install basic filters
installNewProgramLocked();
}
socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
- PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
- mNetworkInterface.getIndex());
+ PacketSocketAddress addr = new PacketSocketAddress(
+ (short) ETH_P_IPV6, mInterfaceParams.index);
Os.bind(socket, addr);
NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
} catch(SocketException|ErrnoException e) {
@@ -1168,10 +1168,10 @@
* filtering using APF programs.
*/
public static ApfFilter maybeCreate(ApfConfiguration config,
- NetworkInterface networkInterface, IpClient.Callback ipClientCallback) {
- if (config == null) return null;
+ InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
+ if (config == null || ifParams == null) return null;
ApfCapabilities apfCapabilities = config.apfCapabilities;
- if (apfCapabilities == null || networkInterface == null) return null;
+ if (apfCapabilities == null) return null;
if (apfCapabilities.apfVersionSupported == 0) return null;
if (apfCapabilities.maximumApfProgramSize < 512) {
Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
@@ -1186,7 +1186,7 @@
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
- return new ApfFilter(config, networkInterface, ipClientCallback, new IpConnectivityLog());
+ return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog());
}
public synchronized void shutdown() {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index ed78175..a956cef 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -34,6 +34,7 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
+import android.net.util.InterfaceParams;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -50,7 +51,6 @@
import java.io.IOException;
import java.lang.Thread;
import java.net.Inet4Address;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.Arrays;
@@ -187,7 +187,8 @@
private final String mIfaceName;
private boolean mRegisteredForPreDhcpNotification;
- private NetworkInterface mIface;
+ private InterfaceParams mIface;
+ // TODO: MacAddress-ify more of this class hierarchy.
private byte[] mHwAddr;
private PacketSocketAddress mInterfaceBroadcastAddr;
private int mTransactionId;
@@ -221,8 +222,9 @@
return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
}
+ // TODO: Take an InterfaceParams instance instead of an interface name String.
private DhcpClient(Context context, StateMachine controller, String iface) {
- super(TAG);
+ super(TAG, controller.getHandler());
mContext = context;
mController = controller;
@@ -262,23 +264,23 @@
}
public static DhcpClient makeDhcpClient(
- Context context, StateMachine controller, String intf) {
- DhcpClient client = new DhcpClient(context, controller, intf);
+ Context context, StateMachine controller, InterfaceParams ifParams) {
+ DhcpClient client = new DhcpClient(context, controller, ifParams.name);
+ client.mIface = ifParams;
client.start();
return client;
}
private boolean initInterface() {
- try {
- mIface = NetworkInterface.getByName(mIfaceName);
- mHwAddr = mIface.getHardwareAddress();
- mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.getIndex(),
- DhcpPacket.ETHER_BROADCAST);
- return true;
- } catch(SocketException | NullPointerException e) {
- Log.e(TAG, "Can't determine ifindex or MAC address for " + mIfaceName, e);
+ if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
+ if (mIface == null) {
+ Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
return false;
}
+
+ mHwAddr = mIface.macAddr.toByteArray();
+ mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
+ return true;
}
private void startNewTransaction() {
@@ -293,7 +295,7 @@
private boolean initPacketSocket() {
try {
mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
- PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.getIndex());
+ PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.index);
Os.bind(mPacketSock, addr);
NetworkUtils.attachDhcpFilter(mPacketSock);
} catch(SocketException|ErrnoException e) {
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 6cf4fa9a..e6ddbbc 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -21,6 +21,7 @@
import android.net.NetworkUtils;
import android.net.util.PacketReader;
import android.net.util.ConnectivityPacketSummary;
+import android.net.util.InterfaceParams;
import android.os.Handler;
import android.system.ErrnoException;
import android.system.Os;
@@ -35,7 +36,6 @@
import java.io.FileDescriptor;
import java.io.InterruptedIOException;
import java.io.IOException;
-import java.net.NetworkInterface;
import java.net.SocketException;
@@ -69,24 +69,12 @@
private boolean mRunning;
private String mDisplayName;
- public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
- final String ifname;
- final int ifindex;
- final byte[] hwaddr;
- final int mtu;
+ public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) {
+ if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
- try {
- ifname = netif.getName();
- ifindex = netif.getIndex();
- hwaddr = netif.getHardwareAddress();
- mtu = netif.getMTU();
- } catch (NullPointerException|SocketException e) {
- throw new IllegalArgumentException("bad network interface", e);
- }
-
- mTag = TAG + "." + ifname;
+ mTag = TAG + "." + ifParams.name;
mLog = log;
- mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
+ mPacketListener = new PacketListener(h, ifParams);
}
public void start(String displayName) {
@@ -102,13 +90,11 @@
}
private final class PacketListener extends PacketReader {
- private final int mIfIndex;
- private final byte mHwAddr[];
+ private final InterfaceParams mInterface;
- PacketListener(Handler h, int ifindex, byte[] hwaddr, int mtu) {
- super(h, mtu);
- mIfIndex = ifindex;
- mHwAddr = hwaddr;
+ PacketListener(Handler h, InterfaceParams ifParams) {
+ super(h, ifParams.defaultMtu);
+ mInterface = ifParams;
}
@Override
@@ -117,7 +103,7 @@
try {
s = Os.socket(AF_PACKET, SOCK_RAW, 0);
NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
- Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex));
+ Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mInterface.index));
} catch (ErrnoException | IOException e) {
logError("Failed to create packet tracking socket: ", e);
closeFd(s);
@@ -129,7 +115,7 @@
@Override
protected void handlePacket(byte[] recvbuf, int length) {
final String summary = ConnectivityPacketSummary.summarize(
- mHwAddr, recvbuf, length);
+ mInterface.macAddr, recvbuf, length);
if (summary == null) return;
if (DBG) Log.d(mTag, summary);
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index fdb366c..1f370a5 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -35,6 +35,7 @@
import android.net.dhcp.DhcpClient;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
+import android.net.util.InterfaceParams;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.net.util.NetworkConstants;
@@ -63,7 +64,6 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collection;
@@ -72,6 +72,7 @@
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
+import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -100,6 +101,11 @@
/**
* Callbacks for handling IpClient events.
+ *
+ * These methods are called by IpClient on its own thread. Implementations
+ * of this class MUST NOT carry out long-running computations or hold locks
+ * for which there might be contention with other code calling public
+ * methods of the same IpClient instance.
*/
public static class Callback {
// In order to receive onPreDhcpAction(), call #withPreDhcpAction()
@@ -545,6 +551,7 @@
private final String mClatInterfaceName;
@VisibleForTesting
protected final Callback mCallback;
+ private final CountDownLatch mShutdownLatch;
private final INetworkManagementService mNwService;
private final NetlinkTracker mNetlinkTracker;
private final WakeupMessage mProvisioningTimeoutAlarm;
@@ -556,7 +563,7 @@
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
private final InterfaceController mInterfaceCtrl;
- private NetworkInterface mNetworkInterface;
+ private InterfaceParams mInterfaceParams;
/**
* Non-final member variables accessed only from within our StateMachine.
@@ -597,6 +604,7 @@
mInterfaceName = ifName;
mClatInterfaceName = CLAT_PREFIX + ifName;
mCallback = new LoggingCallbackWrapper(callback);
+ mShutdownLatch = new CountDownLatch(1);
mNwService = nwService;
mLog = new SharedLog(MAX_LOG_RECORDS, mTag);
@@ -704,6 +712,7 @@
@Override
protected void onQuitting() {
mCallback.onQuit();
+ mShutdownLatch.countDown();
}
// Shut down this IpClient instance altogether.
@@ -712,6 +721,17 @@
sendMessage(CMD_TERMINATE_AFTER_STOP);
}
+ // In order to avoid deadlock, this method MUST NOT be called on the
+ // IpClient instance's thread. This prohibition includes code executed by
+ // when methods on the passed-in IpClient.Callback instance are called.
+ public void awaitShutdown() {
+ try {
+ mShutdownLatch.await();
+ } catch (InterruptedException e) {
+ mLog.e("Interrupted while awaiting shutdown: " + e);
+ }
+ }
+
public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
return new ProvisioningConfiguration.Builder();
}
@@ -722,7 +742,12 @@
return;
}
- getNetworkInterface();
+ mInterfaceParams = InterfaceParams.getByName(mInterfaceName);
+ if (mInterfaceParams == null) {
+ logError("Failed to find InterfaceParams for " + mInterfaceName);
+ // TODO: call doImmediateProvisioningFailure() with an error code
+ // indicating something like "interface not ready".
+ }
mCallback.setNeighborDiscoveryOffload(true);
sendMessage(CMD_START, new ProvisioningConfiguration(req));
@@ -858,7 +883,7 @@
protected String getLogRecString(Message msg) {
final String logLine = String.format(
"%s/%d %d %d %s [%s]",
- mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
+ mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
@@ -889,15 +914,6 @@
mLog.log(msg);
}
- private void getNetworkInterface() {
- try {
- mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
- } catch (SocketException | NullPointerException e) {
- // TODO: throw new IllegalStateException.
- logError("Failed to get interface object: %s", e);
- }
- }
-
// This needs to be called with care to ensure that our LinkProperties
// are in sync with the actual LinkProperties of the interface. For example,
// we should only call this if we know for sure that there are no IP addresses
@@ -1218,7 +1234,7 @@
}
} else {
// Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceName);
+ mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
mDhcpClient.registerForPreDhcpNotification();
mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
}
@@ -1245,7 +1261,7 @@
try {
mIpReachabilityMonitor = new IpReachabilityMonitor(
mContext,
- mInterfaceName,
+ mInterfaceParams,
getHandler(),
mLog,
new IpReachabilityMonitor.Callback() {
@@ -1447,7 +1463,7 @@
mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
apfConfig.ethTypeBlackList =
mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
- mApfFilter = ApfFilter.maybeCreate(apfConfig, mNetworkInterface, mCallback);
+ mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
@@ -1515,7 +1531,7 @@
private ConnectivityPacketTracker createPacketTracker() {
try {
return new ConnectivityPacketTracker(
- getHandler(), mNetworkInterface, mConnectivityPacketLog);
+ getHandler(), mInterfaceParams, mConnectivityPacketLog);
} catch (IllegalArgumentException e) {
return null;
}
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
index 6807334..fc07aa1 100644
--- a/services/net/java/android/net/ip/IpNeighborMonitor.java
+++ b/services/net/java/android/net/ip/IpNeighborMonitor.java
@@ -16,7 +16,11 @@
package android.net.ip;
-import android.net.netlink.NetlinkConstants;
+import static android.net.netlink.NetlinkConstants.hexify;
+import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
+
+import android.net.MacAddress;
import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.NetlinkMessage;
import android.net.netlink.NetlinkSocket;
@@ -92,37 +96,35 @@
final int ifindex;
final InetAddress ip;
final short nudState;
- final byte[] linkLayerAddr;
+ final MacAddress macAddr;
public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
- short nudState, byte[] linkLayerAddr) {
+ short nudState, MacAddress macAddr) {
this.elapsedMs = elapsedMs;
this.msgType = msgType;
this.ifindex = ifindex;
this.ip = ip;
this.nudState = nudState;
- this.linkLayerAddr = linkLayerAddr;
+ this.macAddr = macAddr;
}
boolean isConnected() {
- return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
- StructNdMsg.isNudStateConnected(nudState);
+ return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
}
boolean isValid() {
- return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
- StructNdMsg.isNudStateValid(nudState);
+ return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
}
@Override
public String toString() {
final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
return j.add("@" + elapsedMs)
- .add(NetlinkConstants.stringForNlMsgType(msgType))
+ .add(stringForNlMsgType(msgType))
.add("if=" + ifindex)
.add(ip.getHostAddress())
.add(StructNdMsg.stringForNudState(nudState))
- .add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]")
+ .add("[" + macAddr + "]")
.toString();
}
}
@@ -183,7 +185,7 @@
final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
if (nlMsg == null || nlMsg.getHeader() == null) {
byteBuffer.position(position);
- mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
+ mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
break;
}
@@ -217,12 +219,13 @@
final int ifindex = ndMsg.ndm_ifindex;
final InetAddress destination = neighMsg.getDestination();
final short nudState =
- (msgType == NetlinkConstants.RTM_DELNEIGH)
+ (msgType == RTM_DELNEIGH)
? StructNdMsg.NUD_NONE
: ndMsg.ndm_state;
final NeighborEvent event = new NeighborEvent(
- whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress());
+ whenMs, msgType, ifindex, destination, nudState,
+ getMacAddress(neighMsg.getLinkLayerAddress()));
if (VDBG) {
Log.d(TAG, neighMsg.toString());
@@ -233,4 +236,16 @@
mConsumer.accept(event);
}
+
+ private static MacAddress getMacAddress(byte[] linkLayerAddress) {
+ if (linkLayerAddress != null) {
+ try {
+ return MacAddress.fromBytes(linkLayerAddress);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
+ }
+ }
+
+ return null;
+ }
}
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index b31ffbb..7e02a28 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -26,6 +26,7 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpReachabilityEvent;
import android.net.netlink.StructNdMsg;
+import android.net.util.InterfaceParams;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -46,9 +47,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
import java.net.SocketAddress;
-import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -168,8 +167,7 @@
}
}
- private final String mInterfaceName;
- private final int mInterfaceIndex;
+ private final InterfaceParams mInterfaceParams;
private final IpNeighborMonitor mIpNeighborMonitor;
private final SharedLog mLog;
private final Callback mCallback;
@@ -182,30 +180,25 @@
private volatile long mLastProbeTimeMs;
public IpReachabilityMonitor(
- Context context, String ifName, Handler h, SharedLog log, Callback callback) {
- this(context, ifName, h, log, callback, null);
- }
-
- public IpReachabilityMonitor(
- Context context, String ifName, Handler h, SharedLog log, Callback callback,
+ Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
MultinetworkPolicyTracker tracker) {
- this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker,
- Dependencies.makeDefault(context, ifName));
+ this(ifParams, h, log, callback, tracker, Dependencies.makeDefault(context, ifParams.name));
}
@VisibleForTesting
- IpReachabilityMonitor(String ifName, int ifIndex, Handler h, SharedLog log, Callback callback,
+ IpReachabilityMonitor(InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
MultinetworkPolicyTracker tracker, Dependencies dependencies) {
- mInterfaceName = ifName;
+ if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
+
+ mInterfaceParams = ifParams;
mLog = log.forSubComponent(TAG);
mCallback = callback;
mMultinetworkPolicyTracker = tracker;
- mInterfaceIndex = ifIndex;
mDependencies = dependencies;
mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
(NeighborEvent event) -> {
- if (mInterfaceIndex != event.ifindex) return;
+ if (mInterfaceParams.index != event.ifindex) return;
if (!mNeighborWatchList.containsKey(event.ip)) return;
final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
@@ -241,7 +234,7 @@
private String describeWatchList(String sep) {
final StringBuilder sb = new StringBuilder();
- sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}," + sep);
+ sb.append("iface{" + mInterfaceParams + "}," + sep);
sb.append("ntable=[" + sep);
String delimiter = "";
for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
@@ -262,10 +255,10 @@
}
public void updateLinkProperties(LinkProperties lp) {
- if (!mInterfaceName.equals(lp.getInterfaceName())) {
+ if (!mInterfaceParams.name.equals(lp.getInterfaceName())) {
// TODO: figure out whether / how to cope with interface changes.
Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() +
- "' does not match: " + mInterfaceName);
+ "' does not match: " + mInterfaceParams.name);
return;
}
@@ -353,10 +346,10 @@
mDependencies.acquireWakeLock(getProbeWakeLockDuration());
}
- for (InetAddress target : ipProbeList) {
- final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target);
+ for (InetAddress ip : ipProbeList) {
+ final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip);
mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
- target.getHostAddress(), rval));
+ ip.getHostAddress(), rval));
logEvent(IpReachabilityEvent.PROBE, rval);
}
mLastProbeTimeMs = SystemClock.elapsedRealtime();
@@ -378,22 +371,9 @@
return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
}
- private static int getInterfaceIndex(String ifname) {
- final NetworkInterface iface;
- try {
- iface = NetworkInterface.getByName(ifname);
- } catch (SocketException e) {
- throw new IllegalArgumentException("invalid interface '" + ifname + "': ", e);
- }
- if (iface == null) {
- throw new IllegalArgumentException("NetworkInterface was null for " + ifname);
- }
- return iface.getIndex();
- }
-
private void logEvent(int probeType, int errorCode) {
int eventType = probeType | (errorCode & 0xff);
- mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
+ mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
}
private void logNudFailed(ProvisioningChange delta) {
@@ -401,6 +381,6 @@
boolean isFromProbe = (duration < getProbeWakeLockDuration());
boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
- mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
+ mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
}
}
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index cb3123c..49a1e79 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -25,6 +25,7 @@
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.TrafficStats;
+import android.net.util.InterfaceParams;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructGroupReq;
@@ -96,9 +97,7 @@
(byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
};
- private final String mIfName;
- private final int mIfIndex;
- private final byte[] mHwAddr;
+ private final InterfaceParams mInterface;
private final InetSocketAddress mAllNodes;
// This lock is to protect the RA from being updated while being
@@ -223,11 +222,9 @@
}
- public RouterAdvertisementDaemon(String ifname, int ifindex, byte[] hwaddr) {
- mIfName = ifname;
- mIfIndex = ifindex;
- mHwAddr = hwaddr;
- mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
+ public RouterAdvertisementDaemon(InterfaceParams ifParams) {
+ mInterface = ifParams;
+ mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
mDeprecatedInfoTracker = new DeprecatedInfoTracker();
}
@@ -279,7 +276,7 @@
try {
putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
- putSlla(ra, mHwAddr);
+ putSlla(ra, mInterface.macAddr.toByteArray());
mRaLength = ra.position();
// https://tools.ietf.org/html/rfc5175#section-4 says:
@@ -579,9 +576,9 @@
// Setting SNDTIMEO is purely for defensive purposes.
Os.setsockoptTimeval(
mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
- Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
+ Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
NetworkUtils.protectFromVpn(mSocket);
- NetworkUtils.setupRaSocket(mSocket, mIfIndex);
+ NetworkUtils.setupRaSocket(mSocket, mInterface.index);
} catch (ErrnoException | IOException e) {
Log.e(TAG, "Failed to create RA daemon socket: " + e);
return false;
@@ -614,7 +611,7 @@
final InetAddress destip = dest.getAddress();
return (destip instanceof Inet6Address) &&
destip.isLinkLocalAddress() &&
- (((Inet6Address) destip).getScopeId() == mIfIndex);
+ (((Inet6Address) destip).getScopeId() == mInterface.index);
}
private void maybeSendRA(InetSocketAddress dest) {
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
index dae93af..4951400 100644
--- a/services/net/java/android/net/util/ConnectivityPacketSummary.java
+++ b/services/net/java/android/net/util/ConnectivityPacketSummary.java
@@ -17,6 +17,7 @@
package android.net.util;
import android.net.dhcp.DhcpPacket;
+import android.net.MacAddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -45,21 +46,20 @@
private final ByteBuffer mPacket;
private final String mSummary;
- public static String summarize(byte[] hwaddr, byte[] buffer) {
+ public static String summarize(MacAddress hwaddr, byte[] buffer) {
return summarize(hwaddr, buffer, buffer.length);
}
// Methods called herein perform some but by no means all error checking.
// They may throw runtime exceptions on malformed packets.
- public static String summarize(byte[] hwaddr, byte[] buffer, int length) {
- if ((hwaddr == null) || (hwaddr.length != ETHER_ADDR_LEN)) return null;
- if (buffer == null) return null;
+ public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
+ if ((macAddr == null) || (buffer == null)) return null;
length = Math.min(length, buffer.length);
- return (new ConnectivityPacketSummary(hwaddr, buffer, length)).toString();
+ return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString();
}
- private ConnectivityPacketSummary(byte[] hwaddr, byte[] buffer, int length) {
- mHwAddr = hwaddr;
+ private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) {
+ mHwAddr = macAddr.toByteArray();
mBytes = buffer;
mLength = Math.min(length, mBytes.length);
mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
diff --git a/services/net/java/android/net/util/InterfaceParams.java b/services/net/java/android/net/util/InterfaceParams.java
new file mode 100644
index 0000000..a4b2fbb
--- /dev/null
+++ b/services/net/java/android/net/util/InterfaceParams.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static android.net.MacAddress.ALL_ZEROS_ADDRESS;
+import static android.net.util.NetworkConstants.ETHER_MTU;
+import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.net.MacAddress;
+import android.text.TextUtils;
+
+import java.net.NetworkInterface;
+import java.net.SocketException;
+
+
+/**
+ * Encapsulate the interface parameters common to IpClient/IpServer components.
+ *
+ * Basically all java.net.NetworkInterface methods throw Exceptions. IpClient
+ * and IpServer (sub)components need most or all of this information at some
+ * point during their lifecycles, so pass only this simplified object around
+ * which can be created once when IpClient/IpServer are told to start.
+ *
+ * @hide
+ */
+public class InterfaceParams {
+ public final String name;
+ public final int index;
+ public final MacAddress macAddr;
+ public final int defaultMtu;
+
+ public static InterfaceParams getByName(String name) {
+ final NetworkInterface netif = getNetworkInterfaceByName(name);
+ if (netif == null) return null;
+
+ // Not all interfaces have MAC addresses, e.g. rmnet_data0.
+ final MacAddress macAddr = getMacAddress(netif);
+
+ try {
+ return new InterfaceParams(name, netif.getIndex(), macAddr, netif.getMTU());
+ } catch (IllegalArgumentException|SocketException e) {
+ return null;
+ }
+ }
+
+ public InterfaceParams(String name, int index, MacAddress macAddr) {
+ this(name, index, macAddr, ETHER_MTU);
+ }
+
+ public InterfaceParams(String name, int index, MacAddress macAddr, int defaultMtu) {
+ checkArgument((!TextUtils.isEmpty(name)), "impossible interface name");
+ checkArgument((index > 0), "invalid interface index");
+ this.name = name;
+ this.index = index;
+ this.macAddr = (macAddr != null) ? macAddr : ALL_ZEROS_ADDRESS;
+ this.defaultMtu = (defaultMtu > IPV6_MIN_MTU) ? defaultMtu : IPV6_MIN_MTU;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s/%d/%s/%d", name, index, macAddr, defaultMtu);
+ }
+
+ private static NetworkInterface getNetworkInterfaceByName(String name) {
+ try {
+ return NetworkInterface.getByName(name);
+ } catch (NullPointerException|SocketException e) {
+ return null;
+ }
+ }
+
+ private static MacAddress getMacAddress(NetworkInterface netif) {
+ try {
+ return MacAddress.fromBytes(netif.getHardwareAddress());
+ } catch (IllegalArgumentException|NullPointerException|SocketException e) {
+ return null;
+ }
+ }
+}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 5a3a8be..984c9f8 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -121,6 +121,14 @@
public static final int ICMP_ECHO_DATA_OFFSET = 8;
/**
+ * ICMPv4 constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc792
+ */
+ public static final int ICMPV4_ECHO_REQUEST_TYPE = 8;
+
+ /**
* ICMPv6 constants.
*
* See also:
@@ -139,6 +147,8 @@
public static final int ICMPV6_ND_OPTION_TLLA = 2;
public static final int ICMPV6_ND_OPTION_MTU = 5;
+ public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
+
/**
* UDP constants.
*
@@ -157,6 +167,14 @@
public static final int DHCP4_CLIENT_PORT = 68;
/**
+ * DNS constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc1035
+ */
+ public static final int DNS_SERVER_PORT = 53;
+
+ /**
* Utility functions.
*/
public static byte asByte(int i) { return (byte) i; }
diff --git a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
new file mode 100644
index 0000000..6e76b67
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link WatchdogDiagnostics}
+ */
+@RunWith(AndroidJUnit4.class)
+public class WatchdogDiagnosticsTest {
+
+ private static class TestThread1 extends Thread {
+ Object lock1;
+ Object lock2;
+ volatile boolean inB = false;
+
+ public TestThread1(Object lock1, Object lock2) {
+ super("TestThread1");
+ this.lock1 = lock1;
+ this.lock2 = lock2;
+ }
+
+ @Override
+ public void run() {
+ a();
+ }
+
+ private void a() {
+ synchronized(lock1) {
+ b();
+ }
+ }
+
+ private void b() {
+ inB = true;
+ synchronized(lock2) {
+ // Nothing.
+ }
+ }
+ }
+
+ private static class TestThread2 extends Thread {
+ Object lock1;
+ Object lock2;
+ volatile boolean inY = false;
+
+ public TestThread2(Object lock1, Object lock2) {
+ super("TestThread2");
+ this.lock1 = lock1;
+ this.lock2 = lock2;
+ }
+
+ @Override
+ public void run() {
+ x();
+ }
+
+ private void x() {
+ synchronized(lock1) {
+ y();
+ }
+ }
+
+ private void y() {
+ synchronized(lock2) {
+ inY = true;
+ try {
+ lock2.wait();
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void printAnnotatedStack() throws Exception {
+ // Preparation.
+
+ Object heldLock1 = new Object();
+ Object heldLock2 = 0;
+ Object waitLock = "123";
+
+ TestThread1 thread1 = new TestThread1(heldLock1, heldLock2);
+ TestThread2 thread2 = new TestThread2(heldLock2, waitLock);
+
+ // Start the second thread, ensure it grabs heldLock2.
+ thread2.start();
+ while(!thread2.inY) {
+ Thread.yield();
+ }
+
+ // Start the first thread, ensure it made progress.
+ thread1.start();
+ while(!thread1.inB) {
+ Thread.yield();
+ }
+
+ // Now wait till both are no longer in runnable state.
+ while (thread1.getState() == Thread.State.RUNNABLE) {
+ Thread.yield();
+ }
+ while (thread2.getState() == Thread.State.RUNNABLE) {
+ Thread.yield();
+ }
+
+ // Now do the test.
+ StringWriter stringBuffer = new StringWriter();
+ PrintWriter print = new PrintWriter(stringBuffer, true);
+
+ {
+ WatchdogDiagnostics.printAnnotatedStack(thread1, print);
+
+ String output = stringBuffer.toString();
+ String expected =
+ "TestThread1 annotated stack trace:\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread1.b(" +
+ "WatchdogDiagnosticsTest.java:59)\n" +
+ " - waiting to lock <HASH> (a java.lang.Integer)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread1.a(" +
+ "WatchdogDiagnosticsTest.java:53)\n" +
+ " - locked <HASH> (a java.lang.Object)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread1.run(" +
+ "WatchdogDiagnosticsTest.java:48)\n";
+ assertEquals(expected, filterHashes(output));
+ }
+
+ stringBuffer.getBuffer().setLength(0);
+
+ {
+ WatchdogDiagnostics.printAnnotatedStack(thread2, print);
+
+ String output = stringBuffer.toString();
+ String expected =
+ "TestThread2 annotated stack trace:\n" +
+ " at java.lang.Object.wait(Native Method)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread2.y(" +
+ "WatchdogDiagnosticsTest.java:91)\n" +
+ " - locked <HASH> (a java.lang.String)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread2.x(" +
+ "WatchdogDiagnosticsTest.java:83)\n" +
+ " - locked <HASH> (a java.lang.Integer)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread2.run(" +
+ "WatchdogDiagnosticsTest.java:78)\n";
+ assertEquals(expected, filterHashes(output));
+ }
+
+ // Let the threads finish.
+ synchronized (waitLock) {
+ waitLock.notifyAll();
+ }
+
+ thread1.join();
+ thread2.join();
+ }
+
+ /**
+ * A filter function that removes hash codes (which will change between tests and cannot be
+ * controlled.)
+ * <p>
+ * Note: leaves "<HASH>" to indicate that something was replaced.
+ */
+ private static String filterHashes(String t) {
+ return t.replaceAll("<0x[0-9a-f]{8}>", "<HASH>");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/IpConfigStoreTest.java b/services/tests/servicestests/src/com/android/server/net/IpConfigStoreTest.java
new file mode 100644
index 0000000..9f4b754
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/IpConfigStoreTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.net.IpConfiguration;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.net.ProxyInfo;
+import android.net.StaticIpConfiguration;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Unit tests for {@link IpConfigStore}
+ */
+@RunWith(AndroidJUnit4.class)
+public class IpConfigStoreTest {
+
+ @Test
+ public void backwardCompatibility2to3() throws IOException {
+ final int KEY_CONFIG = 17;
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ DataOutputStream outputStream = new DataOutputStream(byteStream);
+
+ IpConfiguration expectedConfig = new IpConfiguration(IpAssignment.DHCP,
+ ProxySettings.NONE, null, null);
+
+ // Emulate writing to old format.
+ writeDhcpConfigV2(outputStream, KEY_CONFIG, expectedConfig);
+
+ InputStream in = new ByteArrayInputStream(byteStream.toByteArray());
+ ArrayMap<String, IpConfiguration> configurations = IpConfigStore.readIpConfigurations(in);
+
+ assertNotNull(configurations);
+ assertEquals(1, configurations.size());
+ IpConfiguration actualConfig = configurations.get(String.valueOf(KEY_CONFIG));
+ assertNotNull(actualConfig);
+ assertEquals(expectedConfig, actualConfig);
+ }
+
+ @Test
+ public void staticIpMultiNetworks() throws Exception {
+ final String IFACE_1 = "eth0";
+ final String IFACE_2 = "eth1";
+ final String IP_ADDR_1 = "192.168.1.10/24";
+ final String IP_ADDR_2 = "192.168.1.20/24";
+ final String DNS_IP_ADDR_1 = "1.2.3.4";
+ final String DNS_IP_ADDR_2 = "5.6.7.8";
+
+ StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
+ staticIpConfiguration.ipAddress = new LinkAddress(IP_ADDR_1);
+ staticIpConfiguration.dnsServers.add(NetworkUtils.numericToInetAddress(DNS_IP_ADDR_1));
+ staticIpConfiguration.dnsServers.add(NetworkUtils.numericToInetAddress(DNS_IP_ADDR_2));
+
+ ProxyInfo proxyInfo = new ProxyInfo("10.10.10.10", 88, "host1,host2");
+
+ IpConfiguration expectedConfig1 = new IpConfiguration(IpAssignment.STATIC,
+ ProxySettings.STATIC, staticIpConfiguration, proxyInfo);
+ IpConfiguration expectedConfig2 = new IpConfiguration(expectedConfig1);
+ expectedConfig2.getStaticIpConfiguration().ipAddress = new LinkAddress(IP_ADDR_2);
+
+ ArrayMap<String, IpConfiguration> expectedNetworks = new ArrayMap<>();
+ expectedNetworks.put(IFACE_1, expectedConfig1);
+ expectedNetworks.put(IFACE_2, expectedConfig2);
+
+ MockedDelayedDiskWrite writer = new MockedDelayedDiskWrite();
+ IpConfigStore store = new IpConfigStore(writer);
+ store.writeIpConfigurations("file/path/not/used/", expectedNetworks);
+
+ InputStream in = new ByteArrayInputStream(writer.byteStream.toByteArray());
+ ArrayMap<String, IpConfiguration> actualNetworks = IpConfigStore.readIpConfigurations(in);
+ assertNotNull(actualNetworks);
+ assertEquals(2, actualNetworks.size());
+ assertEquals(expectedNetworks.get(IFACE_1), actualNetworks.get(IFACE_1));
+ assertEquals(expectedNetworks.get(IFACE_2), actualNetworks.get(IFACE_2));
+ }
+
+ // This is simplified snapshot of code that was used to store values in V2 format (key as int).
+ private static void writeDhcpConfigV2(DataOutputStream out, int configKey,
+ IpConfiguration config) throws IOException {
+ out.writeInt(2); // VERSION 2
+ switch (config.ipAssignment) {
+ case DHCP:
+ out.writeUTF("ipAssignment");
+ out.writeUTF(config.ipAssignment.toString());
+ break;
+ default:
+ fail("Not supported in test environment");
+ }
+
+ out.writeUTF("id");
+ out.writeInt(configKey);
+ out.writeUTF("eos");
+ }
+
+ /** Synchronously writes into given byte steam */
+ private static class MockedDelayedDiskWrite extends DelayedDiskWrite {
+ final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+
+ @Override
+ public void write(String filePath, Writer w) {
+ DataOutputStream outputStream = new DataOutputStream(byteStream);
+
+ try {
+ w.onWriteCalled(outputStream);
+ } catch (IOException e) {
+ fail();
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 4db9a30..36d0c8b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -17,12 +17,15 @@
package com.android.server.pm.dex;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.pm.Installer;
+
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
@@ -36,8 +39,13 @@
import java.util.Map;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,6 +53,12 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
@@ -56,6 +70,12 @@
private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
DelegateLastClassLoader.class.getName();
+ @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+ @Mock Installer mInstaller;
+ @Mock IPackageManager mPM;
+ private final Object mInstallLock = new Object();
+ @Mock DexManager.Listener mListener;
+
private DexManager mDexManager;
private TestData mFooUser0;
@@ -90,7 +110,8 @@
mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
DELEGATE_LAST_CLASS_LOADER_NAME);
- mDexManager = new DexManager(null, null, null, null);
+ mDexManager = new DexManager(
+ mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener);
// Foo and Bar are available to user0.
// Only Bar is available to user1;
@@ -440,6 +461,20 @@
}
+ @Test
+ public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
+ notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+ when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
+ .thenReturn(mFooUser0.mPackageInfo);
+
+ mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
+
+ verify(mListener, times(fooSecondaries.size()))
+ .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
+ any(DexUseInfo.class), anyString(), anyInt());
+ }
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
@@ -492,12 +527,12 @@
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
- assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
- return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+ assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
+ return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
}
private void assertNoUseInfo(TestData testData) {
- assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+ assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
}
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -555,8 +590,8 @@
List<String> getSecondaryDexPathsFromProtectedDirs() {
List<String> paths = new ArrayList<>();
- paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
- paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
+ paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
+ paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
return paths;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index c2072df..93064bc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -53,6 +53,7 @@
assertFalse(opt.isDowngrade());
assertFalse(opt.isForce());
assertFalse(opt.isDexoptIdleBackgroundJob());
+ assertFalse(opt.isDexoptInstallWithDexMetadata());
}
@Test
@@ -65,7 +66,8 @@
DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
DexoptOptions.DEXOPT_DOWNGRADE |
DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
- DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
+ DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB |
+ DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
assertEquals(mPackageName, opt.getPackageName());
@@ -79,6 +81,7 @@
assertTrue(opt.isForce());
assertTrue(opt.isDexoptAsSharedLibrary());
assertTrue(opt.isDexoptIdleBackgroundJob());
+ assertTrue(opt.isDexoptInstallWithDexMetadata());
}
@Test
@@ -115,7 +118,7 @@
public void testCreateDexoptOptionsSplit() {
int flags = DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE;
- DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, mSplitName, flags);
+ DexoptOptions opt = new DexoptOptions(mPackageName, -1, mCompilerFilter, mSplitName, flags);
assertEquals(mPackageName, opt.getPackageName());
assertEquals(mCompilerFilter, opt.getCompilerFilter());
assertEquals(mSplitName, opt.getSplitName());
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 0cd9ac3..feb7b76 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -10,5 +10,6 @@
static_libs: [
"android.hardware.usb-V1.0-java",
"android.hardware.usb-V1.1-java",
+ "android.hardware.usb.gadget-V1.0-java",
],
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c668100..e3e5e3e 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -16,6 +16,9 @@
package com.android.server.usb;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -26,6 +29,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -37,12 +41,21 @@
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
+import android.hardware.usb.gadget.V1_0.IUsbGadget;
+import android.hardware.usb.gadget.V1_0.IUsbGadgetCallback;
+import android.hardware.usb.gadget.V1_0.Status;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
import android.os.BatteryManager;
+import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.HwBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UEventObserver;
@@ -60,6 +73,7 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
+import com.android.server.LocalServices;
import java.io.File;
import java.io.FileNotFoundException;
@@ -69,32 +83,24 @@
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Set;
+import java.util.StringJoiner;
/**
* UsbDeviceManager manages USB state in device mode.
*/
-public class UsbDeviceManager {
+public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver {
private static final String TAG = "UsbDeviceManager";
private static final boolean DEBUG = false;
/**
- * The persistent property which stores whether adb is enabled or not.
- * May also contain vendor-specific default functions for testing purposes.
+ * The SharedPreference setting per user that stores the screen unlocked functions between
+ * sessions.
*/
- private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
-
- /**
- * The non-persistent property which stores the current USB settings.
- */
- private static final String USB_CONFIG_PROPERTY = "sys.usb.config";
-
- /**
- * The non-persistent property which stores the current USB actual state.
- */
- private static final String USB_STATE_PROPERTY = "sys.usb.state";
+ private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
/**
* ro.bootmode value when phone boots into usual Android.
@@ -128,6 +134,12 @@
private static final int MSG_UPDATE_CHARGING_STATE = 9;
private static final int MSG_UPDATE_HOST_STATE = 10;
private static final int MSG_LOCALE_CHANGED = 11;
+ private static final int MSG_SET_SCREEN_UNLOCKED_FUNCTIONS = 12;
+ private static final int MSG_UPDATE_SCREEN_LOCK = 13;
+ private static final int MSG_SET_CHARGING_FUNCTIONS = 14;
+ private static final int MSG_SET_FUNCTIONS_TIMEOUT = 15;
+ private static final int MSG_GET_CURRENT_USB_FUNCTIONS = 16;
+ private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -143,9 +155,9 @@
private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
-
private UsbHandler mHandler;
private boolean mBootCompleted;
+ private boolean mSystemReady;
private final Object mLock = new Object();
@@ -161,7 +173,6 @@
private boolean mMidiEnabled;
private int mMidiCard;
private int mMidiDevice;
- private HashMap<String, HashMap<String, Pair<String, String>>> mOemModeMap;
private String[] mAccessoryStrings;
private UsbDebuggingManager mDebuggingManager;
private final UsbAlsaManager mUsbAlsaManager;
@@ -169,6 +180,7 @@
private Intent mBroadcastedIntent;
private boolean mPendingBootBroadcast;
private static Set<Integer> sBlackListedInterfaces;
+ private SharedPreferences mSettings;
static {
sBlackListedInterfaces = new HashSet<>();
@@ -217,6 +229,31 @@
}
};
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ int userHandle = ActivityManager.getCurrentUser();
+ boolean secure = mContext.getSystemService(KeyguardManager.class)
+ .isDeviceSecure(userHandle);
+ boolean unlocking = mContext.getSystemService(UserManager.class)
+ .isUserUnlockingOrUnlocked(userHandle);
+ if (DEBUG) {
+ Slog.v(TAG, "onKeyguardStateChanged: isShowing:" + isShowing + " secure:" + secure
+ + " unlocking:" + unlocking + " user:" + userHandle);
+ }
+ // We are unlocked when the keyguard is down or non-secure, and user storage is unlocked.
+ mHandler.sendMessage(MSG_UPDATE_SCREEN_LOCK, (isShowing && secure) || !unlocking);
+ }
+
+ @Override
+ public void onAwakeStateChanged(boolean isAwake) {
+ // ignore
+ }
+
+ /** Called when a user is unlocked. */
+ public void onUnlockUser(int userHandle) {
+ onKeyguardStateChanged(false);
+ }
+
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
UsbSettingsManager settingsManager) {
mContext = context;
@@ -227,9 +264,27 @@
mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
initRndisAddress();
- readOemUsbOverrideConfig();
+ boolean halNotPresent = false;
+ try {
+ IUsbGadget.getService(true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "USB GADGET HAL present but exception thrown", e);
+ } catch (NoSuchElementException e) {
+ halNotPresent = true;
+ Slog.i(TAG, "USB GADGET HAL not present in the device", e);
+ }
- mHandler = new UsbHandler(FgThread.get().getLooper());
+ if (halNotPresent) {
+ /**
+ * Initialze the legacy UsbHandler
+ */
+ mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext);
+ } else {
+ /**
+ * Initialize HAL based UsbHandler
+ */
+ mHandler = new UsbHandlerHal(FgThread.get().getLooper());
+ }
if (nativeIsStartRequested()) {
if (DEBUG) Slog.d(TAG, "accessory attached at boot");
@@ -303,6 +358,8 @@
public void systemReady() {
if (DEBUG) Slog.d(TAG, "systemReady");
+ LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
+
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -325,15 +382,6 @@
massStorageSupported = primary != null && primary.allowMassStorage();
mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
com.android.internal.R.bool.config_usbChargingMessage);
-
- // make sure the ADB_ENABLED setting value matches the current state
- try {
- Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
- } catch (SecurityException e) {
- // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
- Slog.d(TAG, "ADB_ENABLED is restricted.");
- }
mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
@@ -407,7 +455,15 @@
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
- private final class UsbHandler extends Handler {
+ private SharedPreferences getPinnedSharedPrefs(Context context) {
+ final File prefsFile = new File(new File(
+ Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+ context.getUserId(), context.getPackageName()), "shared_prefs"),
+ UsbDeviceManager.class.getSimpleName() + ".xml");
+ return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ }
+
+ private abstract class UsbHandler extends Handler {
// current USB state
private boolean mConnected;
@@ -415,71 +471,53 @@
private boolean mSourcePower;
private boolean mSinkPower;
private boolean mConfigured;
- private boolean mUsbDataUnlocked;
+ protected boolean mUsbDataUnlocked;
private boolean mAudioAccessoryConnected;
private boolean mAudioAccessorySupported;
- private String mCurrentFunctions;
- private boolean mCurrentFunctionsApplied;
+ protected String mCurrentFunctions;
+ protected boolean mCurrentFunctionsApplied;
private UsbAccessory mCurrentAccessory;
private int mUsbNotificationId;
private boolean mAdbNotificationShown;
- private int mCurrentUser = UserHandle.USER_NULL;
+ private int mCurrentUser;
private boolean mUsbCharging;
- private String mCurrentOemFunctions;
private boolean mHideUsbNotification;
private boolean mSupportsAllCombinations;
+ private String mScreenUnlockedFunctions = UsbManager.USB_FUNCTION_NONE;
+ private boolean mScreenLocked;
+ protected boolean mCurrentUsbFunctionsRequested;
+ protected boolean mCurrentUsbFunctionsReceived;
+
+ /**
+ * The persistent property which stores whether adb is enabled or not.
+ * May also contain vendor-specific default functions for testing purposes.
+ */
+ protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
public UsbHandler(Looper looper) {
super(looper);
- try {
- // Restore default functions.
- if (isNormalBoot()) {
- mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
- UsbManager.USB_FUNCTION_NONE);
- mCurrentFunctionsApplied = mCurrentFunctions.equals(
- SystemProperties.get(USB_STATE_PROPERTY));
- } else {
- mCurrentFunctions = SystemProperties.get(getPersistProp(true),
- UsbManager.USB_FUNCTION_NONE);
- mCurrentFunctionsApplied = SystemProperties.get(USB_CONFIG_PROPERTY,
- UsbManager.USB_FUNCTION_NONE).equals(
- SystemProperties.get(USB_STATE_PROPERTY));
- }
+ mCurrentUser = ActivityManager.getCurrentUser();
+ mScreenLocked = true;
- /*
- * Use the normal bootmode persistent prop to maintain state of adb across
- * all boot modes.
- */
- mAdbEnabled = UsbManager.containsFunction(
- SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY),
- UsbManager.USB_FUNCTION_ADB);
+ /*
+ * Use the normal bootmode persistent prop to maintain state of adb across
+ * all boot modes.
+ */
+ mAdbEnabled = UsbManager.containsFunction(
+ SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY),
+ UsbManager.USB_FUNCTION_ADB);
- /*
- * Previous versions can set persist config to mtp/ptp but it does not
- * get reset on OTA. Reset the property here instead.
- */
- String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
- if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_PTP)) {
- SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
- UsbManager.removeFunction(UsbManager.removeFunction(persisted,
- UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP));
- }
-
- String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
- updateState(state);
-
- // register observer to listen for settings changes
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
- false, new AdbSettingsObserver());
-
- // Watch for USB configuration changes
- mUEventObserver.startObserving(USB_STATE_MATCH);
- mUEventObserver.startObserving(ACCESSORY_START_MATCH);
- } catch (Exception e) {
- Slog.e(TAG, "Error initializing UsbHandler", e);
+ /*
+ * Previous versions can set persist config to mtp/ptp but it does not
+ * get reset on OTA. Reset the property here instead.
+ */
+ String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
+ if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)
+ || UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_PTP)) {
+ SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
+ UsbManager.removeFunction(UsbManager.removeFunction(persisted,
+ UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP));
}
}
@@ -505,6 +543,21 @@
sendMessage(m);
}
+ public void sendMessage(int what, boolean arg1, boolean arg2) {
+ removeMessages(what);
+ Message m = Message.obtain(this, what);
+ m.arg1 = (arg1 ? 1 : 0);
+ m.arg2 = (arg2 ? 1 : 0);
+ sendMessage(m);
+ }
+
+ public void sendMessageDelayed(int what, boolean arg, long delayMillis) {
+ removeMessages(what);
+ Message m = Message.obtain(this, what);
+ m.arg1 = (arg ? 1 : 0);
+ sendMessageDelayed(m, delayMillis);
+ }
+
public void updateState(String state) {
int connected, configured;
@@ -522,6 +575,7 @@
return;
}
removeMessages(MSG_UPDATE_STATE);
+ if (connected == 1) removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
Message msg = Message.obtain(this, MSG_UPDATE_STATE);
msg.arg1 = connected;
msg.arg2 = configured;
@@ -544,28 +598,6 @@
sendMessageDelayed(msg, UPDATE_DELAY);
}
- private boolean waitForState(String state) {
- // wait for the transition to complete.
- // give up after 1 second.
- String value = null;
- for (int i = 0; i < 20; i++) {
- // State transition is done when sys.usb.state is set to the new configuration
- value = SystemProperties.get(USB_STATE_PROPERTY);
- if (state.equals(value)) return true;
- SystemClock.sleep(50);
- }
- Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value);
- return false;
- }
-
- private void setUsbConfig(String config) {
- if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
- // set the new configuration
- // we always set it due to b/23631400, where adbd was getting killed
- // and not restarted due to property timeouts on some devices
- SystemProperties.set(USB_CONFIG_PROPERTY, config);
- }
-
private void setAdbEnabled(boolean enable) {
if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
if (enable != mAdbEnabled) {
@@ -592,115 +624,7 @@
}
}
- /**
- * Evaluates USB function policies and applies the change accordingly.
- */
- private void setEnabledFunctions(String functions, boolean forceRestart,
- boolean usbDataUnlocked) {
- if (DEBUG) {
- Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
- + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
- }
-
- if (usbDataUnlocked != mUsbDataUnlocked) {
- mUsbDataUnlocked = usbDataUnlocked;
- updateUsbNotification(false);
- forceRestart = true;
- }
-
- // Try to set the enabled functions.
- final String oldFunctions = mCurrentFunctions;
- final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
- if (trySetEnabledFunctions(functions, forceRestart)) {
- return;
- }
-
- // Didn't work. Try to revert changes.
- // We always reapply the policy in case certain constraints changed such as
- // user restrictions independently of any other new functions we were
- // trying to activate.
- if (oldFunctionsApplied && !oldFunctions.equals(functions)) {
- Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
- if (trySetEnabledFunctions(oldFunctions, false)) {
- return;
- }
- }
-
- // Still didn't work. Try to restore the default functions.
- Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
- if (trySetEnabledFunctions(null, false)) {
- return;
- }
-
- // Now we're desperate. Ignore the default functions.
- // Try to get ADB working if enabled.
- Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
- if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) {
- return;
- }
-
- // Ouch.
- Slog.e(TAG, "Unable to set any USB functions!");
- }
-
- private boolean isNormalBoot() {
- String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
- return bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown");
- }
-
- private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
- if (functions == null || applyAdbFunction(functions)
- .equals(UsbManager.USB_FUNCTION_NONE)) {
- functions = getDefaultFunctions();
- }
- functions = applyAdbFunction(functions);
-
- String oemFunctions = applyOemOverrideFunction(functions);
-
- if (!isNormalBoot() && !mCurrentFunctions.equals(functions)) {
- SystemProperties.set(getPersistProp(true), functions);
- }
-
- if ((!functions.equals(oemFunctions) &&
- (mCurrentOemFunctions == null ||
- !mCurrentOemFunctions.equals(oemFunctions)))
- || !mCurrentFunctions.equals(functions)
- || !mCurrentFunctionsApplied
- || forceRestart) {
- Slog.i(TAG, "Setting USB config to " + functions);
- mCurrentFunctions = functions;
- mCurrentOemFunctions = oemFunctions;
- mCurrentFunctionsApplied = false;
-
- // Kick the USB stack to close existing connections.
- setUsbConfig(UsbManager.USB_FUNCTION_NONE);
-
- if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
- Slog.e(TAG, "Failed to kick USB config");
- return false;
- }
-
- // Set the new USB configuration.
- setUsbConfig(oemFunctions);
-
- if (mBootCompleted
- && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
- // Start up dependent services.
- updateUsbStateBroadcastIfNeeded(true);
- }
-
- if (!waitForState(oemFunctions)) {
- Slog.e(TAG, "Failed to switch USB config to " + functions);
- return false;
- }
-
- mCurrentFunctionsApplied = true;
- }
- return true;
- }
-
- private String applyAdbFunction(String functions) {
+ protected String applyAdbFunction(String functions) {
// Do not pass null pointer to the UsbManager.
// There isnt a check there.
if (functions == null) {
@@ -782,7 +706,7 @@
return false;
}
- private void updateUsbStateBroadcastIfNeeded(boolean configChanged) {
+ protected void updateUsbStateBroadcastIfNeeded(boolean configChanged) {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -875,6 +799,14 @@
mMidiEnabled && mConfigured, mMidiCard, mMidiDevice);
}
+ private void setScreenUnlockedFunctions() {
+ setEnabledFunctions(mScreenUnlockedFunctions, false,
+ UsbManager.containsFunction(mScreenUnlockedFunctions,
+ UsbManager.USB_FUNCTION_MTP)
+ || UsbManager.containsFunction(mScreenUnlockedFunctions,
+ UsbManager.USB_FUNCTION_PTP));
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -892,9 +824,16 @@
updateCurrentAccessory();
}
if (mBootCompleted) {
- if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)) {
+ if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)
+ && !hasMessages(MSG_FUNCTION_SWITCH_TIMEOUT)) {
// restore defaults when USB is disconnected
- setEnabledFunctions(null, !mAdbEnabled, false);
+ if (!mScreenLocked
+ && !UsbManager.USB_FUNCTION_NONE.equals(
+ mScreenUnlockedFunctions)) {
+ setScreenUnlockedFunctions();
+ } else {
+ setEnabledFunctions(null, !mAdbEnabled, false);
+ }
}
updateUsbFunctions();
} else {
@@ -977,6 +916,47 @@
String functions = (String) msg.obj;
setEnabledFunctions(functions, false, msg.arg1 == 1);
break;
+ case MSG_SET_SCREEN_UNLOCKED_FUNCTIONS:
+ mScreenUnlockedFunctions = (String) msg.obj;
+ SharedPreferences.Editor editor = mSettings.edit();
+ editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF,
+ mCurrentUser), mScreenUnlockedFunctions);
+ editor.commit();
+ if (!mScreenLocked && !UsbManager.USB_FUNCTION_NONE.equals(
+ mScreenUnlockedFunctions)) {
+ // If the screen is unlocked, also set current functions.
+ setScreenUnlockedFunctions();
+ }
+ break;
+ case MSG_UPDATE_SCREEN_LOCK:
+ if (msg.arg1 == 1 == mScreenLocked) {
+ break;
+ }
+ mScreenLocked = msg.arg1 == 1;
+ if (mSettings == null && !mScreenLocked) {
+ // Shared preferences aren't accessible until the user has been unlocked.
+ mSettings = getPinnedSharedPrefs(mContext);
+ mScreenUnlockedFunctions = mSettings.getString(
+ String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
+ UsbManager.USB_FUNCTION_NONE);
+ }
+ if (!mBootCompleted) {
+ break;
+ }
+ if (mScreenLocked) {
+ if (!mConnected) {
+ setEnabledFunctions(null, false, false);
+ }
+ } else {
+ if (!UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)
+ && (UsbManager.USB_FUNCTION_ADB.equals(mCurrentFunctions)
+ || (UsbManager.USB_FUNCTION_MTP.equals(mCurrentFunctions)
+ && !mUsbDataUnlocked))) {
+ // Set the screen unlocked functions if current function is charging.
+ setScreenUnlockedFunctions();
+ }
+ }
+ break;
case MSG_UPDATE_USER_RESTRICTIONS:
// Restart the USB stack if USB transfer is enabled but no longer allowed.
final boolean forceRestart = mUsbDataUnlocked
@@ -986,9 +966,8 @@
mCurrentFunctions, forceRestart, mUsbDataUnlocked && !forceRestart);
break;
case MSG_SYSTEM_READY:
- updateUsbNotification(false);
- updateAdbNotification(false);
- updateUsbFunctions();
+ mSystemReady = true;
+ finishBoot();
break;
case MSG_LOCALE_CHANGED:
updateAdbNotification(true);
@@ -996,30 +975,19 @@
break;
case MSG_BOOT_COMPLETED:
mBootCompleted = true;
- if (mPendingBootBroadcast) {
- updateUsbStateBroadcastIfNeeded(false);
- mPendingBootBroadcast = false;
- }
- setEnabledFunctions(null, false, false);
- if (mCurrentAccessory != null) {
- getCurrentSettings().accessoryAttached(mCurrentAccessory);
- }
- if (mDebuggingManager != null) {
- mDebuggingManager.setAdbEnabled(mAdbEnabled);
- }
+ finishBoot();
break;
case MSG_USER_SWITCHED: {
if (mCurrentUser != msg.arg1) {
- // Restart the USB stack and re-apply user restrictions for MTP or PTP.
- if (mUsbDataUnlocked
- && isUsbDataTransferActive()
- && mCurrentUser != UserHandle.USER_NULL) {
- Slog.v(TAG, "Current user switched to " + msg.arg1
- + "; resetting USB host stack for MTP or PTP");
- // avoid leaking sensitive data from previous user
- setEnabledFunctions(null, true, false);
+ if (DEBUG) {
+ Slog.v(TAG, "Current user switched to " + msg.arg1);
}
mCurrentUser = msg.arg1;
+ mScreenLocked = true;
+ mScreenUnlockedFunctions = mSettings.getString(
+ String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser),
+ UsbManager.USB_FUNCTION_NONE);
+ setEnabledFunctions(null, false, false);
}
break;
}
@@ -1037,6 +1005,41 @@
}
}
+ protected void finishBoot() {
+ if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
+ if (mPendingBootBroadcast) {
+ updateUsbStateBroadcastIfNeeded(false);
+ mPendingBootBroadcast = false;
+ }
+ if (!mScreenLocked
+ && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) {
+ setScreenUnlockedFunctions();
+ } else {
+ setEnabledFunctions(null, false, false);
+ }
+ if (mCurrentAccessory != null) {
+ getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ }
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mAdbEnabled);
+ }
+
+ // make sure the ADB_ENABLED setting value matches the current state
+ try {
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+ } catch (SecurityException e) {
+ // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
+ // be changed.
+ Slog.d(TAG, "ADB_ENABLED is restricted.");
+ }
+
+ updateUsbNotification(false);
+ updateAdbNotification(false);
+ updateUsbFunctions();
+ }
+ }
+
private boolean isUsbDataTransferActive() {
return UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)
|| UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP);
@@ -1046,7 +1049,7 @@
return mCurrentAccessory;
}
- private void updateUsbNotification(boolean force) {
+ protected void updateUsbNotification(boolean force) {
if (mNotificationManager == null || !mUseUsbNotification
|| ("0".equals(SystemProperties.get("persist.charging.notify")))) {
return;
@@ -1071,20 +1074,12 @@
titleRes = com.android.internal.R.string.usb_unsupported_audio_accessory_title;
id = SystemMessage.NOTE_USB_AUDIO_ACCESSORY_NOT_SUPPORTED;
} else if (mConnected) {
- if (!mUsbDataUnlocked) {
- if (mSourcePower) {
- titleRes = com.android.internal.R.string.usb_supplying_notification_title;
- id = SystemMessage.NOTE_USB_SUPPLYING;
- } else {
- titleRes = com.android.internal.R.string.usb_charging_notification_title;
- id = SystemMessage.NOTE_USB_CHARGING;
- }
- } else if (UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_MTP)) {
+ if (UsbManager.containsFunction(mCurrentFunctions,
+ UsbManager.USB_FUNCTION_MTP) && mUsbDataUnlocked) {
titleRes = com.android.internal.R.string.usb_mtp_notification_title;
id = SystemMessage.NOTE_USB_MTP;
} else if (UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_PTP)) {
+ UsbManager.USB_FUNCTION_PTP) && mUsbDataUnlocked) {
titleRes = com.android.internal.R.string.usb_ptp_notification_title;
id = SystemMessage.NOTE_USB_PTP;
} else if (UsbManager.containsFunction(mCurrentFunctions,
@@ -1154,18 +1149,18 @@
}
Notification.Builder builder = new Notification.Builder(mContext, channel)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setWhen(0)
- .setOngoing(true)
- .setTicker(title)
- .setDefaults(0) // please be quiet
- .setColor(mContext.getColor(
- com.android.internal.R.color
- .system_notification_accent_color))
- .setContentTitle(title)
- .setContentText(message)
- .setContentIntent(pi)
- .setVisibility(Notification.VISIBILITY_PUBLIC);
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0) // please be quiet
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setContentIntent(pi)
+ .setVisibility(Notification.VISIBILITY_PUBLIC);
if (titleRes
== com.android.internal.R.string
@@ -1183,7 +1178,7 @@
}
}
- private void updateAdbNotification(boolean force) {
+ protected void updateAdbNotification(boolean force) {
if (mNotificationManager == null) return;
final int id = SystemMessage.NOTE_ADB_ACTIVE;
final int titleRes = com.android.internal.R.string.adb_active_notification_title;
@@ -1235,23 +1230,26 @@
}
}
- private String getDefaultFunctions() {
- String func = SystemProperties.get(getPersistProp(true),
- UsbManager.USB_FUNCTION_NONE);
+ protected String getChargingFunctions() {
// if ADB is enabled, reset functions to ADB
// else enable MTP as usual.
- if (UsbManager.containsFunction(func, UsbManager.USB_FUNCTION_ADB)) {
+ if (mAdbEnabled) {
return UsbManager.USB_FUNCTION_ADB;
} else {
return UsbManager.USB_FUNCTION_MTP;
}
}
+ public boolean isFunctionEnabled(String function) {
+ return UsbManager.containsFunction(mCurrentFunctions, function);
+ }
+
public void dump(IndentingPrintWriter pw) {
pw.println("USB Device State:");
pw.println(" mCurrentFunctions: " + mCurrentFunctions);
- pw.println(" mCurrentOemFunctions: " + mCurrentOemFunctions);
pw.println(" mCurrentFunctionsApplied: " + mCurrentFunctionsApplied);
+ pw.println(" mScreenUnlockedFunctions: " + mScreenUnlockedFunctions);
+ pw.println(" mScreenLocked: " + mScreenLocked);
pw.println(" mConnected: " + mConnected);
pw.println(" mConfigured: " + mConfigured);
pw.println(" mUsbDataUnlocked: " + mUsbDataUnlocked);
@@ -1262,6 +1260,7 @@
pw.println(" mUsbCharging: " + mUsbCharging);
pw.println(" mHideUsbNotification: " + mHideUsbNotification);
pw.println(" mAudioAccessoryConnected: " + mAudioAccessoryConnected);
+ pw.println(" mAdbEnabled: " + mAdbEnabled);
try {
pw.println(" Kernel state: "
@@ -1272,6 +1271,675 @@
pw.println("IOException: " + e);
}
}
+
+ /**
+ * Evaluates USB function policies and applies the change accordingly.
+ */
+ protected abstract void setEnabledFunctions(String functions, boolean forceRestart,
+ boolean usbDataUnlocked);
+
+ }
+
+ private final class UsbHandlerLegacy extends UsbHandler {
+ /**
+ * The non-persistent property which stores the current USB settings.
+ */
+ private static final String USB_CONFIG_PROPERTY = "sys.usb.config";
+
+ /**
+ * The non-persistent property which stores the current USB actual state.
+ */
+ private static final String USB_STATE_PROPERTY = "sys.usb.state";
+
+ private HashMap<String, HashMap<String, Pair<String, String>>> mOemModeMap;
+ private String mCurrentOemFunctions;
+
+ UsbHandlerLegacy(Looper looper, Context context) {
+ super(looper);
+ try {
+ readOemUsbOverrideConfig(context);
+ // Restore default functions.
+ mCurrentOemFunctions = SystemProperties.get(getPersistProp(false),
+ UsbManager.USB_FUNCTION_NONE);
+ if (isNormalBoot()) {
+ mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
+ UsbManager.USB_FUNCTION_NONE);
+ mCurrentFunctionsApplied = mCurrentFunctions.equals(
+ SystemProperties.get(USB_STATE_PROPERTY));
+ } else {
+ mCurrentFunctions = SystemProperties.get(getPersistProp(true),
+ UsbManager.USB_FUNCTION_NONE);
+ mCurrentFunctionsApplied = SystemProperties.get(USB_CONFIG_PROPERTY,
+ UsbManager.USB_FUNCTION_NONE).equals(
+ SystemProperties.get(USB_STATE_PROPERTY));
+ }
+ mCurrentUsbFunctionsReceived = true;
+
+ String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
+ updateState(state);
+
+ // register observer to listen for settings changes
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+ false, new AdbSettingsObserver());
+
+ // Watch for USB configuration changes
+ mUEventObserver.startObserving(USB_STATE_MATCH);
+ mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error initializing UsbHandler", e);
+ }
+ }
+
+ private void readOemUsbOverrideConfig(Context context) {
+ String[] configList = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_oemUsbModeOverride);
+
+ if (configList != null) {
+ for (String config : configList) {
+ String[] items = config.split(":");
+ if (items.length == 3 || items.length == 4) {
+ if (mOemModeMap == null) {
+ mOemModeMap = new HashMap<>();
+ }
+ HashMap<String, Pair<String, String>> overrideMap =
+ mOemModeMap.get(items[0]);
+ if (overrideMap == null) {
+ overrideMap = new HashMap<>();
+ mOemModeMap.put(items[0], overrideMap);
+ }
+
+ // Favoring the first combination if duplicate exists
+ if (!overrideMap.containsKey(items[1])) {
+ if (items.length == 3) {
+ overrideMap.put(items[1], new Pair<>(items[2], ""));
+ } else {
+ overrideMap.put(items[1], new Pair<>(items[2], items[3]));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private String applyOemOverrideFunction(String usbFunctions) {
+ if ((usbFunctions == null) || (mOemModeMap == null)) {
+ return usbFunctions;
+ }
+
+ String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+ Slog.d(TAG, "applyOemOverride usbfunctions=" + usbFunctions + " bootmode=" + bootMode);
+
+ Map<String, Pair<String, String>> overridesMap =
+ mOemModeMap.get(bootMode);
+ // Check to ensure that the oem is not overriding in the normal
+ // boot mode
+ if (overridesMap != null && !(bootMode.equals(NORMAL_BOOT)
+ || bootMode.equals("unknown"))) {
+ Pair<String, String> overrideFunctions =
+ overridesMap.get(usbFunctions);
+ if (overrideFunctions != null) {
+ Slog.d(TAG, "OEM USB override: " + usbFunctions
+ + " ==> " + overrideFunctions.first
+ + " persist across reboot "
+ + overrideFunctions.second);
+ if (!overrideFunctions.second.equals("")) {
+ String newFunction;
+ if (mAdbEnabled) {
+ newFunction = UsbManager.addFunction(overrideFunctions.second,
+ UsbManager.USB_FUNCTION_ADB);
+ } else {
+ newFunction = overrideFunctions.second;
+ }
+ Slog.d(TAG, "OEM USB override persisting: " + newFunction + "in prop: "
+ + getPersistProp(false));
+ SystemProperties.set(getPersistProp(false),
+ newFunction);
+ }
+ return overrideFunctions.first;
+ } else if (mAdbEnabled) {
+ String newFunction = UsbManager.addFunction(UsbManager.USB_FUNCTION_NONE,
+ UsbManager.USB_FUNCTION_ADB);
+ SystemProperties.set(getPersistProp(false),
+ newFunction);
+ } else {
+ SystemProperties.set(getPersistProp(false),
+ UsbManager.USB_FUNCTION_NONE);
+ }
+ }
+ // return passed in functions as is.
+ return usbFunctions;
+ }
+
+ private boolean waitForState(String state) {
+ // wait for the transition to complete.
+ // give up after 1 second.
+ String value = null;
+ for (int i = 0; i < 20; i++) {
+ // State transition is done when sys.usb.state is set to the new configuration
+ value = SystemProperties.get(USB_STATE_PROPERTY);
+ if (state.equals(value)) return true;
+ SystemClock.sleep(50);
+ }
+ Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value);
+ return false;
+ }
+
+ private void setUsbConfig(String config) {
+ if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
+ /**
+ * set the new configuration
+ * we always set it due to b/23631400, where adbd was getting killed
+ * and not restarted due to property timeouts on some devices
+ */
+ SystemProperties.set(USB_CONFIG_PROPERTY, config);
+ }
+
+ @Override
+ protected void setEnabledFunctions(String functions, boolean forceRestart,
+ boolean usbDataUnlocked) {
+ if (DEBUG) {
+ Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+ + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
+ }
+
+ if (usbDataUnlocked != mUsbDataUnlocked) {
+ mUsbDataUnlocked = usbDataUnlocked;
+ updateUsbNotification(false);
+ forceRestart = true;
+ }
+
+ /**
+ * Try to set the enabled functions.
+ */
+ final String oldFunctions = mCurrentFunctions;
+ final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
+ if (trySetEnabledFunctions(functions, forceRestart)) {
+ return;
+ }
+
+ /**
+ * Didn't work. Try to revert changes.
+ * We always reapply the policy in case certain constraints changed such as
+ * user restrictions independently of any other new functions we were
+ * trying to activate.
+ */
+ if (oldFunctionsApplied && !oldFunctions.equals(functions)) {
+ Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
+ if (trySetEnabledFunctions(oldFunctions, false)) {
+ return;
+ }
+ }
+
+ /**
+ * Still didn't work. Try to restore the default functions.
+ */
+ Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
+ if (trySetEnabledFunctions(null, false)) {
+ return;
+ }
+
+ /**
+ * Now we're desperate. Ignore the default functions.
+ * Try to get ADB working if enabled.
+ */
+ Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
+ if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) {
+ return;
+ }
+
+ /**
+ * Ouch.
+ */
+ Slog.e(TAG, "Unable to set any USB functions!");
+ }
+
+ private boolean isNormalBoot() {
+ String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+ return bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown");
+ }
+
+ private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
+ if (functions == null || applyAdbFunction(functions)
+ .equals(UsbManager.USB_FUNCTION_NONE)) {
+ functions = getChargingFunctions();
+ }
+ functions = applyAdbFunction(functions);
+
+ String oemFunctions = applyOemOverrideFunction(functions);
+
+ if (!isNormalBoot() && !mCurrentFunctions.equals(functions)) {
+ SystemProperties.set(getPersistProp(true), functions);
+ }
+
+ if ((!functions.equals(oemFunctions)
+ && !mCurrentOemFunctions.equals(oemFunctions))
+ || !mCurrentFunctions.equals(functions)
+ || !mCurrentFunctionsApplied
+ || forceRestart) {
+ Slog.i(TAG, "Setting USB config to " + functions);
+ mCurrentFunctions = functions;
+ mCurrentOemFunctions = oemFunctions;
+ mCurrentFunctionsApplied = false;
+
+ /**
+ * Kick the USB stack to close existing connections.
+ */
+ setUsbConfig(UsbManager.USB_FUNCTION_NONE);
+
+ if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
+ Slog.e(TAG, "Failed to kick USB config");
+ return false;
+ }
+
+ /**
+ * Set the new USB configuration.
+ */
+ setUsbConfig(oemFunctions);
+
+ if (mBootCompleted
+ && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+ || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+ /**
+ * Start up dependent services.
+ */
+ updateUsbStateBroadcastIfNeeded(true);
+ }
+
+ if (!waitForState(oemFunctions)) {
+ Slog.e(TAG, "Failed to switch USB config to " + functions);
+ return false;
+ }
+
+ mCurrentFunctionsApplied = true;
+ }
+ return true;
+ }
+
+ private String getPersistProp(boolean functions) {
+ String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
+ String persistProp = USB_PERSISTENT_CONFIG_PROPERTY;
+ if (!(bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"))) {
+ if (functions) {
+ persistProp = "persist.sys.usb." + bootMode + ".func";
+ } else {
+ persistProp = "persist.sys.usb." + bootMode + ".config";
+ }
+ }
+ return persistProp;
+ }
+ }
+
+ private final class UsbHandlerHal extends UsbHandler {
+
+ /**
+ * Proxy object for the usb gadget hal daemon.
+ */
+ @GuardedBy("mGadgetProxyLock")
+ private IUsbGadget mGadgetProxy;
+
+ private final Object mGadgetProxyLock = new Object();
+
+ /**
+ * Cookie sent for usb gadget hal death notification.
+ */
+ private static final int USB_GADGET_HAL_DEATH_COOKIE = 2000;
+
+ /**
+ * Keeps track of the latest setCurrentUsbFunctions request number.
+ */
+ private int mCurrentRequest = 0;
+
+ /**
+ * The maximum time for which the UsbDeviceManager would wait once
+ * setCurrentUsbFunctions is called.
+ */
+ private static final int SET_FUNCTIONS_TIMEOUT_MS = 3000;
+
+ /**
+ * Conseration leeway to make sure that the hal callback arrives before
+ * SET_FUNCTIONS_TIMEOUT_MS expires. If the callback does not arrive
+ * within SET_FUNCTIONS_TIMEOUT_MS, UsbDeviceManager retries enabling
+ * default functions.
+ */
+ private static final int SET_FUNCTIONS_LEEWAY_MS = 500;
+
+ /**
+ * While switching functions, a disconnect is excpect as the usb gadget
+ * us torn down and brought back up. Wait for SET_FUNCTIONS_TIMEOUT_MS +
+ * ENUMERATION_TIME_OUT_MS before switching back to default fumctions when
+ * switching functions.
+ */
+ private static final int ENUMERATION_TIME_OUT_MS = 2000;
+
+ /**
+ * Command to start native service.
+ */
+ protected static final String CTL_START = "ctl.start";
+
+ /**
+ * Command to start native service.
+ */
+ protected static final String CTL_STOP = "ctl.stop";
+
+ /**
+ * Adb natvie daemon
+ */
+ protected static final String ADBD = "adbd";
+
+
+ UsbHandlerHal(Looper looper) {
+ super(looper);
+ try {
+ ServiceNotification serviceNotification = new ServiceNotification();
+
+ boolean ret = IServiceManager.getService()
+ .registerForNotifications("android.hardware.usb.gadget@1.0::IUsbGadget",
+ "", serviceNotification);
+ if (!ret) {
+ Slog.e(TAG, "Failed to register usb gadget service start notification");
+ return;
+ }
+
+ synchronized (mGadgetProxyLock) {
+ mGadgetProxy = IUsbGadget.getService(true);
+ mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
+ USB_GADGET_HAL_DEATH_COOKIE);
+ mCurrentFunctions = UsbManager.USB_FUNCTION_NONE;
+ mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
+ mCurrentUsbFunctionsRequested = true;
+ }
+ String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
+ updateState(state);
+
+ /**
+ * Register observer to listen for settings changes.
+ */
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+ false, new AdbSettingsObserver());
+
+ /**
+ * Watch for USB configuration changes.
+ */
+ mUEventObserver.startObserving(USB_STATE_MATCH);
+ mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+ } catch (NoSuchElementException e) {
+ Slog.e(TAG, "Usb gadget hal not found", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Usb Gadget hal not responding", e);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error initializing UsbHandler", e);
+ }
+ }
+
+
+ final class UsbGadgetDeathRecipient implements HwBinder.DeathRecipient {
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == USB_GADGET_HAL_DEATH_COOKIE) {
+ Slog.e(TAG, "Usb Gadget hal service died cookie: " + cookie);
+ synchronized (mGadgetProxyLock) {
+ mGadgetProxy = null;
+ }
+ }
+ }
+ }
+
+ final class ServiceNotification extends IServiceNotification.Stub {
+ @Override
+ public void onRegistration(String fqName, String name, boolean preexisting) {
+ Slog.i(TAG, "Usb gadget hal service started " + fqName + " " + name);
+ synchronized (mGadgetProxyLock) {
+ try {
+ mGadgetProxy = IUsbGadget.getService();
+ mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
+ USB_GADGET_HAL_DEATH_COOKIE);
+ if (!mCurrentFunctionsApplied) {
+ setCurrentFunctions(mCurrentFunctions, mUsbDataUnlocked);
+ }
+ } catch (NoSuchElementException e) {
+ Slog.e(TAG, "Usb gadget hal not found", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Usb Gadget hal not responding", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_CHARGING_FUNCTIONS:
+ setEnabledFunctions(null, false, mUsbDataUnlocked);
+ break;
+ case MSG_SET_FUNCTIONS_TIMEOUT:
+ Slog.e(TAG, "Set functions timed out! no reply from usb hal");
+ if (msg.arg1 != 1) {
+ setEnabledFunctions(null, false, mUsbDataUnlocked);
+ }
+ break;
+ case MSG_GET_CURRENT_USB_FUNCTIONS:
+ Slog.e(TAG, "prcessing MSG_GET_CURRENT_USB_FUNCTIONS");
+ mCurrentUsbFunctionsReceived = true;
+
+ if (mCurrentUsbFunctionsRequested) {
+ Slog.e(TAG, "updating mCurrentFunctions");
+ mCurrentFunctions = functionListToString((Long) msg.obj);
+ Slog.e(TAG,
+ "mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
+ mCurrentFunctionsApplied = msg.arg1 == 1;
+ }
+ finishBoot();
+ break;
+ case MSG_FUNCTION_SWITCH_TIMEOUT:
+ /**
+ * Dont force to default when the configuration is already set to default.
+ */
+ if (msg.arg1 != 1) {
+ setEnabledFunctions(null, !mAdbEnabled, false);
+ }
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ private class UsbGadgetCallback extends IUsbGadgetCallback.Stub {
+ int mRequest;
+ long mFunctions;
+ boolean mChargingFunctions;
+
+ UsbGadgetCallback() {
+ }
+
+ UsbGadgetCallback(int request, long functions,
+ boolean chargingFunctions) {
+ mRequest = request;
+ mFunctions = functions;
+ mChargingFunctions = chargingFunctions;
+ }
+
+ @Override
+ public void setCurrentUsbFunctionsCb(long functions,
+ int status) {
+ /**
+ * Callback called for a previous setCurrenUsbFunction
+ */
+ if ((mCurrentRequest != mRequest) || !hasMessages(MSG_SET_FUNCTIONS_TIMEOUT)
+ || (mFunctions != functions)) {
+ return;
+ }
+
+ removeMessages(MSG_SET_FUNCTIONS_TIMEOUT);
+ Slog.e(TAG, "notifyCurrentFunction request:" + mRequest + " status:" + status);
+ if (status == Status.SUCCESS) {
+ mCurrentFunctionsApplied = true;
+ } else if (!mChargingFunctions) {
+ Slog.e(TAG, "Setting default fuctions");
+ sendEmptyMessage(MSG_SET_CHARGING_FUNCTIONS);
+ }
+ }
+
+ @Override
+ public void getCurrentUsbFunctionsCb(long functions,
+ int status) {
+ sendMessage(MSG_GET_CURRENT_USB_FUNCTIONS, functions,
+ status == Status.FUNCTIONS_APPLIED);
+ }
+ }
+
+ private long stringToFunctionList(String config) {
+ long functionsMask = 0;
+ String[] functions = config.split(",");
+ for (int i = 0; i < functions.length; i++) {
+ switch (functions[i]) {
+ case "none":
+ functionsMask |= GadgetFunction.NONE;
+ break;
+ case "adb":
+ functionsMask |= GadgetFunction.ADB;
+ break;
+ case "mtp":
+ functionsMask |= GadgetFunction.MTP;
+ break;
+ case "ptp":
+ functionsMask |= GadgetFunction.PTP;
+ break;
+ case "midi":
+ functionsMask |= GadgetFunction.MIDI;
+ break;
+ case "accessory":
+ functionsMask |= GadgetFunction.ACCESSORY;
+ break;
+ case "rndis":
+ functionsMask |= GadgetFunction.RNDIS;
+ break;
+ }
+ }
+ return functionsMask;
+ }
+
+ private String functionListToString(Long functionList) {
+ StringJoiner functions = new StringJoiner(",");
+ if (functionList == GadgetFunction.NONE) {
+ functions.add("none");
+ return functions.toString();
+ }
+ if ((functionList & GadgetFunction.ADB) != 0) {
+ functions.add("adb");
+ }
+ if ((functionList & GadgetFunction.MTP) != 0) {
+ functions.add("mtp");
+ }
+ if ((functionList & GadgetFunction.PTP) != 0) {
+ functions.add("ptp");
+ }
+ if ((functionList & GadgetFunction.MIDI) != 0) {
+ functions.add("midi");
+ }
+ if ((functionList & GadgetFunction.ACCESSORY) != 0) {
+ functions.add("accessory");
+ }
+ if ((functionList & GadgetFunction.RNDIS) != 0) {
+ functions.add("rndis");
+ }
+ /**
+ * Remove the trailing comma.
+ */
+ return functions.toString();
+ }
+
+
+ private void setUsbConfig(String config, boolean chargingFunctions) {
+ Long functions = stringToFunctionList(config);
+ if (true) Slog.d(TAG, "setUsbConfig(" + config + ") request:" + ++mCurrentRequest);
+ /**
+ * Cancel any ongoing requests, if present.
+ */
+ removeMessages(MSG_FUNCTION_SWITCH_TIMEOUT);
+ removeMessages(MSG_SET_FUNCTIONS_TIMEOUT);
+ removeMessages(MSG_SET_CHARGING_FUNCTIONS);
+
+ synchronized (mGadgetProxyLock) {
+ if (mGadgetProxy == null) {
+ Slog.e(TAG, "setUsbConfig mGadgetProxy is null");
+ return;
+ }
+ try {
+ if ((functions & GadgetFunction.ADB) != 0) {
+ /**
+ * Start adbd if ADB function is included in the configuration.
+ */
+ SystemProperties.set(CTL_START, ADBD);
+ } else {
+ /**
+ * Stop adbd otherwise.
+ */
+ SystemProperties.set(CTL_STOP, ADBD);
+ }
+ UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
+ functions, chargingFunctions);
+ mGadgetProxy.setCurrentUsbFunctions(functions, usbGadgetCallback,
+ SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
+ sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
+ SET_FUNCTIONS_TIMEOUT_MS);
+ sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
+ SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+ if (DEBUG) Slog.d(TAG, "timeout message queued");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remoteexception while calling setCurrentUsbFunctions", e);
+ }
+ }
+ }
+
+ @Override
+ protected void setEnabledFunctions(String functions, boolean forceRestart,
+ boolean usbDataUnlocked) {
+ if (DEBUG) {
+ Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+ + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
+ }
+
+ if (usbDataUnlocked != mUsbDataUnlocked) {
+ mUsbDataUnlocked = usbDataUnlocked;
+ updateUsbNotification(false);
+ forceRestart = true;
+ }
+
+ trySetEnabledFunctions(functions, forceRestart);
+ }
+
+ private void trySetEnabledFunctions(String functions, boolean forceRestart) {
+ boolean chargingFunctions = false;
+
+ if (functions == null || applyAdbFunction(functions)
+ .equals(UsbManager.USB_FUNCTION_NONE)) {
+ functions = getChargingFunctions();
+ chargingFunctions = true;
+ }
+ functions = applyAdbFunction(functions);
+
+ if (!mCurrentFunctions.equals(functions)
+ || !mCurrentFunctionsApplied
+ || forceRestart) {
+ Slog.i(TAG, "Setting USB config to " + functions);
+ mCurrentFunctions = functions;
+ mCurrentFunctionsApplied = false;
+ // set the flag to false as that would be stale value
+ mCurrentUsbFunctionsRequested = false;
+
+ // Set the new USB configuration.
+ setUsbConfig(mCurrentFunctions, chargingFunctions);
+
+ if (mBootCompleted
+ && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+ || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
+ // Start up dependent services.
+ updateUsbStateBroadcastIfNeeded(true);
+ }
+ }
+ }
}
/* returns the currently attached USB accessory */
@@ -1279,7 +1947,11 @@
return mHandler.getCurrentAccessory();
}
- /* opens the currently attached USB accessory */
+ /**
+ * opens the currently attached USB accessory.
+ *
+ * @param accessory accessory to be openened.
+ */
public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
UsbUserSettingsManager settings) {
UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
@@ -1296,113 +1968,41 @@
return nativeOpenAccessory();
}
+ /**
+ * Checks whether the function is present in the USB configuration.
+ *
+ * @param function function to be checked.
+ */
public boolean isFunctionEnabled(String function) {
- return UsbManager.containsFunction(SystemProperties.get(USB_CONFIG_PROPERTY), function);
+ return mHandler.isFunctionEnabled(function);
}
+ /**
+ * Adds function to the current USB configuration.
+ *
+ * @param functions name of the USB function, or null to restore the default function.
+ * @param usbDataUnlocked whether user data is accessible.
+ */
public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
if (DEBUG) {
- Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
- usbDataUnlocked + ")");
+ Slog.d(TAG, "setCurrentFunctions(" + functions + ", "
+ + usbDataUnlocked + ")");
}
mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
}
- private void readOemUsbOverrideConfig() {
- String[] configList = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_oemUsbModeOverride);
-
- if (configList != null) {
- for (String config : configList) {
- String[] items = config.split(":");
- if (items.length == 3 || items.length == 4) {
- if (mOemModeMap == null) {
- mOemModeMap = new HashMap<>();
- }
- HashMap<String, Pair<String, String>> overrideMap
- = mOemModeMap.get(items[0]);
- if (overrideMap == null) {
- overrideMap = new HashMap<>();
- mOemModeMap.put(items[0], overrideMap);
- }
-
- // Favoring the first combination if duplicate exists
- if (!overrideMap.containsKey(items[1])) {
- if (items.length == 3) {
- overrideMap.put(items[1], new Pair<>(items[2], ""));
- } else {
- overrideMap.put(items[1], new Pair<>(items[2], items[3]));
- }
- }
- }
- }
+ /**
+ * Sets the functions which are set when the screen is unlocked.
+ *
+ * @param functions Functions to set.
+ */
+ public void setScreenUnlockedFunctions(String functions) {
+ if (DEBUG) {
+ Slog.d(TAG, "setScreenUnlockedFunctions(" + functions + ")");
}
+ mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
}
- private String applyOemOverrideFunction(String usbFunctions) {
- if ((usbFunctions == null) || (mOemModeMap == null)) {
- return usbFunctions;
- }
-
- String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
- Slog.d(TAG, "applyOemOverride usbfunctions=" + usbFunctions + " bootmode=" + bootMode);
-
- Map<String, Pair<String, String>> overridesMap =
- mOemModeMap.get(bootMode);
- // Check to ensure that the oem is not overriding in the normal
- // boot mode
- if (overridesMap != null && !(bootMode.equals(NORMAL_BOOT) ||
- bootMode.equals("unknown"))) {
- Pair<String, String> overrideFunctions =
- overridesMap.get(usbFunctions);
- if (overrideFunctions != null) {
- Slog.d(TAG, "OEM USB override: " + usbFunctions
- + " ==> " + overrideFunctions.first
- + " persist across reboot "
- + overrideFunctions.second);
- if (!overrideFunctions.second.equals("")) {
- String newFunction;
- if (mAdbEnabled) {
- newFunction = UsbManager.addFunction(overrideFunctions.second,
- UsbManager.USB_FUNCTION_ADB);
- } else {
- newFunction = overrideFunctions.second;
- }
- Slog.d(TAG, "OEM USB override persisting: " + newFunction + "in prop: "
- + UsbDeviceManager.getPersistProp(false));
- SystemProperties.set(UsbDeviceManager.getPersistProp(false),
- newFunction);
- }
- return overrideFunctions.first;
- } else if (mAdbEnabled) {
- String newFunction = UsbManager.addFunction(UsbManager.USB_FUNCTION_NONE,
- UsbManager.USB_FUNCTION_ADB);
- SystemProperties.set(UsbDeviceManager.getPersistProp(false),
- newFunction);
- } else {
- SystemProperties.set(UsbDeviceManager.getPersistProp(false),
- UsbManager.USB_FUNCTION_NONE);
- }
- }
- // return passed in functions as is.
- return usbFunctions;
- }
-
- public static String getPersistProp(boolean functions) {
- String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
- String persistProp = USB_PERSISTENT_CONFIG_PROPERTY;
- if (!(bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"))) {
- if (functions) {
- persistProp = "persist.sys.usb." + bootMode + ".func";
- } else {
- persistProp = "persist.sys.usb." + bootMode + ".config";
- }
- }
-
- return persistProp;
- }
-
-
public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
if (mDebuggingManager != null) {
mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey);
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index e4fcea7..039597c 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -87,6 +87,11 @@
public void onStopUser(int userHandle) {
mUsbService.onStopUser(UserHandle.of(userHandle));
}
+
+ @Override
+ public void onUnlockUser(int userHandle) {
+ mUsbService.onUnlockUser(userHandle);
+ }
}
private static final String TAG = "UsbService";
@@ -205,6 +210,13 @@
}
}
+ /** Called when a user is unlocked. */
+ public void onUnlockUser(int user) {
+ if (mDeviceManager != null) {
+ mDeviceManager.onUnlockUser(user);
+ }
+ }
+
/* Returns a list of all currently attached USB devices (host mdoe) */
@Override
public void getDeviceList(Bundle devices) {
@@ -389,6 +401,23 @@
}
}
+ @Override
+ public void setScreenUnlockedFunctions(String function) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ if (!isSupportedCurrentFunction(function)) {
+ Slog.w(TAG, "Caller of setScreenUnlockedFunctions() requested unsupported USB function:"
+ + function);
+ function = UsbManager.USB_FUNCTION_NONE;
+ }
+
+ if (mDeviceManager != null) {
+ mDeviceManager.setScreenUnlockedFunctions(function);
+ } else {
+ throw new IllegalStateException("USB device mode not supported");
+ }
+ }
+
private static boolean isSupportedCurrentFunction(String function) {
if (function == null) return true;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index f53eb15..763dffb 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -34,6 +34,7 @@
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
+import android.os.Binder;
import android.os.DeadObjectException;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -880,21 +881,26 @@
}
private void initializeTelephonyAndPowerStateListeners() {
- // Get the current call state synchronously for the first recognition.
- mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
+ long token = Binder.clearCallingIdentity();
+ try {
+ // Get the current call state synchronously for the first recognition.
+ mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
- // Register for call state changes when the first call to start recognition occurs.
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+ // Register for call state changes when the first call to start recognition occurs.
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
- // Register for power saver mode changes when the first call to start recognition
- // occurs.
- if (mPowerSaveModeListener == null) {
- mPowerSaveModeListener = new PowerSaveModeListener();
- mContext.registerReceiver(mPowerSaveModeListener,
- new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+ // Register for power saver mode changes when the first call to start recognition
+ // occurs.
+ if (mPowerSaveModeListener == null) {
+ mPowerSaveModeListener = new PowerSaveModeListener();
+ mContext.registerReceiver(mPowerSaveModeListener,
+ new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+ }
+ mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
+ .batterySaverEnabled;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
- .batterySaverEnabled;
}
// Sends an error callback to all models with a valid registered callback.
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
new file mode 100644
index 0000000..a3bcfb2
--- /dev/null
+++ b/telecomm/OWNERS
@@ -0,0 +1,6 @@
+set noparent
+
+tgunn@google.com
+breadley@google.com
+hallliu@google.com
+rgreenwalt@google.com
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2091101..a79f2c9 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -352,8 +352,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x01000000
+ // Next CAPABILITY value: 0x02000000
//******************************************************************************************
/**
@@ -423,8 +426,14 @@
*/
public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
+ /**
+ * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the
+ * {@link RttCall} object that is used to send and receive text.
+ */
+ public static final int PROPERTY_RTT = 0x00000400;
+
//******************************************************************************************
- // Next PROPERTY value: 0x00000400
+ // Next PROPERTY value: 0x00000800
//******************************************************************************************
private final String mTelecomCallId;
@@ -529,6 +538,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(" CAPABILITY_CAN_PULL_CALL");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(" CAPABILITY_SUPPORT_DEFLECT");
+ }
builder.append("]");
return builder.toString();
}
@@ -1184,6 +1196,23 @@
return null;
}
}
+
+ /**
+ * Closes the underlying file descriptors
+ * @hide
+ */
+ public void close() {
+ try {
+ mReceiveStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ try {
+ mTransmitStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
}
/**
@@ -1236,6 +1265,15 @@
}
/**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to deflect.
+ *
+ * @param address The address to which the call will be deflected.
+ */
+ public void deflect(Uri address) {
+ mInCallAdapter.deflectCall(mTelecomCallId, address);
+ }
+
+ /**
* Instructs this {@link #STATE_RINGING} {@code Call} to reject.
*
* @param rejectWithMessage Whether to reject with a text message.
@@ -1408,7 +1446,7 @@
* @param extras Bundle containing extra information associated with the event.
*/
public void sendCallEvent(String event, Bundle extras) {
- mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
+ mInCallAdapter.sendCallEvent(mTelecomCallId, event, mTargetSdkVersion, extras);
}
/**
@@ -1650,7 +1688,7 @@
* @return true if there is a connection, false otherwise.
*/
public boolean isRttActive() {
- return mRttCall != null;
+ return mRttCall != null && mDetails.hasProperty(Details.PROPERTY_RTT);
}
/**
@@ -1853,7 +1891,8 @@
boolean isRttChanged = false;
boolean rttModeChanged = false;
- if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) {
+ if (parcelableCall.getIsRttCallChanged()
+ && mDetails.hasProperty(Details.PROPERTY_RTT)) {
ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall();
InputStreamReader receiveStream = new InputStreamReader(
new ParcelFileDescriptor.AutoCloseInputStream(
@@ -1961,6 +2000,15 @@
}
}
+ /** {@hide} */
+ final void internalOnHandoverComplete() {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onHandoverComplete(call));
+ }
+ }
+
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 5fcff18..024bd30 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -29,7 +29,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -82,7 +81,7 @@
private int mConnectionProperties;
private String mDisconnectMessage;
private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
- private long mConnectElapsedTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
+ private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
private StatusHints mStatusHints;
private Bundle mExtras;
private Set<String> mPreviousExtraKeys;
@@ -584,30 +583,36 @@
}
/**
- * Sets the connection start time of the {@code Conference}. Should be specified in wall-clock
- * time returned by {@link System#currentTimeMillis()}.
+ * Sets the connection start time of the {@code Conference}. This is used in the call log to
+ * indicate the date and time when the conference took place.
+ * <p>
+ * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
* <p>
* When setting the connection time, you should always set the connection elapsed time via
- * {@link #setConnectionElapsedTime(long)}.
+ * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
*
- * @param connectionTimeMillis The connection time, in milliseconds.
+ * @param connectionTimeMillis The connection time, in milliseconds, as returned by
+ * {@link System#currentTimeMillis()}.
*/
public final void setConnectionTime(long connectionTimeMillis) {
mConnectTimeMillis = connectionTimeMillis;
}
/**
- * Sets the elapsed time since system boot when the {@link Conference} was connected.
- * This is used to determine the duration of the {@link Conference}.
+ * Sets the start time of the {@link Conference} which is the basis for the determining the
+ * duration of the {@link Conference}.
* <p>
- * When setting the connection elapsed time, you should always set the connection time via
+ * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
+ * zone changes do not impact the conference duration.
+ * <p>
+ * When setting this, you should also set the connection time via
* {@link #setConnectionTime(long)}.
*
- * @param connectionElapsedTime The connection time, as measured by
+ * @param connectionStartElapsedRealTime The connection time, as measured by
* {@link SystemClock#elapsedRealtime()}.
*/
- public final void setConnectionElapsedTime(long connectionElapsedTime) {
- mConnectElapsedTimeMillis = connectionElapsedTime;
+ public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
+ mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
}
/**
@@ -642,8 +647,8 @@
* @return The elapsed time at which the {@link Conference} was connected.
* @hide
*/
- public final long getConnectElapsedTime() {
- return mConnectElapsedTimeMillis;
+ public final long getConnectionStartElapsedRealTime() {
+ return mConnectionStartElapsedRealTime;
}
/**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index d71fde2..9a53d8c 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Notification;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
@@ -41,6 +40,8 @@
import android.util.ArraySet;
import android.view.Surface;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
@@ -328,8 +329,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x02000000
+ // Next CAPABILITY value: 0x04000000
//**********************************************************************************************
/**
@@ -397,9 +401,7 @@
/**
* Set by the framework to indicate that a connection has an active RTT session associated with
* it.
- * @hide
*/
- @TestApi
public static final int PROPERTY_IS_RTT = 1 << 8;
/**
@@ -729,6 +731,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
+ }
builder.append("]");
return builder.toString();
@@ -790,6 +795,10 @@
builder.append(isLong ? " PROPERTY_HAS_CDMA_VOICE_PRIVACY" : " priv");
}
+ if (can(properties, PROPERTY_IS_RTT)) {
+ builder.append(isLong ? " PROPERTY_IS_RTT" : " rtt");
+ }
+
builder.append("]");
return builder.toString();
}
@@ -837,9 +846,7 @@
/**
* Provides methods to read and write RTT data to/from the in-call app.
- * @hide
*/
- @TestApi
public static final class RttTextStream {
private static final int READ_BUFFER_SIZE = 1000;
private final InputStreamReader mPipeFromInCall;
@@ -855,18 +862,19 @@
mFdFromInCall = fromInCall;
mFdToInCall = toInCall;
mPipeFromInCall = new InputStreamReader(
- new ParcelFileDescriptor.AutoCloseInputStream(fromInCall));
+ new FileInputStream(fromInCall.getFileDescriptor()));
mPipeToInCall = new OutputStreamWriter(
- new ParcelFileDescriptor.AutoCloseOutputStream(toInCall));
+ new FileOutputStream(toInCall.getFileDescriptor()));
}
/**
* Writes the string {@param input} into the text stream to the UI for this RTT call. Since
* RTT transmits text in real-time, this method should be called as often as text snippets
* are received from the remote user, even if it is only one character.
- *
+ * <p>
* This method is not thread-safe -- calling it from multiple threads simultaneously may
* lead to interleaved text.
+ *
* @param input The message to send to the in-call app.
*/
public void write(String input) throws IOException {
@@ -879,9 +887,10 @@
* Reads a string from the in-call app, blocking if there is no data available. Returns
* {@code null} if the RTT conversation has been terminated and there is no further data
* to read.
- *
+ * <p>
* This method is not thread-safe -- calling it from multiple threads simultaneously may
* lead to interleaved text.
+ *
* @return A string containing text entered by the user, or {@code null} if the
* conversation has been terminated or if there was an error while reading.
*/
@@ -896,6 +905,7 @@
/**
* Non-blocking version of {@link #read()}. Returns {@code null} if there is nothing to
* be read.
+ *
* @return A string containing text entered by the user, or {@code null} if the user has
* not entered any new text yet.
*/
@@ -2292,7 +2302,7 @@
*
* @hide
*/
- public final void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
+ public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) {
mConnectElapsedTimeMillis = connectElapsedTimeMillis;
}
@@ -2612,36 +2622,27 @@
/**
* Informs listeners that a previously requested RTT session via
* {@link ConnectionRequest#isRequestingRtt()} or
- * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded.
- * @hide
+ * {@link #onStartRtt(RttTextStream)} has succeeded.
*/
- @TestApi
public final void sendRttInitiationSuccess() {
- setRttProperty();
mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
}
/**
* Informs listeners that a previously requested RTT session via
- * {@link ConnectionRequest#isRequestingRtt()} or
- * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)}
+ * {@link ConnectionRequest#isRequestingRtt()} or {@link #onStartRtt(RttTextStream)}
* has failed.
* @param reason One of the reason codes defined in {@link RttModifyStatus}, with the
* exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
- * @hide
*/
- @TestApi
public final void sendRttInitiationFailure(int reason) {
- unsetRttProperty();
mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
}
/**
* Informs listeners that a currently active RTT session has been terminated by the remote
* side of the coll.
- * @hide
*/
- @TestApi
public final void sendRttSessionRemotelyTerminated() {
mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
}
@@ -2649,9 +2650,7 @@
/**
* Informs listeners that the remote side of the call has requested an upgrade to include an
* RTT session in the call.
- * @hide
*/
- @TestApi
public final void sendRemoteRttRequest() {
mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this));
}
@@ -2731,7 +2730,20 @@
/**
* Notifies this Connection, which is in {@link #STATE_RINGING}, of
* a request to accept.
- *
+ * <p>
+ * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+ * the default dialer's {@link InCallService}.
+ * <p>
+ * Although a self-managed {@link ConnectionService} provides its own incoming call UI, the
+ * Telecom framework may request that the call is answered in the following circumstances:
+ * <ul>
+ * <li>The user chooses to answer an incoming call via a Bluetooth device.</li>
+ * <li>A car mode {@link InCallService} is in use which has declared
+ * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS} in its manifest. Such an
+ * {@link InCallService} will be able to see calls from self-managed
+ * {@link ConnectionService}s, and will be able to display an incoming call UI on their
+ * behalf.</li>
+ * </ul>
* @param videoState The video state in which to answer the connection.
*/
public void onAnswer(int videoState) {}
@@ -2739,6 +2751,20 @@
/**
* Notifies this Connection, which is in {@link #STATE_RINGING}, of
* a request to accept.
+ * <p>
+ * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+ * the default dialer's {@link InCallService}.
+ * <p>
+ * Although a self-managed {@link ConnectionService} provides its own incoming call UI, the
+ * Telecom framework may request that the call is answered in the following circumstances:
+ * <ul>
+ * <li>The user chooses to answer an incoming call via a Bluetooth device.</li>
+ * <li>A car mode {@link InCallService} is in use which has declared
+ * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS} in its manifest. Such an
+ * {@link InCallService} will be able to see calls from self-managed
+ * {@link ConnectionService}s, and will be able to display an incoming call UI on their
+ * behalf.</li>
+ * </ul>
*/
public void onAnswer() {
onAnswer(VideoProfile.STATE_AUDIO_ONLY);
@@ -2746,7 +2772,27 @@
/**
* Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to deflect.
+ */
+ public void onDeflect(Uri address) {}
+
+ /**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
* a request to reject.
+ * <p>
+ * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+ * the default dialer's {@link InCallService}.
+ * <p>
+ * Although a self-managed {@link ConnectionService} provides its own incoming call UI, the
+ * Telecom framework may request that the call is rejected in the following circumstances:
+ * <ul>
+ * <li>The user chooses to reject an incoming call via a Bluetooth device.</li>
+ * <li>A car mode {@link InCallService} is in use which has declared
+ * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS} in its manifest. Such an
+ * {@link InCallService} will be able to see calls from self-managed
+ * {@link ConnectionService}s, and will be able to display an incoming call UI on their
+ * behalf.</li>
+ * </ul>
*/
public void onReject() {}
@@ -2800,6 +2846,15 @@
public void onCallEvent(String event, Bundle extras) {}
/**
+ * Notifies this {@link Connection} that a handover has completed.
+ * <p>
+ * A handover is initiated with {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int,
+ * Bundle)} on the initiating side of the handover, and on the receiving side with
+ * {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)}.
+ */
+ public void onHandoverComplete() {}
+
+ /**
* Notifies this {@link Connection} of a change to the extras made outside the
* {@link ConnectionService}.
* <p>
@@ -2820,9 +2875,10 @@
* should show its own incoming call user interface.
* <p>
* Where there are ongoing calls in other self-managed {@link ConnectionService}s, or in a
- * regular {@link ConnectionService}, the Telecom framework will display its own incoming call
- * user interface to allow the user to choose whether to answer the new incoming call and
- * disconnect other ongoing calls, or to reject the new incoming call.
+ * regular {@link ConnectionService}, and it is not possible to hold these other calls, the
+ * Telecom framework will display its own incoming call user interface to allow the user to
+ * choose whether to answer the new incoming call and disconnect other ongoing calls, or to
+ * reject the new incoming call.
* <p>
* You should trigger the display of the incoming call user interface for your application by
* showing a {@link Notification} with a full-screen {@link Intent} specified.
@@ -2868,17 +2924,13 @@
* request, respectively.
* @param rttTextStream The object that should be used to send text to or receive text from
* the in-call app.
- * @hide
*/
- @TestApi
public void onStartRtt(@NonNull RttTextStream rttTextStream) {}
/**
* Notifies this {@link Connection} that it should terminate any existing RTT communication
* channel. No response to Telecom is needed for this method.
- * @hide
*/
- @TestApi
public void onStopRtt() {}
/**
@@ -2886,29 +2938,11 @@
* request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is
* indicated by the supplied {@link RttTextStream} being non-null, and rejection is
* indicated by {@code rttTextStream} being {@code null}
- * @hide
* @param rttTextStream The object that should be used to send text to or receive text from
* the in-call app.
*/
- @TestApi
public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
- /**
- * Internal method to set {@link #PROPERTY_IS_RTT}.
- * @hide
- */
- void setRttProperty() {
- setConnectionProperties(getConnectionProperties() | PROPERTY_IS_RTT);
- }
-
- /**
- * Internal method to un-set {@link #PROPERTY_IS_RTT}.
- * @hide
- */
- void unsetRttProperty() {
- setConnectionProperties(getConnectionProperties() & (~PROPERTY_IS_RTT));
- }
-
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index e169e5f..b6e6b0e 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.TestApi;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -144,6 +143,8 @@
private final boolean mShouldShowIncomingCallUi;
private final ParcelFileDescriptor mRttPipeToInCall;
private final ParcelFileDescriptor mRttPipeFromInCall;
+ // Cached return value of getRttTextStream -- we don't want to wrap it more than once.
+ private Connection.RttTextStream mRttTextStream;
/**
* @param accountHandle The accountHandle which should be used to place the call.
@@ -310,12 +311,13 @@
* send and receive RTT text to/from the in-call app.
* @return An instance of {@link android.telecom.Connection.RttTextStream}, or {@code null}
* if this connection request is not requesting an RTT session upon connection establishment.
- * @hide
*/
- @TestApi
public Connection.RttTextStream getRttTextStream() {
if (isRequestingRtt()) {
- return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
+ if (mRttTextStream == null) {
+ mRttTextStream = new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
+ }
+ return mRttTextStream;
} else {
return null;
}
@@ -324,9 +326,7 @@
/**
* Convenience method for determining whether the ConnectionRequest is requesting an RTT session
* @return {@code true} if RTT is requested, {@code false} otherwise.
- * @hide
*/
- @TestApi
public boolean isRequestingRtt() {
return mRttPipeFromInCall != null && mRttPipeToInCall != null;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4fd602f..593e12b 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -21,7 +21,6 @@
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -124,6 +123,7 @@
private static final String SESSION_ABORT = "CS.ab";
private static final String SESSION_ANSWER = "CS.an";
private static final String SESSION_ANSWER_VIDEO = "CS.anV";
+ private static final String SESSION_DEFLECT = "CS.def";
private static final String SESSION_REJECT = "CS.r";
private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
private static final String SESSION_SILENCE = "CS.s";
@@ -140,11 +140,15 @@
private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
+ private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
private static final String SESSION_START_RTT = "CS.+RTT";
+ private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT";
private static final String SESSION_STOP_RTT = "CS.-RTT";
private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
private static final String SESSION_HANDOVER_FAILED = "CS.haF";
+ private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
+ private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -174,7 +178,11 @@
private static final int MSG_ON_STOP_RTT = 27;
private static final int MSG_RTT_UPGRADE_RESPONSE = 28;
private static final int MSG_CREATE_CONNECTION_COMPLETE = 29;
+ private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30;
+ private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
private static final int MSG_HANDOVER_FAILED = 32;
+ private static final int MSG_HANDOVER_COMPLETE = 33;
+ private static final int MSG_DEFLECT = 34;
private static Connection sNullConnection;
@@ -294,6 +302,19 @@
}
@Override
+ public void handoverComplete(String callId, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_HANDOVER_COMPLETE);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_HANDOVER_COMPLETE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void abort(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_ABORT);
try {
@@ -334,6 +355,20 @@
}
@Override
+ public void deflect(String callId, Uri address, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_DEFLECT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = address;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void reject(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_REJECT);
try {
@@ -610,6 +645,26 @@
Log.endSession();
}
}
+
+ @Override
+ public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_LOST);
+ try {
+ mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_LOST).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_GAINED);
+ try {
+ mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_GAINED).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
};
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -808,6 +863,17 @@
}
break;
}
+ case MSG_DEFLECT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT);
+ try {
+ deflect((String) args.arg1, (Uri) args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_REJECT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
@@ -1004,6 +1070,19 @@
}
break;
}
+ case MSG_HANDOVER_COMPLETE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg2,
+ SESSION_HANDLER + SESSION_HANDOVER_COMPLETE);
+ String callId = (String) args.arg1;
+ notifyHandoverComplete(callId);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_ON_EXTRAS_CHANGED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -1061,6 +1140,12 @@
}
break;
}
+ case MSG_CONNECTION_SERVICE_FOCUS_GAINED:
+ onConnectionServiceFocusGained();
+ break;
+ case MSG_CONNECTION_SERVICE_FOCUS_LOST:
+ onConnectionServiceFocusLost();
+ break;
default:
break;
}
@@ -1415,19 +1500,24 @@
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
+ boolean isLegacyHandover = request.getExtras() != null &&
+ request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
+ boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
+ TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
- "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
- isIncoming,
- isUnknown);
+ "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
+ callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
+ isHandover);
Connection connection = null;
- if (getApplicationContext().getApplicationInfo().targetSdkVersion >
- Build.VERSION_CODES.O_MR1 && request.getExtras() != null &&
- request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER,false)) {
+ if (isHandover) {
+ PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
+ ? (PhoneAccountHandle) request.getExtras().getParcelable(
+ TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;
if (!isIncoming) {
- connection = onCreateOutgoingHandoverConnection(callManagerAccount, request);
+ connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
} else {
- connection = onCreateIncomingHandoverConnection(callManagerAccount, request);
+ connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
}
} else {
connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
@@ -1542,6 +1632,11 @@
findConnectionForAction(callId, "answer").onAnswer();
}
+ private void deflect(String callId, Uri address) {
+ Log.d(this, "deflect %s", callId);
+ findConnectionForAction(callId, "deflect").onDeflect(address);
+ }
+
private void reject(String callId) {
Log.d(this, "reject %s", callId);
findConnectionForAction(callId, "reject").onReject();
@@ -1724,6 +1819,19 @@
}
/**
+ * Notifies a {@link Connection} that a handover has completed.
+ *
+ * @param callId The ID of the call which completed handover.
+ */
+ private void notifyHandoverComplete(String callId) {
+ Log.d(this, "notifyHandoverComplete(%s)", callId);
+ Connection connection = findConnectionForAction(callId, "notifyHandoverComplete");
+ if (connection != null) {
+ connection.onHandoverComplete();
+ }
+ }
+
+ /**
* Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom.
* <p>
* These extra changes can originate from Telecom itself, or from an {@link InCallService} via
@@ -1757,7 +1865,6 @@
Log.d(this, "stopRtt(%s)", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "stopRtt").onStopRtt();
- findConnectionForAction(callId, "stopRtt").unsetRttProperty();
} else if (mConferenceById.containsKey(callId)) {
Log.w(this, "stopRtt called on a conference.");
}
@@ -1899,7 +2006,7 @@
null : conference.getVideoProvider().getInterface(),
conference.getVideoState(),
conference.getConnectTimeMillis(),
- conference.getConnectElapsedTime(),
+ conference.getConnectionStartElapsedRealTime(),
conference.getStatusHints(),
conference.getExtras());
@@ -1930,6 +2037,16 @@
}
/**
+ * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g
+ * microphone, camera).
+ *
+ * @see ConnectionService#onConnectionServiceFocusLost()
+ */
+ public final void connectionServiceFocusReleased() {
+ mAdapter.onConnectionServiceFocusReleased();
+ }
+
+ /**
* Adds a connection created by the {@link ConnectionService} and informs telecom of the new
* connection.
*
@@ -2179,6 +2296,20 @@
public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}
/**
+ * Called when the {@link ConnectionService} has lost the call focus.
+ * The {@link ConnectionService} should release the call resources and invokes
+ * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has
+ * released the call resources.
+ */
+ public void onConnectionServiceFocusLost() {}
+
+ /**
+ * Called when the {@link ConnectionService} has gained the call focus. The
+ * {@link ConnectionService} can acquire the call resources at this time.
+ */
+ public void onConnectionServiceFocusGained() {}
+
+ /**
* @hide
*/
public boolean containsConference(Conference conference) {
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 92a9dc2..0d319bb 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -628,4 +628,17 @@
}
}
}
+
+ /**
+ * Notifies Telecom that the {@link ConnectionService} has released the call resource.
+ */
+ void onConnectionServiceFocusReleased() {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ Log.d(this, "onConnectionServiceFocusReleased");
+ adapter.onConnectionServiceFocusReleased(Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 3fbdeb1..3e1bf77 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -73,6 +73,7 @@
private static final int MSG_ON_RTT_REMOTELY_TERMINATED = 32;
private static final int MSG_ON_RTT_UPGRADE_REQUEST = 33;
private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
+ private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
private final IConnectionServiceAdapter mDelegate;
@@ -329,6 +330,9 @@
}
break;
}
+ case MSG_CONNECTION_SERVICE_FOCUS_RELEASED:
+ mDelegate.onConnectionServiceFocusReleased(null /*Session.Info*/);
+ break;
}
}
};
@@ -601,6 +605,11 @@
args.arg2 = pHandle;
mHandler.obtainMessage(MSG_SET_PHONE_ACCOUNT_CHANGED, args).sendToTarget();
}
+
+ @Override
+ public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {
+ mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_RELEASED).sendToTarget();
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 4bc2a9b1..8678e33 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.net.Uri;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.RemoteException;
@@ -61,6 +62,19 @@
}
/**
+ * Instructs Telecom to deflect the specified call.
+ *
+ * @param callId The identifier of the call to deflect.
+ * @param address The address to deflect.
+ */
+ public void deflectCall(String callId, Uri address) {
+ try {
+ mAdapter.deflectCall(callId, address);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Instructs Telecom to reject the specified call.
*
* @param callId The identifier of the call to reject.
@@ -286,11 +300,12 @@
*
* @param callId The callId to send the event for.
* @param event The event.
+ * @param targetSdkVer Target sdk version of the app calling this api
* @param extras Extras associated with the event.
*/
- public void sendCallEvent(String callId, String event, Bundle extras) {
+ public void sendCallEvent(String callId, String event, int targetSdkVer, Bundle extras) {
try {
- mAdapter.sendCallEvent(callId, event, extras);
+ mAdapter.sendCallEvent(callId, event, targetSdkVer, extras);
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 74fa62d..af65c65 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -60,6 +60,26 @@
* </service>
* }
* </pre>
+ * <p>
+ * In addition to implementing the {@link InCallService} API, you must also declare an activity in
+ * your manifest which handles the {@link Intent#ACTION_DIAL} intent. The example below illustrates
+ * how this is done:
+ * <pre>
+ * {@code
+ * <activity android:name="your.package.YourDialerActivity"
+ * android:label="@string/yourDialerActivityLabel">
+ * <intent-filter>
+ * <action android:name="android.intent.action.DIAL" />
+ * <category android:name="android.intent.category.DEFAULT" />
+ * </intent-filter>
+ * </activity>
+ * }
+ * </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.
*/
public abstract class InCallService extends Service {
@@ -81,6 +101,7 @@
private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10;
private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
private static final int MSG_ON_HANDOVER_FAILED = 12;
+ private static final int MSG_ON_HANDOVER_COMPLETE = 13;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -157,6 +178,11 @@
mPhone.internalOnHandoverFailed(callId, error);
break;
}
+ case MSG_ON_HANDOVER_COMPLETE: {
+ String callId = (String) msg.obj;
+ mPhone.internalOnHandoverComplete(callId);
+ break;
+ }
default:
break;
}
@@ -237,6 +263,11 @@
public void onHandoverFailed(String callId, int error) {
mHandler.obtainMessage(MSG_ON_HANDOVER_FAILED, error, 0, callId).sendToTarget();
}
+
+ @Override
+ public void onHandoverComplete(String callId) {
+ mHandler.obtainMessage(MSG_ON_HANDOVER_COMPLETE, callId).sendToTarget();
+ }
}
private Phone.Listener mPhoneListener = new Phone.Listener() {
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 3361b5b..83ca470 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -340,24 +340,6 @@
return sSessionManager;
}
- private static MessageDigest sMessageDigest;
-
- public static void initMd5Sum() {
- new AsyncTask<Void, Void, Void>() {
- @Override
- public Void doInBackground(Void... args) {
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- md = null;
- }
- sMessageDigest = md;
- return null;
- }
- }.execute();
- }
-
public static void setTag(String tag) {
TAG = tag;
DEBUG = isLoggable(android.util.Log.DEBUG);
@@ -425,44 +407,13 @@
/**
* Redact personally identifiable information for production users.
* If we are running in verbose mode, return the original string,
- * and return "****" if we are running on the user build, otherwise
- * return a SHA-1 hash of the input string.
+ * and return "***" otherwise.
*/
public static String pii(Object pii) {
if (pii == null || VERBOSE) {
return String.valueOf(pii);
}
- return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
- }
-
- private static String secureHash(byte[] input) {
- // Refrain from logging user personal information in user build.
- if (USER_BUILD) {
- return "****";
- }
-
- if (sMessageDigest != null) {
- sMessageDigest.reset();
- sMessageDigest.update(input);
- byte[] result = sMessageDigest.digest();
- return encodeHex(result);
- } else {
- return "Uninitialized SHA1";
- }
- }
-
- private static String encodeHex(byte[] bytes) {
- StringBuffer hex = new StringBuffer(bytes.length * 2);
-
- for (int i = 0; i < bytes.length; i++) {
- int byteIntValue = bytes[i] & 0xff;
- if (byteIntValue < 0x10) {
- hex.append("0");
- }
- hex.append(Integer.toString(byteIntValue, 16));
- }
-
- return hex.toString();
+ return "***";
}
private static String getPrefixFromObject(Object obj) {
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index b5394b9..99f94f2 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -230,6 +230,13 @@
}
}
+ final void internalOnHandoverComplete(String callId) {
+ Call call = mCallByTelecomCallId.get(callId);
+ if (call != null) {
+ call.internalOnHandoverComplete();
+ }
+ }
+
/**
* Called to destroy the phone and cleanup any lingering calls.
*/
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index fcfc593..95eb14a 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -134,6 +134,25 @@
"android.telecom.extra.LOG_SELF_MANAGED_CALLS";
/**
+ * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+ * indicates whether calls for a {@link PhoneAccount} should generate a "call recording tone"
+ * when the user is recording audio on the device.
+ * <p>
+ * The call recording tone is played over the telephony audio stream so that the remote party
+ * has an audible indication that it is possible their call is being recorded using a call
+ * recording app on the device.
+ * <p>
+ * This extra only has an effect for calls placed via Telephony (e.g.
+ * {@link #CAPABILITY_SIM_SUBSCRIPTION}).
+ * <p>
+ * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is
+ * in progress.
+ * @hide
+ */
+ public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
+ "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 85906ad..59ce590 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -213,6 +213,9 @@
}
@Override
+ public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {}
+
+ @Override
public void addConferenceCall(
final String callId, ParcelableConference parcel, Session.Info sessionInfo) {
RemoteConference conference = new RemoteConference(callId,
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e124750..c848f77 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -236,6 +237,15 @@
"android.telecom.extra.INCOMING_CALL_EXTRAS";
/**
+ * Optional extra for {@link #ACTION_INCOMING_CALL} containing a boolean to indicate that the
+ * call has an externally generated ringer. Used by the HfpClientConnectionService when In Band
+ * Ringtone is enabled to prevent two ringers from being generated.
+ * @hide
+ */
+ public static final String EXTRA_CALL_EXTERNAL_RINGER =
+ "android.telecom.extra.CALL_EXTERNAL_RINGER";
+
+ /**
* Optional extra for {@link android.content.Intent#ACTION_CALL} and
* {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
* which contains metadata about the call. This {@link Bundle} will be saved into
@@ -369,6 +379,17 @@
public static final String EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER";
/**
+ * When {@code true} indicates that a request to create a new connection is for the purpose of
+ * a handover. Note: This is used with the
+ * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)} API as part of the
+ * internal communication mechanism with the {@link android.telecom.ConnectionService}. It is
+ * not the same as the legacy {@link #EXTRA_IS_HANDOVER} extra.
+ * @hide
+ */
+ public static final String EXTRA_IS_HANDOVER_CONNECTION =
+ "android.telecom.extra.IS_HANDOVER_CONNECTION";
+
+ /**
* Parcelable extra used with {@link #EXTRA_IS_HANDOVER} to indicate the source
* {@link PhoneAccountHandle} when initiating a handover which {@link ConnectionService}
* the handover is from.
@@ -645,7 +666,6 @@
mContext = context;
}
mTelecomServiceOverride = telecomServiceImpl;
- android.telecom.Log.initMd5Sum();
}
/**
@@ -1281,7 +1301,7 @@
public boolean endCall() {
try {
if (isServiceConnected()) {
- return getTelecomService().endCall();
+ return getTelecomService().endCall(mContext.getPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#endCall", e);
@@ -1424,6 +1444,13 @@
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
try {
if (isServiceConnected()) {
+ if (extras != null && extras.getBoolean(EXTRA_IS_HANDOVER) &&
+ mContext.getApplicationContext().getApplicationInfo().targetSdkVersion >
+ Build.VERSION_CODES.O_MR1) {
+ Log.e("TAG", "addNewIncomingCall failed. Use public api " +
+ "acceptHandover for API > O-MR1");
+ // TODO add "return" after DUO team adds support for new handover API
+ }
getTelecomService().addNewIncomingCall(
phoneAccount, extras == null ? new Bundle() : extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 732d00d..04f057d 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
@@ -58,6 +59,8 @@
void answer(String callId, in Session.Info sessionInfo);
+ void deflect(String callId, in Uri address, in Session.Info sessionInfo);
+
void reject(String callId, in Session.Info sessionInfo);
void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
@@ -103,4 +106,10 @@
void handoverFailed(String callId, in ConnectionRequest request,
int error, in Session.Info sessionInfo);
+
+ void handoverComplete(String callId, in Session.Info sessionInfo);
+
+ void connectionServiceFocusLost(in Session.Info sessionInfo);
+
+ void connectionServiceFocusGained(in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index da2015f..be474bd 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -119,4 +119,6 @@
void onPhoneAccountChanged(String callId, in PhoneAccountHandle pHandle,
in Session.Info sessionInfo);
+
+ void onConnectionServiceFocusReleased(in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 23ac940..57df5c1 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
@@ -29,6 +30,8 @@
oneway interface IInCallAdapter {
void answerCall(String callId, int videoState);
+ void deflectCall(String callId, in Uri address);
+
void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
void disconnectCall(String callId);
@@ -64,7 +67,7 @@
void pullExternalCall(String callId);
- void sendCallEvent(String callId, String event, in Bundle extras);
+ void sendCallEvent(String callId, String event, int targetSdkVer, in Bundle extras);
void putExtras(String callId, in Bundle extras);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index 110109e..b9563fa 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -56,4 +56,6 @@
void onRttInitiationFailure(String callId, int reason);
void onHandoverFailed(String callId, int error);
+
+ void onHandoverComplete(String callId);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 3460754..b4e7d56 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -187,7 +187,7 @@
/**
* @see TelecomServiceImpl#endCall
*/
- boolean endCall();
+ boolean endCall(String callingPackage);
/**
* @see TelecomServiceImpl#acceptRingingCall
diff --git a/telephony/OWNERS b/telephony/OWNERS
new file mode 100644
index 0000000..6f67bc2
--- /dev/null
+++ b/telephony/OWNERS
@@ -0,0 +1,14 @@
+set noparent
+
+tgunn@google.com
+breadley@google.com
+hallliu@google.com
+rgreenwalt@google.com
+mpq@google.com
+amitmahajan@google.com
+fionaxu@google.com
+jackyu@google.com
+jminjie@google.com
+satk@google.com
+shuoq@google.com
+refuhoo@google.com
diff --git a/telephony/java/android/telephony/RadioNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
similarity index 89%
rename from telephony/java/android/telephony/RadioNetworkConstants.java
rename to telephony/java/android/telephony/AccessNetworkConstants.java
index 5f5dd82..7cd16128 100644
--- a/telephony/java/android/telephony/RadioNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -16,24 +16,39 @@
package android.telephony;
-/**
- * Contains radio access network related constants.
- */
-public final class RadioNetworkConstants {
+import android.annotation.SystemApi;
- public static final class RadioAccessNetworks {
+/**
+ * Contains access network related constants.
+ */
+public final class AccessNetworkConstants {
+
+ public static final class AccessNetworkType {
+ public static final int UNKNOWN = 0;
public static final int GERAN = 1;
public static final int UTRAN = 2;
public static final int EUTRAN = 3;
- /** @hide */
public static final int CDMA2000 = 4;
+ public static final int IWLAN = 5;
+ }
+
+ /**
+ * Wireless transportation type
+ * @hide
+ */
+ @SystemApi
+ public static final class TransportType {
+ /** 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;
}
/**
* Frenquency bands for GERAN.
* http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
*/
- public static final class GeranBands {
+ public static final class GeranBand {
public static final int BAND_T380 = 1;
public static final int BAND_T410 = 2;
public static final int BAND_450 = 3;
@@ -54,7 +69,7 @@
* Frenquency bands for UTRAN.
* http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf
*/
- public static final class UtranBands {
+ public static final class UtranBand {
public static final int BAND_1 = 1;
public static final int BAND_2 = 2;
public static final int BAND_3 = 3;
@@ -83,7 +98,7 @@
* Frenquency bands for EUTRAN.
* http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf
*/
- public static final class EutranBands {
+ public static final class EutranBand {
public static final int BAND_1 = 1;
public static final int BAND_2 = 2;
public static final int BAND_3 = 3;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 514222d..de75ac4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -27,8 +27,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.carrier.CarrierService;
+import android.telephony.ims.ImsReasonInfo;
-import com.android.ims.ImsReasonInfo;
import com.android.internal.telephony.ICarrierConfigLoader;
/**
@@ -39,13 +39,29 @@
private final static String TAG = "CarrierConfigManager";
/**
+ * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the slot index that the
+ * broadcast is for.
+ */
+ public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+
+ /**
+ * Optional extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the
+ * subscription index that the broadcast is for, if a valid one is available.
+ */
+ public static final String EXTRA_SUBSCRIPTION_INDEX =
+ SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX;
+
+ /**
* @hide
*/
public CarrierConfigManager() {
}
/**
- * This intent is broadcast by the system when carrier config changes.
+ * This intent is broadcast by the system when carrier config changes. An int is specified in
+ * {@link #EXTRA_SLOT_INDEX} to indicate the slot index that this is for. An optional int extra
+ * {@link #EXTRA_SUBSCRIPTION_INDEX} is included to indicate the subscription index if a valid
+ * one is available for the slot index.
*/
public static final String
ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
@@ -61,6 +77,14 @@
public static final String
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ /**
+ * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+ * true means visible. false means gone.
+ * @hide
+ */
+ public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
+ "call_barring_visibility_bool";
+
/**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
@@ -130,6 +154,15 @@
public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
/**
+ * Determines if the carrier requires that a tone be played to the remote party when an app is
+ * recording audio during a call (e.g. using a call recording app).
+ * <p>
+ * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true
+ * in order to work.
+ * @hide
+ */
+ public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
+ /**
* Determines if the carrier requires converting the destination number before sending out an
* SMS. Certain networks and numbering plans require different formats.
*/
@@ -352,6 +385,17 @@
public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
/**
+ * Flag that specifies to use the user's own phone number as the voicemail number when there is
+ * no pre-loaded voicemail number on the SIM card.
+ * <p>
+ * {@link #KEY_DEFAULT_VM_NUMBER_STRING} takes precedence over this flag.
+ * <p>
+ * If false, the system default (*86) will be used instead.
+ */
+ public static final String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL =
+ "config_telephony_use_own_number_for_voicemail_bool";
+
+ /**
* When {@code true}, changes to the mobile data enabled switch will not cause the VT
* registration state to change. That is, turning on or off mobile data will not cause VT to be
* enabled or disabled.
@@ -464,6 +508,20 @@
public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL
= "carrier_volte_override_wfc_provisioning_bool";
+ /**
+ * Override the device's configuration for the cellular data service to use for this SIM card.
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_data_service_wwan_package_override_string";
+
+ /**
+ * Override the device's configuration for the IWLAN data service to use for this SIM card.
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_data_service_wlan_package_override_string";
+
/** Flag specifying whether VoLTE TTY is supported. */
public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
= "carrier_volte_tty_supported_bool";
@@ -939,8 +997,9 @@
public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
/**
- * String to identify carrier name in CarrierConfig app. This string is used only if
- * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true
+ * String to identify carrier name in CarrierConfig app. This string overrides SPN if
+ * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true; otherwise, it will be used if its value is provided
+ * and SPN is unavailable
* @hide
*/
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
@@ -995,6 +1054,13 @@
public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL =
"always_show_data_rat_icon_bool";
+ /**
+ * Boolean to decide whether to show precise call failed cause to user
+ * @hide
+ */
+ public static final String KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL =
+ "show_precise_failed_cause_bool";
+
// These variables are used by the MMS service and exposed through another API, {@link
// SmsManager}. The variable names and string values are copied from there.
public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -1304,6 +1370,12 @@
*/
public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+ /**
+ * Flag indicating whether the carrier supports call deflection for an incoming IMS call.
+ * @hide
+ */
+ public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
+ "carrier_allow_deflect_ims_call_bool";
/**
* Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
@@ -1340,6 +1412,14 @@
public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
/**
+ * When true, indicates that the HD audio icon in the in-call screen should be shown for
+ * GSM/CDMA calls.
+ * @hide
+ */
+ public static final String KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO =
+ "gsm_cdma_calls_can_be_hd_audio";
+
+ /**
* Whether system apps are allowed to use fallback if carrier video call is not available.
* Defaults to {@code true}.
*
@@ -1349,7 +1429,7 @@
"allow_video_calling_fallback_bool";
/**
- * Defines operator-specific {@link com.android.ims.ImsReasonInfo} mappings.
+ * Defines operator-specific {@link ImsReasonInfo} mappings.
*
* Format: "ORIGINAL_CODE|MESSAGE|NEW_CODE"
* Where {@code ORIGINAL_CODE} corresponds to a {@link ImsReasonInfo#getCode()} code,
@@ -1719,16 +1799,41 @@
*/
public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
+ /**
+ * List of thresholds of RSRP for determining the display level of LTE signal bar.
+ * @hide
+ */
+ public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
+ "lte_rsrp_thresholds_int_array";
+
+ /**
+ * Decides when clients try to bind to iwlan network service, which package name will
+ * the binding intent go to.
+ * @hide
+ */
+ public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_network_service_wlan_package_override_string";
+
+ /**
+ * Decides when clients try to bind to wwan (cellular) network service, which package name will
+ * the binding intent go to.
+ * @hide
+ */
+ public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
+ = "carrier_network_service_wwan_package_override_string";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
static {
sDefaults = new PersistableBundle();
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
+ sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false);
sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
@@ -1737,6 +1842,7 @@
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
+ sDefaults.putBoolean(KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
@@ -1749,11 +1855,15 @@
sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
- sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+ sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+ sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
+ sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+ sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
@@ -1767,6 +1877,7 @@
sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+ sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
@@ -1966,6 +2077,7 @@
sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+ sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true);
sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
@@ -2000,9 +2112,19 @@
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
+ sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+ new int[] {
+ -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
+ -128, /* SIGNAL_STRENGTH_POOR */
+ -118, /* SIGNAL_STRENGTH_MODERATE */
+ -108, /* SIGNAL_STRENGTH_GOOD */
+ -98, /* SIGNAL_STRENGTH_GREAT */
+ -44
+ });
}
/**
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentity.aidl
similarity index 90%
rename from telephony/java/android/telephony/data/InterfaceAddress.aidl
rename to telephony/java/android/telephony/CellIdentity.aidl
index d750363..aeee769 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentity.aidl
@@ -15,6 +15,6 @@
*/
/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+parcelable CellIdentity;
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
new file mode 100644
index 0000000..e092d52
--- /dev/null
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * CellIdentity represents the identity of a unique cell. This is the base class for
+ * CellIdentityXxx which represents cell identity for specific network access technology.
+ */
+public abstract class CellIdentity implements Parcelable {
+ /**
+ * Cell identity type
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "TYPE_", value = {TYPE_GSM, TYPE_CDMA, TYPE_LTE, TYPE_WCDMA, TYPE_TDSCDMA})
+ public @interface Type {}
+
+ /**
+ * Unknown cell identity type
+ * @hide
+ */
+ public static final int TYPE_UNKNOWN = 0;
+ /**
+ * GSM cell identity type
+ * @hide
+ */
+ public static final int TYPE_GSM = 1;
+ /**
+ * CDMA cell identity type
+ * @hide
+ */
+ public static final int TYPE_CDMA = 2;
+ /**
+ * LTE cell identity type
+ * @hide
+ */
+ public static final int TYPE_LTE = 3;
+ /**
+ * WCDMA cell identity type
+ * @hide
+ */
+ public static final int TYPE_WCDMA = 4;
+ /**
+ * TDS-CDMA cell identity type
+ * @hide
+ */
+ public static final int TYPE_TDSCDMA = 5;
+
+ // Log tag
+ /** @hide */
+ protected final String mTag;
+ // Cell identity type
+ /** @hide */
+ protected final int mType;
+ // 3-digit Mobile Country Code in string format. Null for CDMA cell identity.
+ /** @hide */
+ protected final String mMccStr;
+ // 2 or 3-digit Mobile Network Code in string format. Null for CDMA cell identity.
+ /** @hide */
+ protected final String mMncStr;
+
+ /** @hide */
+ protected CellIdentity(String tag, int type, String mcc, String mnc) {
+ mTag = tag;
+ mType = type;
+
+ // Only allow INT_MAX if unknown string mcc/mnc
+ if (mcc == null || mcc.matches("^[0-9]{3}$")) {
+ mMccStr = mcc;
+ } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mccStr is empty or unknown, set it as null.
+ mMccStr = null;
+ } else {
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+ // after the bug got fixed.
+ mMccStr = null;
+ log("invalid MCC format: " + mcc);
+ }
+
+ if (mnc == null || mnc.matches("^[0-9]{2,3}$")) {
+ mMncStr = mnc;
+ } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mncStr is empty or unknown, set it as null.
+ mMncStr = null;
+ } else {
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+ // after the bug got fixed.
+ mMncStr = null;
+ log("invalid MNC format: " + mnc);
+ }
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ * @return The type of the cell identity
+ */
+ public @Type int getType() { return mType; }
+
+ /**
+ * Used by child classes for parceling.
+ *
+ * @hide
+ */
+ @CallSuper
+ public void writeToParcel(Parcel dest, int type) {
+ dest.writeInt(type);
+ dest.writeString(mMccStr);
+ dest.writeString(mMncStr);
+ }
+
+ /**
+ * Construct from Parcel
+ * @hide
+ */
+ protected CellIdentity(String tag, int type, Parcel source) {
+ this(tag, type, source.readString(), source.readString());
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellIdentity> CREATOR =
+ new Creator<CellIdentity>() {
+ @Override
+ public CellIdentity createFromParcel(Parcel in) {
+ int type = in.readInt();
+ switch (type) {
+ case TYPE_GSM: return CellIdentityGsm.createFromParcelBody(in);
+ case TYPE_WCDMA: return CellIdentityWcdma.createFromParcelBody(in);
+ case TYPE_CDMA: return CellIdentityCdma.createFromParcelBody(in);
+ case TYPE_LTE: return CellIdentityLte.createFromParcelBody(in);
+ case TYPE_TDSCDMA: return CellIdentityTdscdma.createFromParcelBody(in);
+ default: throw new IllegalArgumentException("Bad Cell identity Parcel");
+ }
+ }
+
+ @Override
+ public CellIdentity[] newArray(int size) {
+ return new CellIdentity[size];
+ }
+ };
+
+ /** @hide */
+ protected void log(String s) {
+ Rlog.w(mTag, s);
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentityCdma.aidl
similarity index 90%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/CellIdentityCdma.aidl
index d750363..b31ad0b 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentityCdma.aidl
@@ -15,6 +15,6 @@
*/
/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+parcelable CellIdentityCdma;
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index ddc938e..2e1d1dc 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -17,8 +17,6 @@
package android.telephony;
import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
import android.text.TextUtils;
import java.util.Objects;
@@ -26,9 +24,8 @@
/**
* CellIdentity is to represent a unique CDMA cell
*/
-public final class CellIdentityCdma implements Parcelable {
-
- private static final String LOG_TAG = "CellSignalStrengthCdma";
+public final class CellIdentityCdma extends CellIdentity {
+ private static final String TAG = CellIdentityCdma.class.getSimpleName();
private static final boolean DBG = false;
// Network Id 0..65535
@@ -60,6 +57,7 @@
* @hide
*/
public CellIdentityCdma() {
+ super(TAG, TYPE_CDMA, null, null);
mNetworkId = Integer.MAX_VALUE;
mSystemId = Integer.MAX_VALUE;
mBasestationId = Integer.MAX_VALUE;
@@ -81,7 +79,7 @@
*
* @hide
*/
- public CellIdentityCdma (int nid, int sid, int bid, int lon, int lat) {
+ public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat) {
this(nid, sid, bid, lon, lat, null, null);
}
@@ -99,8 +97,9 @@
*
* @hide
*/
- public CellIdentityCdma (int nid, int sid, int bid, int lon, int lat, String alphal,
+ public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
String alphas) {
+ super(TAG, TYPE_CDMA, null, null);
mNetworkId = nid;
mSystemId = sid;
mBasestationId = bid;
@@ -196,40 +195,33 @@
CellIdentityCdma o = (CellIdentityCdma) other;
- return mNetworkId == o.mNetworkId &&
- mSystemId == o.mSystemId &&
- mBasestationId == o.mBasestationId &&
- mLatitude == o.mLatitude &&
- mLongitude == o.mLongitude &&
- TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
- TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ return mNetworkId == o.mNetworkId
+ && mSystemId == o.mSystemId
+ && mBasestationId == o.mBasestationId
+ && mLatitude == o.mLatitude
+ && mLongitude == o.mLongitude
+ && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+ && TextUtils.equals(mAlphaShort, o.mAlphaShort);
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CellIdentityCdma:{");
- sb.append(" mNetworkId="); sb.append(mNetworkId);
- sb.append(" mSystemId="); sb.append(mSystemId);
- sb.append(" mBasestationId="); sb.append(mBasestationId);
- sb.append(" mLongitude="); sb.append(mLongitude);
- sb.append(" mLatitude="); sb.append(mLatitude);
- sb.append(" mAlphaLong="); sb.append(mAlphaLong);
- sb.append(" mAlphaShort="); sb.append(mAlphaShort);
- sb.append("}");
-
- return sb.toString();
- }
-
- /** Implement the Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
+ return new StringBuilder(TAG)
+ .append(":{ mNetworkId=").append(mNetworkId)
+ .append(" mSystemId=").append(mSystemId)
+ .append(" mBasestationId=").append(mBasestationId)
+ .append(" mLongitude=").append(mLongitude)
+ .append(" mLatitude=").append(mLatitude)
+ .append(" mAlphaLong=").append(mAlphaLong)
+ .append(" mAlphaShort=").append(mAlphaShort)
+ .append("}").toString();
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ super.writeToParcel(dest, TYPE_CDMA);
dest.writeInt(mNetworkId);
dest.writeInt(mSystemId);
dest.writeInt(mBasestationId);
@@ -241,10 +233,16 @@
/** Construct from Parcel, type has already been processed */
private CellIdentityCdma(Parcel in) {
- this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(),
- in.readString(), in.readString());
+ super(TAG, TYPE_CDMA, in);
+ mNetworkId = in.readInt();
+ mSystemId = in.readInt();
+ mBasestationId = in.readInt();
+ mLongitude = in.readInt();
+ mLatitude = in.readInt();
+ mAlphaLong = in.readString();
+ mAlphaShort = in.readString();
- if (DBG) log("CellIdentityCdma(Parcel): " + toString());
+ if (DBG) log(toString());
}
/** Implement the Parcelable interface */
@@ -253,7 +251,8 @@
new Creator<CellIdentityCdma>() {
@Override
public CellIdentityCdma createFromParcel(Parcel in) {
- return new CellIdentityCdma(in);
+ in.readInt(); // skip
+ return createFromParcelBody(in);
}
@Override
@@ -262,10 +261,8 @@
}
};
- /**
- * log
- */
- private static void log(String s) {
- Rlog.w(LOG_TAG, s);
+ /** @hide */
+ protected static CellIdentityCdma createFromParcelBody(Parcel in) {
+ return new CellIdentityCdma(in);
}
}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentityGsm.aidl
similarity index 90%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/CellIdentityGsm.aidl
index d750363..bcc0751 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentityGsm.aidl
@@ -15,6 +15,6 @@
*/
/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+parcelable CellIdentityGsm;
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 376e6aa..f948f81 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -17,8 +17,6 @@
package android.telephony;
import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
import android.text.TextUtils;
import java.util.Objects;
@@ -26,9 +24,8 @@
/**
* CellIdentity to represent a unique GSM cell
*/
-public final class CellIdentityGsm implements Parcelable {
-
- private static final String LOG_TAG = "CellIdentityGsm";
+public final class CellIdentityGsm extends CellIdentity {
+ private static final String TAG = CellIdentityGsm.class.getSimpleName();
private static final boolean DBG = false;
// 16-bit Location Area Code, 0..65535
@@ -39,10 +36,6 @@
private final int mArfcn;
// 6-bit Base Station Identity Code
private final int mBsic;
- // 3-digit Mobile Country Code in string format
- private final String mMccStr;
- // 2 or 3-digit Mobile Network Code in string format
- private final String mMncStr;
// long alpha Operator Name String or Enhanced Operator Name String
private final String mAlphaLong;
// short alpha Operator Name String or Enhanced Operator Name String
@@ -52,12 +45,11 @@
* @hide
*/
public CellIdentityGsm() {
+ super(TAG, TYPE_GSM, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mArfcn = Integer.MAX_VALUE;
mBsic = Integer.MAX_VALUE;
- mMccStr = null;
- mMncStr = null;
mAlphaLong = null;
mAlphaShort = null;
}
@@ -70,7 +62,7 @@
*
* @hide
*/
- public CellIdentityGsm (int mcc, int mnc, int lac, int cid) {
+ public CellIdentityGsm(int mcc, int mnc, int lac, int cid) {
this(lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE,
String.valueOf(mcc), String.valueOf(mnc), null, null);
}
@@ -86,7 +78,7 @@
*
* @hide
*/
- public CellIdentityGsm (int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
+ public CellIdentityGsm(int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
this(lac, cid, arfcn, bsic, String.valueOf(mcc), String.valueOf(mnc), null, null);
}
@@ -103,8 +95,9 @@
*
* @hide
*/
- public CellIdentityGsm (int lac, int cid, int arfcn, int bsic, String mccStr,
+ public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
String mncStr, String alphal, String alphas) {
+ super(TAG, TYPE_GSM, mccStr, mncStr);
mLac = lac;
mCid = cid;
mArfcn = arfcn;
@@ -112,31 +105,6 @@
// for inbound parcels
mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
- // Only allow INT_MAX if unknown string mcc/mnc
- if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
- mMccStr = mccStr;
- } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
- // If the mccStr is empty or unknown, set it as null.
- mMccStr = null;
- } else {
- // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
- // after the bug got fixed.
- mMccStr = null;
- log("invalid MCC format: " + mccStr);
- }
-
- if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
- mMncStr = mncStr;
- } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
- // If the mncStr is empty or unknown, set it as null.
- mMncStr = null;
- } else {
- // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
- // after the bug got fixed.
- mMncStr = null;
- log("invalid MNC format: " + mncStr);
- }
-
mAlphaLong = alphal;
mAlphaShort = alphas;
}
@@ -237,6 +205,7 @@
/**
+ * @deprecated Primary Scrambling Code is not applicable to GSM.
* @return Integer.MAX_VALUE, undefined for GSM
*/
@Deprecated
@@ -260,58 +229,54 @@
}
CellIdentityGsm o = (CellIdentityGsm) other;
- return mLac == o.mLac &&
- mCid == o.mCid &&
- mArfcn == o.mArfcn &&
- mBsic == o.mBsic &&
- TextUtils.equals(mMccStr, o.mMccStr) &&
- TextUtils.equals(mMncStr, o.mMncStr) &&
- TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
- TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ return mLac == o.mLac
+ && mCid == o.mCid
+ && mArfcn == o.mArfcn
+ && mBsic == o.mBsic
+ && TextUtils.equals(mMccStr, o.mMccStr)
+ && TextUtils.equals(mMncStr, o.mMncStr)
+ && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+ && TextUtils.equals(mAlphaShort, o.mAlphaShort);
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CellIdentityGsm:{");
- sb.append(" mLac=").append(mLac);
- sb.append(" mCid=").append(mCid);
- sb.append(" mArfcn=").append(mArfcn);
- sb.append(" mBsic=").append("0x").append(Integer.toHexString(mBsic));
- sb.append(" mMcc=").append(mMccStr);
- sb.append(" mMnc=").append(mMncStr);
- sb.append(" mAlphaLong=").append(mAlphaLong);
- sb.append(" mAlphaShort=").append(mAlphaShort);
- sb.append("}");
-
- return sb.toString();
- }
-
- /** Implement the Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
+ return new StringBuilder(TAG)
+ .append(":{ mLac=").append(mLac)
+ .append(" mCid=").append(mCid)
+ .append(" mArfcn=").append(mArfcn)
+ .append(" mBsic=").append("0x").append(Integer.toHexString(mBsic))
+ .append(" mMcc=").append(mMccStr)
+ .append(" mMnc=").append(mMncStr)
+ .append(" mAlphaLong=").append(mAlphaLong)
+ .append(" mAlphaShort=").append(mAlphaShort)
+ .append("}").toString();
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ super.writeToParcel(dest, TYPE_GSM);
dest.writeInt(mLac);
dest.writeInt(mCid);
dest.writeInt(mArfcn);
dest.writeInt(mBsic);
- dest.writeString(mMccStr);
- dest.writeString(mMncStr);
dest.writeString(mAlphaLong);
dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
private CellIdentityGsm(Parcel in) {
- this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readString(),
- in.readString(), in.readString(), in.readString());
+ super(TAG, TYPE_GSM, in);
+ mLac = in.readInt();
+ mCid = in.readInt();
+ mArfcn = in.readInt();
+ mBsic = in.readInt();
+ mAlphaLong = in.readString();
+ mAlphaShort = in.readString();
- if (DBG) log("CellIdentityGsm(Parcel): " + toString());
+ if (DBG) log(toString());
}
/** Implement the Parcelable interface */
@@ -320,7 +285,8 @@
new Creator<CellIdentityGsm>() {
@Override
public CellIdentityGsm createFromParcel(Parcel in) {
- return new CellIdentityGsm(in);
+ in.readInt(); // skip
+ return createFromParcelBody(in);
}
@Override
@@ -329,10 +295,8 @@
}
};
- /**
- * log
- */
- private static void log(String s) {
- Rlog.w(LOG_TAG, s);
+ /** @hide */
+ protected static CellIdentityGsm createFromParcelBody(Parcel in) {
+ return new CellIdentityGsm(in);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentityLte.aidl
similarity index 90%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/CellIdentityLte.aidl
index d750363..940d170 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentityLte.aidl
@@ -15,6 +15,6 @@
*/
/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+parcelable CellIdentityLte;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 6ca5daf6..5f1f448 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -17,8 +17,6 @@
package android.telephony;
import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
import android.text.TextUtils;
import java.util.Objects;
@@ -26,9 +24,8 @@
/**
* CellIdentity is to represent a unique LTE cell
*/
-public final class CellIdentityLte implements Parcelable {
-
- private static final String LOG_TAG = "CellIdentityLte";
+public final class CellIdentityLte extends CellIdentity {
+ private static final String TAG = CellIdentityLte.class.getSimpleName();
private static final boolean DBG = false;
// 28-bit cell identity
@@ -39,25 +36,23 @@
private final int mTac;
// 18-bit Absolute RF Channel Number
private final int mEarfcn;
- // 3-digit Mobile Country Code in string format
- private final String mMccStr;
- // 2 or 3-digit Mobile Network Code in string format
- private final String mMncStr;
// long alpha Operator Name String or Enhanced Operator Name String
private final String mAlphaLong;
// short alpha Operator Name String or Enhanced Operator Name String
private final String mAlphaShort;
+ // cell bandwidth, in kHz
+ private final int mBandwidth;
/**
* @hide
*/
public CellIdentityLte() {
+ super(TAG, TYPE_LTE, null, null);
mCi = Integer.MAX_VALUE;
mPci = Integer.MAX_VALUE;
mTac = Integer.MAX_VALUE;
mEarfcn = Integer.MAX_VALUE;
- mMccStr = null;
- mMncStr = null;
+ mBandwidth = Integer.MAX_VALUE;
mAlphaLong = null;
mAlphaShort = null;
}
@@ -72,8 +67,9 @@
*
* @hide
*/
- public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac) {
- this(ci, pci, tac, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc), null, null);
+ public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
+ this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+ String.valueOf(mnc), null, null);
}
/**
@@ -87,8 +83,9 @@
*
* @hide
*/
- public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
- this(ci, pci, tac, earfcn, String.valueOf(mcc), String.valueOf(mnc), null, null);
+ public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
+ this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+ null, null);
}
/**
@@ -97,6 +94,7 @@
* @param pci Physical Cell Id 0..503
* @param tac 16-bit Tracking Area Code
* @param earfcn 18-bit LTE Absolute RF Channel Number
+ * @param bandwidth cell bandwidth in kHz
* @param mccStr 3-digit Mobile Country Code in string format
* @param mncStr 2 or 3-digit Mobile Network Code in string format
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
@@ -104,44 +102,20 @@
*
* @hide
*/
- public CellIdentityLte (int ci, int pci, int tac, int earfcn, String mccStr,
- String mncStr, String alphal, String alphas) {
+ public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
+ String mncStr, String alphal, String alphas) {
+ super(TAG, TYPE_LTE, mccStr, mncStr);
mCi = ci;
mPci = pci;
mTac = tac;
mEarfcn = earfcn;
-
- // Only allow INT_MAX if unknown string mcc/mnc
- if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
- mMccStr = mccStr;
- } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
- // If the mccStr is empty or unknown, set it as null.
- mMccStr = null;
- } else {
- // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
- // after the bug got fixed.
- mMccStr = null;
- log("invalid MCC format: " + mccStr);
- }
-
- if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
- mMncStr = mncStr;
- } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
- // If the mncStr is empty or unknown, set it as null.
- mMncStr = null;
- } else {
- // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
- // after the bug got fixed.
- mMncStr = null;
- log("invalid MNC format: " + mncStr);
- }
-
+ mBandwidth = bandwidth;
mAlphaLong = alphal;
mAlphaShort = alphas;
}
private CellIdentityLte(CellIdentityLte cid) {
- this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mMccStr,
+ this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
}
@@ -196,6 +170,13 @@
}
/**
+ * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+ */
+ public int getBandwidth() {
+ return mBandwidth;
+ }
+
+ /**
* @return Mobile Country Code in string format, null if unknown
*/
public String getMccStr() {
@@ -248,58 +229,58 @@
}
CellIdentityLte o = (CellIdentityLte) other;
- return mCi == o.mCi &&
- mPci == o.mPci &&
- mTac == o.mTac &&
- mEarfcn == o.mEarfcn &&
- TextUtils.equals(mMccStr, o.mMccStr) &&
- TextUtils.equals(mMncStr, o.mMncStr) &&
- TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
- TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ return mCi == o.mCi
+ && mPci == o.mPci
+ && mTac == o.mTac
+ && mEarfcn == o.mEarfcn
+ && mBandwidth == o.mBandwidth
+ && TextUtils.equals(mMccStr, o.mMccStr)
+ && TextUtils.equals(mMncStr, o.mMncStr)
+ && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+ && TextUtils.equals(mAlphaShort, o.mAlphaShort);
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CellIdentityLte:{");
- sb.append(" mCi="); sb.append(mCi);
- sb.append(" mPci="); sb.append(mPci);
- sb.append(" mTac="); sb.append(mTac);
- sb.append(" mEarfcn="); sb.append(mEarfcn);
- sb.append(" mMcc="); sb.append(mMccStr);
- sb.append(" mMnc="); sb.append(mMncStr);
- sb.append(" mAlphaLong="); sb.append(mAlphaLong);
- sb.append(" mAlphaShort="); sb.append(mAlphaShort);
- sb.append("}");
-
- return sb.toString();
- }
-
- /** Implement the Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
+ return new StringBuilder(TAG)
+ .append(":{ mCi=").append(mCi)
+ .append(" mPci=").append(mPci)
+ .append(" mTac=").append(mTac)
+ .append(" mEarfcn=").append(mEarfcn)
+ .append(" mBandwidth=").append(mBandwidth)
+ .append(" mMcc=").append(mMccStr)
+ .append(" mMnc=").append(mMncStr)
+ .append(" mAlphaLong=").append(mAlphaLong)
+ .append(" mAlphaShort=").append(mAlphaShort)
+ .append("}").toString();
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ super.writeToParcel(dest, TYPE_LTE);
dest.writeInt(mCi);
dest.writeInt(mPci);
dest.writeInt(mTac);
dest.writeInt(mEarfcn);
- dest.writeString(mMccStr);
- dest.writeString(mMncStr);
+ dest.writeInt(mBandwidth);
dest.writeString(mAlphaLong);
dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
private CellIdentityLte(Parcel in) {
- this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readString(),
- in.readString(), in.readString(), in.readString());
+ super(TAG, TYPE_LTE, in);
+ mCi = in.readInt();
+ mPci = in.readInt();
+ mTac = in.readInt();
+ mEarfcn = in.readInt();
+ mBandwidth = in.readInt();
+ mAlphaLong = in.readString();
+ mAlphaShort = in.readString();
- if (DBG) log("CellIdentityLte(Parcel): " + toString());
+ if (DBG) log(toString());
}
/** Implement the Parcelable interface */
@@ -308,7 +289,8 @@
new Creator<CellIdentityLte>() {
@Override
public CellIdentityLte createFromParcel(Parcel in) {
- return new CellIdentityLte(in);
+ in.readInt(); // skip;
+ return createFromParcelBody(in);
}
@Override
@@ -317,10 +299,8 @@
}
};
- /**
- * log
- */
- private static void log(String s) {
- Rlog.w(LOG_TAG, s);
+ /** @hide */
+ protected static CellIdentityLte createFromParcelBody(Parcel in) {
+ return new CellIdentityLte(in);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentityTdscdma.aidl
similarity index 90%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/CellIdentityTdscdma.aidl
index d750363..2a182cd 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.aidl
@@ -15,6 +15,6 @@
*/
/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+parcelable CellIdentityTdscdma;
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
new file mode 100644
index 0000000..001d19f
--- /dev/null
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * CellIdentity is to represent a unique TD-SCDMA cell
+ */
+public final class CellIdentityTdscdma extends CellIdentity {
+ private static final String TAG = CellIdentityTdscdma.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ // 16-bit Location Area Code, 0..65535, INT_MAX if unknown.
+ private final int mLac;
+ // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown.
+ private final int mCid;
+ // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
+ private final int mCpid;
+
+ /**
+ * @hide
+ */
+ public CellIdentityTdscdma() {
+ super(TAG, TYPE_TDSCDMA, null, null);
+ mLac = Integer.MAX_VALUE;
+ mCid = Integer.MAX_VALUE;
+ mCpid = Integer.MAX_VALUE;
+ }
+
+ /**
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+ * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+ * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ *
+ * @hide
+ */
+ public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
+ this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid);
+ }
+
+ /**
+ * @param mcc 3-digit Mobile Country Code in string format
+ * @param mnc 2 or 3-digit Mobile Network Code in string format
+ * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+ * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+ * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ *
+ * @hide
+ */
+ public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
+ super(TAG, TYPE_TDSCDMA, mcc, mnc);
+ mLac = lac;
+ mCid = cid;
+ mCpid = cpid;
+ }
+
+ private CellIdentityTdscdma(CellIdentityTdscdma cid) {
+ this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid);
+ }
+
+ CellIdentityTdscdma copy() {
+ return new CellIdentityTdscdma(this);
+ }
+
+ /**
+ * Get Mobile Country Code in string format
+ * @return Mobile Country Code in string format, null if unknown
+ */
+ public String getMccStr() {
+ return mMccStr;
+ }
+
+ /**
+ * Get Mobile Network Code in string format
+ * @return Mobile Network Code in string format, null if unknown
+ */
+ public String getMncStr() {
+ return mMncStr;
+ }
+
+ /**
+ * @return 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ /**
+ * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ */
+ public int getCpid() {
+ return mCpid;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof CellIdentityTdscdma)) {
+ return false;
+ }
+
+ CellIdentityTdscdma o = (CellIdentityTdscdma) other;
+ return TextUtils.equals(mMccStr, o.mMccStr)
+ && TextUtils.equals(mMncStr, o.mMncStr)
+ && mLac == o.mLac
+ && mCid == o.mCid
+ && mCpid == o.mCpid;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(TAG)
+ .append(":{ mMcc=").append(mMccStr)
+ .append(" mMnc=").append(mMncStr)
+ .append(" mLac=").append(mLac)
+ .append(" mCid=").append(mCid)
+ .append(" mCpid=").append(mCpid)
+ .append("}").toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ super.writeToParcel(dest, TYPE_TDSCDMA);
+ dest.writeInt(mLac);
+ dest.writeInt(mCid);
+ dest.writeInt(mCpid);
+ }
+
+ /** Construct from Parcel, type has already been processed */
+ private CellIdentityTdscdma(Parcel in) {
+ super(TAG, TYPE_TDSCDMA, in);
+ mLac = in.readInt();
+ mCid = in.readInt();
+ mCpid = in.readInt();
+
+ if (DBG) log(toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Creator<CellIdentityTdscdma> CREATOR =
+ new Creator<CellIdentityTdscdma>() {
+ @Override
+ public CellIdentityTdscdma createFromParcel(Parcel in) {
+ in.readInt(); // skip
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public CellIdentityTdscdma[] newArray(int size) {
+ return new CellIdentityTdscdma[size];
+ }
+ };
+
+ /** @hide */
+ protected static CellIdentityTdscdma createFromParcelBody(Parcel in) {
+ return new CellIdentityTdscdma(in);
+ }
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentityWcdma.aidl
similarity index 90%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/CellIdentityWcdma.aidl
index d750363..462ce2c 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/CellIdentityWcdma.aidl
@@ -15,6 +15,6 @@
*/
/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+parcelable CellIdentityWcdma;
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index e4bb4f2..1aa1715 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -17,8 +17,6 @@
package android.telephony;
import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.Rlog;
import android.text.TextUtils;
import java.util.Objects;
@@ -26,9 +24,8 @@
/**
* CellIdentity to represent a unique UMTS cell
*/
-public final class CellIdentityWcdma implements Parcelable {
-
- private static final String LOG_TAG = "CellIdentityWcdma";
+public final class CellIdentityWcdma extends CellIdentity {
+ private static final String TAG = CellIdentityWcdma.class.getSimpleName();
private static final boolean DBG = false;
// 16-bit Location Area Code, 0..65535
@@ -39,10 +36,6 @@
private final int mPsc;
// 16-bit UMTS Absolute RF Channel Number
private final int mUarfcn;
- // 3-digit Mobile Country Code in string format
- private final String mMccStr;
- // 2 or 3-digit Mobile Network Code in string format
- private final String mMncStr;
// long alpha Operator Name String or Enhanced Operator Name String
private final String mAlphaLong;
// short alpha Operator Name String or Enhanced Operator Name String
@@ -52,12 +45,11 @@
* @hide
*/
public CellIdentityWcdma() {
+ super(TAG, TYPE_TDSCDMA, null, null);
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mPsc = Integer.MAX_VALUE;
mUarfcn = Integer.MAX_VALUE;
- mMccStr = null;
- mMncStr = null;
mAlphaLong = null;
mAlphaShort = null;
}
@@ -106,36 +98,11 @@
*/
public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
String mccStr, String mncStr, String alphal, String alphas) {
+ super(TAG, TYPE_WCDMA, mccStr, mncStr);
mLac = lac;
mCid = cid;
mPsc = psc;
mUarfcn = uarfcn;
-
- // Only allow INT_MAX if unknown string mcc/mnc
- if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
- mMccStr = mccStr;
- } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
- // If the mccStr is empty or unknown, set it as null.
- mMccStr = null;
- } else {
- // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
- // after the bug got fixed.
- mMccStr = null;
- log("invalid MCC format: " + mccStr);
- }
-
- if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
- mMncStr = mncStr;
- } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
- // If the mncStr is empty or unknown, set it as null.
- mMncStr = null;
- } else {
- // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
- // after the bug got fixed.
- mMncStr = null;
- log("invalid MNC format: " + mncStr);
- }
-
mAlphaLong = alphal;
mAlphaShort = alphas;
}
@@ -250,58 +217,53 @@
}
CellIdentityWcdma o = (CellIdentityWcdma) other;
- return mLac == o.mLac &&
- mCid == o.mCid &&
- mPsc == o.mPsc &&
- mUarfcn == o.mUarfcn &&
- TextUtils.equals(mMccStr, o.mMccStr) &&
- TextUtils.equals(mMncStr, o.mMncStr) &&
- TextUtils.equals(mAlphaLong, o.mAlphaLong) &&
- TextUtils.equals(mAlphaShort, o.mAlphaShort);
+ return mLac == o.mLac
+ && mCid == o.mCid
+ && mPsc == o.mPsc
+ && mUarfcn == o.mUarfcn
+ && TextUtils.equals(mMccStr, o.mMccStr)
+ && TextUtils.equals(mMncStr, o.mMncStr)
+ && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+ && TextUtils.equals(mAlphaShort, o.mAlphaShort);
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CellIdentityWcdma:{");
- sb.append(" mLac=").append(mLac);
- sb.append(" mCid=").append(mCid);
- sb.append(" mPsc=").append(mPsc);
- sb.append(" mUarfcn=").append(mUarfcn);
- sb.append(" mMcc=").append(mMccStr);
- sb.append(" mMnc=").append(mMncStr);
- sb.append(" mAlphaLong=").append(mAlphaLong);
- sb.append(" mAlphaShort=").append(mAlphaShort);
- sb.append("}");
-
- return sb.toString();
- }
-
- /** Implement the Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
+ return new StringBuilder(TAG)
+ .append(":{ mLac=").append(mLac)
+ .append(" mCid=").append(mCid)
+ .append(" mPsc=").append(mPsc)
+ .append(" mUarfcn=").append(mUarfcn)
+ .append(" mMcc=").append(mMccStr)
+ .append(" mMnc=").append(mMncStr)
+ .append(" mAlphaLong=").append(mAlphaLong)
+ .append(" mAlphaShort=").append(mAlphaShort)
+ .append("}").toString();
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(Parcel dest, int flags) {
if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ super.writeToParcel(dest, TYPE_WCDMA);
dest.writeInt(mLac);
dest.writeInt(mCid);
dest.writeInt(mPsc);
dest.writeInt(mUarfcn);
- dest.writeString(mMccStr);
- dest.writeString(mMncStr);
dest.writeString(mAlphaLong);
dest.writeString(mAlphaShort);
}
/** Construct from Parcel, type has already been processed */
private CellIdentityWcdma(Parcel in) {
- this(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readString(),
- in.readString(), in.readString(), in.readString());
-
- if (DBG) log("CellIdentityWcdma(Parcel): " + toString());
+ super(TAG, TYPE_WCDMA, in);
+ mLac = in.readInt();
+ mCid = in.readInt();
+ mPsc = in.readInt();
+ mUarfcn = in.readInt();
+ mAlphaLong = in.readString();
+ mAlphaShort = in.readString();
+ if (DBG) log(toString());
}
/** Implement the Parcelable interface */
@@ -310,7 +272,8 @@
new Creator<CellIdentityWcdma>() {
@Override
public CellIdentityWcdma createFromParcel(Parcel in) {
- return new CellIdentityWcdma(in);
+ in.readInt(); // skip
+ return createFromParcelBody(in);
}
@Override
@@ -319,10 +282,8 @@
}
};
- /**
- * log
- */
- private static void log(String s) {
- Rlog.w(LOG_TAG, s);
+ /** @hide */
+ protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
+ return new CellIdentityWcdma(in);
}
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index b5e4eef..9232ed7 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -16,8 +16,11 @@
package android.telephony;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Immutable cell information from a point in time.
@@ -47,6 +50,34 @@
/** @hide */
public static final int TIMESTAMP_TYPE_JAVA_RIL = 4;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ CONNECTION_NONE,
+ CONNECTION_PRIMARY_SERVING,
+ CONNECTION_SECONDARY_SERVING,
+ CONNECTION_UNKNOWN
+ })
+ public @interface CellConnectionStatus {}
+
+ /**
+ * Cell is not a serving cell.
+ *
+ * <p>The cell has been measured but is neither a camped nor serving cell (3GPP 36.304).
+ */
+ public static final int CONNECTION_NONE = 0;
+
+ /** UE is connected to cell for signalling and possibly data (3GPP 36.331, 25.331). */
+ public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+ /** UE is connected to cell for data (3GPP 36.331, 25.331). */
+ public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+ /** Connection status is unknown. */
+ public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
+ private int mCellConnectionStatus = CONNECTION_NONE;
+
// True if device is mRegistered to the mobile network
private boolean mRegistered;
@@ -69,6 +100,7 @@
this.mRegistered = ci.mRegistered;
this.mTimeStampType = ci.mTimeStampType;
this.mTimeStamp = ci.mTimeStamp;
+ this.mCellConnectionStatus = ci.mCellConnectionStatus;
}
/** True if this cell is registered to the mobile network */
@@ -90,6 +122,25 @@
}
/**
+ * Gets the connection status of this cell.
+ *
+ * @see #CONNECTION_NONE
+ * @see #CONNECTION_PRIMARY_SERVING
+ * @see #CONNECTION_SECONDARY_SERVING
+ * @see #CONNECTION_UNKNOWN
+ *
+ * @return The connection status of the cell.
+ */
+ @CellConnectionStatus
+ public int getCellConnectionStatus() {
+ return mCellConnectionStatus;
+ }
+ /** @hide */
+ public void setCellConnectionStatus(@CellConnectionStatus int cellConnectionStatus) {
+ mCellConnectionStatus = cellConnectionStatus;
+ }
+
+ /**
* Where time stamp gets recorded.
* @return one of TIMESTAMP_TYPE_XXXX
*
@@ -111,7 +162,7 @@
public int hashCode() {
int primeNum = 31;
return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
- + (mTimeStampType * primeNum);
+ + (mTimeStampType * primeNum) + (mCellConnectionStatus * primeNum);
}
@Override
@@ -125,7 +176,9 @@
try {
CellInfo o = (CellInfo) other;
return mRegistered == o.mRegistered
- && mTimeStamp == o.mTimeStamp && mTimeStampType == o.mTimeStampType;
+ && mTimeStamp == o.mTimeStamp
+ && mTimeStampType == o.mTimeStampType
+ && mCellConnectionStatus == o.mCellConnectionStatus;
} catch (ClassCastException e) {
return false;
}
@@ -155,6 +208,7 @@
timeStampType = timeStampTypeToString(mTimeStampType);
sb.append(" mTimeStampType=").append(timeStampType);
sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
+ sb.append(" mCellConnectionStatus=").append(mCellConnectionStatus);
return sb.toString();
}
@@ -181,6 +235,7 @@
dest.writeInt(mRegistered ? 1 : 0);
dest.writeInt(mTimeStampType);
dest.writeLong(mTimeStamp);
+ dest.writeInt(mCellConnectionStatus);
}
/**
@@ -192,6 +247,7 @@
mRegistered = (in.readInt() == 1) ? true : false;
mTimeStampType = in.readInt();
mTimeStamp = in.readLong();
+ mCellConnectionStatus = in.readInt();
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 0474362..ece1ee3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -20,6 +20,8 @@
import android.os.Parcelable;
import android.telephony.Rlog;
+import java.util.Objects;
+
/**
* Signal strength related information.
*/
@@ -34,48 +36,14 @@
private int mEvdoEcio; // This value is the EVDO Ec/Io
private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
int evdoSnr) {
- initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param cdmaDbm
- * @param cdmaEcio
- * @param evdoDbm
- * @param evdoEcio
- * @param evdoSnr
- *
- * @hide
- */
- public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) {
mCdmaDbm = cdmaDbm;
mCdmaEcio = cdmaEcio;
mEvdoDbm = evdoDbm;
@@ -83,9 +51,12 @@
mEvdoSnr = evdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthCdma s) {
mCdmaDbm = s.mCdmaDbm;
mCdmaEcio = s.mCdmaEcio;
@@ -94,9 +65,7 @@
mEvdoSnr = s.mEvdoSnr;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthCdma copy() {
return new CellSignalStrengthCdma(this);
@@ -293,9 +262,7 @@
@Override
public int hashCode() {
- int primeNum = 31;
- return ((mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
- + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum));
+ return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr);
}
@Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 4137853..8687cd1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -20,6 +20,8 @@
import android.os.Parcelable;
import android.telephony.Rlog;
+import java.util.Objects;
+
/**
* GSM signal strength related information.
*/
@@ -32,80 +34,40 @@
private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- private int mTimingAdvance;
+ private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthGsm(int ss, int ber) {
- initialize(ss, ber);
+ this(ss, ber, Integer.MAX_VALUE);
}
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
- mSignalStrength = ss;
- mBitErrorRate = ber;
- mTimingAdvance = Integer.MAX_VALUE;
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- * @param ta timing advance
- *
- * @hide
- */
- public void initialize(int ss, int ber, int ta) {
+ /** @hide */
+ public CellSignalStrengthGsm(int ss, int ber, int ta) {
mSignalStrength = ss;
mBitErrorRate = ber;
mTimingAdvance = ta;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthGsm s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthGsm copy() {
return new CellSignalStrengthGsm(this);
@@ -185,8 +147,7 @@
@Override
public int hashCode() {
- int primeNum = 31;
- return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+ return Objects.hash(mSignalStrength, mBitErrorRate, mTimingAdvance);
}
@Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 0d07a40..7e86966 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -20,6 +20,8 @@
import android.os.Parcelable;
import android.telephony.Rlog;
+import java.util.Objects;
+
/**
* LTE signal strength related information.
*/
@@ -35,50 +37,15 @@
private int mCqi;
private int mTimingAdvance;
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
int timingAdvance) {
- initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthLte(CellSignalStrengthLte s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param lteSignalStrength
- * @param rsrp
- * @param rsrq
- * @param rssnr
- * @param cqi
- *
- * @hide
- */
- public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi,
- int timingAdvance) {
- mSignalStrength = lteSignalStrength;
+ mSignalStrength = signalStrength;
mRsrp = rsrp;
mRsrq = rsrq;
mRssnr = rssnr;
@@ -86,25 +53,12 @@
mTimingAdvance = timingAdvance;
}
- /**
- * Initialize from the SignalStrength structure.
- *
- * @param ss
- *
- * @hide
- */
- public void initialize(SignalStrength ss, int timingAdvance) {
- mSignalStrength = ss.getLteSignalStrength();
- mRsrp = ss.getLteRsrp();
- mRsrq = ss.getLteRsrq();
- mRssnr = ss.getLteRssnr();
- mCqi = ss.getLteCqi();
- mTimingAdvance = timingAdvance;
+ /** @hide */
+ public CellSignalStrengthLte(CellSignalStrengthLte s) {
+ copyFrom(s);
}
- /**
- * @hide
- */
+ /** @hide */
protected void copyFrom(CellSignalStrengthLte s) {
mSignalStrength = s.mSignalStrength;
mRsrp = s.mRsrp;
@@ -114,9 +68,7 @@
mTimingAdvance = s.mTimingAdvance;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthLte copy() {
return new CellSignalStrengthLte(this);
@@ -220,7 +172,7 @@
}
/**
- * Get the timing advance value for LTE, as a value between 0..63.
+ * Get the timing advance value for LTE, as a value in range of 0..1282.
* Integer.MAX_VALUE is reported when there is no active RRC
* connection. Refer to 3GPP 36.213 Sec 4.2.3
* @return the LTE timing advance, if available.
@@ -231,10 +183,7 @@
@Override
public int hashCode() {
- int primeNum = 31;
- return (mSignalStrength * primeNum) + (mRsrp * primeNum)
- + (mRsrq * primeNum) + (mRssnr * primeNum) + (mCqi * primeNum)
- + (mTimingAdvance * primeNum);
+ return Objects.hash(mSignalStrength, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
}
@Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index b94b01d..dd32a96 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -20,6 +20,8 @@
import android.os.Parcelable;
import android.telephony.Rlog;
+import java.util.Objects;
+
/**
* Wcdma signal strength related information.
*/
@@ -32,62 +34,32 @@
private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
- private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
- private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
- /**
- * Empty constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma() {
setDefaultValues();
}
- /**
- * Constructor
- *
- * @hide
- */
+ /** @hide */
public CellSignalStrengthWcdma(int ss, int ber) {
- initialize(ss, ber);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
- copyFrom(s);
- }
-
- /**
- * Initialize all the values
- *
- * @param ss SignalStrength as ASU value
- * @param ber is Bit Error Rate
- *
- * @hide
- */
- public void initialize(int ss, int ber) {
mSignalStrength = ss;
mBitErrorRate = ber;
}
- /**
- * @hide
- */
+ /** @hide */
+ public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
protected void copyFrom(CellSignalStrengthWcdma s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
}
- /**
- * @hide
- */
+ /** @hide */
@Override
public CellSignalStrengthWcdma copy() {
return new CellSignalStrengthWcdma(this);
@@ -156,8 +128,7 @@
@Override
public int hashCode() {
- int primeNum = 31;
- return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+ return Objects.hash(mSignalStrength, mBitErrorRate);
}
@Override
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
new file mode 100644
index 0000000..97e3037b
--- /dev/null
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -0,0 +1,72 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to data network registration.
+ * @hide
+ */
+public class DataSpecificRegistrationStates implements Parcelable{
+ /**
+ * The maximum number of simultaneous Data Calls that
+ * must be established using setupDataCall().
+ */
+ public final int maxDataCalls;
+
+ DataSpecificRegistrationStates(int maxDataCalls) {
+ this.maxDataCalls = maxDataCalls;
+ }
+
+ private DataSpecificRegistrationStates(Parcel source) {
+ maxDataCalls = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(maxDataCalls);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "DataSpecificRegistrationStates {" + " mMaxDataCalls=" + maxDataCalls + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(maxDataCalls);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof DataSpecificRegistrationStates)) {
+ return false;
+ }
+
+ DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
+ return this.maxDataCalls == other.maxDataCalls;
+ }
+
+ public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
+ new Parcelable.Creator<DataSpecificRegistrationStates>() {
+ @Override
+ public DataSpecificRegistrationStates createFromParcel(Parcel source) {
+ return new DataSpecificRegistrationStates(source);
+ }
+
+ @Override
+ public DataSpecificRegistrationStates[] newArray(int size) {
+ return new DataSpecificRegistrationStates[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 56e1e64..4fa304a 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -310,6 +310,13 @@
* {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
+
+ /**
+ * The network has reported that an alternative emergency number has been dialed, but the user
+ * must exit airplane mode to place the call.
+ */
+ public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71;
+
//*********************************************************************************************
// When adding a disconnect type:
// 1) Update toString() with the newly added disconnect type.
@@ -462,6 +469,8 @@
return "EMERGENCY_PERM_FAILURE";
case NORMAL_UNSPECIFIED:
return "NORMAL_UNSPECIFIED";
+ case IMS_SIP_ALTERNATE_EMERGENCY_CALL:
+ return "IMS_SIP_ALTERNATE_EMERGENCY_CALL";
default:
return "INVALID: " + cause;
}
diff --git a/telephony/java/android/telephony/INetworkService.aidl b/telephony/java/android/telephony/INetworkService.aidl
new file mode 100644
index 0000000..9ef7186
--- /dev/null
+++ b/telephony/java/android/telephony/INetworkService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.telephony.INetworkServiceCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface INetworkService
+{
+ void createNetworkServiceProvider(int slotId);
+ void removeNetworkServiceProvider(int slotId);
+ void getNetworkRegistrationState(int slotId, int domain, INetworkServiceCallback callback);
+ void registerForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback);
+ void unregisterForNetworkRegistrationStateChanged(int slotId, INetworkServiceCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/INetworkServiceCallback.aidl
similarity index 66%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/INetworkServiceCallback.aidl
index d750363..520598f 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/INetworkServiceCallback.aidl
@@ -14,7 +14,16 @@
* limitations under the License.
*/
-/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+import android.telephony.NetworkRegistrationState;
+
+/**
+ * Network service call back interface
+ * @hide
+ */
+oneway interface INetworkServiceCallback
+{
+ void onGetNetworkRegistrationStateComplete(int result, in NetworkRegistrationState state);
+ void onNetworkStateChanged();
+}
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 0000000..b362df9
--- /dev/null
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+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.provider.Settings;
+import android.util.SparseBooleanArray;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+ /**
+ * 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
+ * @return boolean true or false if permissions is granted
+ */
+ public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
+ int uid, int pid) throws SecurityException {
+ Trace.beginSection("TelephonyLocationCheck");
+ try {
+ // Always allow the phone process to access location. This avoid breaking legacy code
+ // that rely on public-facing APIs to access cell location, and it doesn't create a
+ // info leak risk because the cell location is stored in the phone process anyway.
+ if (uid == Process.PHONE_UID) {
+ return true;
+ }
+
+ // 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 (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) ==
+ PackageManager.PERMISSION_DENIED) {
+ return false;
+ }
+ 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) {
+ return false;
+ }
+ if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
+ && !isLegacyForeground(context, pkgName, 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);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+ int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+ return locationMode != Settings.Secure.LOCATION_MODE_OFF
+ && locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
+ }
+
+ private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName,
+ int uid) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
+ try {
+ if (context.getPackageManager().getApplicationInfo(pkgName, 0)
+ .targetSdkVersion <= Build.VERSION_CODES.O) {
+ 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;
+ }
+
+ private static boolean isForegroundApp(@NonNull Context context, int uid) {
+ final ActivityManager am = context.getSystemService(ActivityManager.class);
+ return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+ }
+
+ private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
+ return context.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int callingUserId = UserHandle.getUserId(uid);
+ if (callingUserId == currentUser) {
+ return true;
+ } else {
+ List<UserInfo> userProfiles = context.getSystemService(
+ UserManager.class).getProfiles(currentUser);
+ for (UserInfo user : userProfiles) {
+ if (user.id == callingUserId) {
+ return true;
+ }
+ }
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 059a2d0..ce1b80c 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -30,7 +30,6 @@
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.telephony.mbms.DownloadStateCallback;
import android.telephony.mbms.FileInfo;
@@ -53,6 +52,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -107,11 +107,8 @@
/**
* {@link Uri} extra that Android will attach to the intent supplied via
* {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
- * Indicates the location of the successfully downloaded file within the temp file root set
- * via {@link #setTempFileRootDirectory(File)}.
- * While you may use this file in-place, it is highly encouraged that you move
- * this file to a different location after receiving the download completion intent, as this
- * file resides within the temp file directory.
+ * Indicates the location of the successfully downloaded file within the directory that the
+ * app provided via the builder.
*
* Will always be set to a non-null value if
* {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
@@ -220,6 +217,8 @@
*/
public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
+ private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile";
+
private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
private final Context mContext;
@@ -236,23 +235,20 @@
private final Map<DownloadStateCallback, InternalDownloadStateCallback>
mInternalDownloadCallbacks = new HashMap<>();
- private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
- int subscriptionId, Handler handler) {
+ private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
+ MbmsDownloadSessionCallback callback) {
mContext = context;
mSubscriptionId = subscriptionId;
- if (handler == null) {
- handler = new Handler(Looper.getMainLooper());
- }
- mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
+ mInternalCallback = new InternalDownloadSessionCallback(callback, executor);
}
/**
* Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
- * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
+ * See {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)}
*/
public static MbmsDownloadSession create(@NonNull Context context,
- @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
- return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+ @NonNull Executor executor, @NonNull MbmsDownloadSessionCallback callback) {
+ return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
}
/**
@@ -279,24 +275,24 @@
* {@link MbmsDownloadSession} that you received before calling this method again.
*
* @param context The instance of {@link Context} to use
- * @param callback A callback to get asynchronous error messages and file service updates.
+ * @param executor The executor on which you wish to execute callbacks.
* @param subscriptionId The data subscription ID to use
- * @param handler The {@link Handler} on which callbacks should be enqueued.
+ * @param callback A callback to get asynchronous error messages and file service updates.
* @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
* setup.
*/
public static @Nullable MbmsDownloadSession create(@NonNull Context context,
- final @NonNull MbmsDownloadSessionCallback callback,
- int subscriptionId, @NonNull Handler handler) {
+ @NonNull Executor executor, int subscriptionId,
+ final @NonNull MbmsDownloadSessionCallback callback) {
if (!sIsInitialized.compareAndSet(false, true)) {
throw new IllegalStateException("Cannot have two active instances");
}
MbmsDownloadSession session =
- new MbmsDownloadSession(context, callback, subscriptionId, handler);
+ new MbmsDownloadSession(context, executor, subscriptionId, callback);
final int result = session.bindAndInitialize();
if (result != MbmsErrors.SUCCESS) {
sIsInitialized.set(false);
- handler.post(new Runnable() {
+ executor.execute(new Runnable() {
@Override
public void run() {
callback.onError(result, null);
@@ -437,6 +433,7 @@
int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, null);
+ return;
}
} catch (RemoteException e) {
mService.set(null);
@@ -499,6 +496,10 @@
* {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
* file root directory.
*
+ * If the {@link DownloadRequest} has a destination that is not on the same filesystem as the
+ * temp file directory provided via {@link #getTempFileRootDirectory()}, an
+ * {@link IllegalArgumentException} will be thrown.
+ *
* Asynchronous errors through the callback may include any error not specific to the
* streaming use-case.
* @param request The request that specifies what should be downloaded.
@@ -521,6 +522,8 @@
setTempFileRootDirectory(tempRootDirectory);
}
+ checkDownloadRequestDestination(request);
+
try {
int result = downloadService.download(request);
if (result == MbmsErrors.SUCCESS) {
@@ -567,21 +570,21 @@
* this method will throw an {@link IllegalArgumentException}.
*
* @param request The {@link DownloadRequest} that you want updates on.
+ * @param executor The {@link Executor} on which calls to {@code callback} should be executed.
* @param callback The callback that should be called when the middleware has information to
* share on the download.
- * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/
public int registerStateCallback(@NonNull DownloadRequest request,
- @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
+ @NonNull Executor executor, @NonNull DownloadStateCallback callback) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
}
InternalDownloadStateCallback internalCallback =
- new InternalDownloadStateCallback(callback, handler);
+ new InternalDownloadStateCallback(callback, executor);
try {
int result = downloadService.registerStateCallback(request, internalCallback,
@@ -603,7 +606,7 @@
/**
* Un-register a callback previously registered via
- * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
+ * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}. After
* this method is called, no further callbacks will be enqueued on the {@link Handler}
* provided upon registration, even if this method throws an exception.
*
@@ -686,30 +689,42 @@
}
/**
- * Gets information about the status of a file pending download.
+ * Requests information about the state of a file pending download.
*
- * If there was a problem communicating with the middleware or if it has no records of the
+ * The state will be delivered as a callback via
+ * {@link DownloadStateCallback#onStateUpdated(DownloadRequest, FileInfo, int)}. If no such
+ * callback has been registered via
+ * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}, this
+ * method will be a no-op.
+ *
+ * If the middleware has no record of the
* file indicated by {@code fileInfo} being associated with {@code downloadRequest},
- * {@link #STATUS_UNKNOWN} will be returned.
+ * an {@link IllegalArgumentException} will be thrown.
*
* @param downloadRequest The download request to query.
* @param fileInfo The particular file within the request to get information on.
- * @return The status of the download.
*/
- @DownloadStatus
- public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
+ public void requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
}
try {
- return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+ int result = downloadService.requestDownloadState(downloadRequest, fileInfo);
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO) {
+ throw new IllegalArgumentException("Unknown file.");
+ }
+ sendErrorToApp(result, null);
+ }
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
- return STATUS_UNKNOWN;
}
}
@@ -762,7 +777,7 @@
* instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
* enqueued will still be delivered.
*
- * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+ * It is safe to call {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)} to
* obtain another instance of {@link MbmsDownloadSession} immediately after this method
* returns.
*
@@ -818,6 +833,36 @@
}
}
+ private void checkDownloadRequestDestination(DownloadRequest request) {
+ File downloadRequestDestination = new File(request.getDestinationUri().getPath());
+ if (!downloadRequestDestination.isDirectory()) {
+ throw new IllegalArgumentException("The destination path must be a directory");
+ }
+ // Check if the request destination is okay to use by attempting to rename an empty
+ // file to there.
+ File testFile = new File(MbmsTempFileProvider.getEmbmsTempFileDir(mContext),
+ DESTINATION_SANITY_CHECK_FILE_NAME);
+ File testFileDestination = new File(downloadRequestDestination,
+ DESTINATION_SANITY_CHECK_FILE_NAME);
+
+ try {
+ if (!testFile.exists()) {
+ testFile.createNewFile();
+ }
+ if (!testFile.renameTo(testFileDestination)) {
+ throw new IllegalArgumentException("Destination provided in the download request " +
+ "is invalid -- files in the temp file directory cannot be directly moved " +
+ "there.");
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Got IOException while testing out the destination: "
+ + e);
+ } finally {
+ testFile.delete();
+ testFileDestination.delete();
+ }
+ }
+
private File getDownloadRequestTokenPath(DownloadRequest request) {
File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
request.getFileServiceId());
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index fb2ff7b..42c760d4 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -24,9 +24,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.InternalStreamingServiceCallback;
@@ -42,6 +40,7 @@
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -89,14 +88,11 @@
private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
/** @hide */
- private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
- int subscriptionId, Handler handler) {
+ private MbmsStreamingSession(Context context, Executor executor, int subscriptionId,
+ MbmsStreamingSessionCallback callback) {
mContext = context;
mSubscriptionId = subscriptionId;
- if (handler == null) {
- handler = new Handler(Looper.getMainLooper());
- }
- mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
+ mInternalCallback = new InternalStreamingSessionCallback(callback, executor);
}
/**
@@ -117,25 +113,25 @@
* {@link MbmsStreamingSession} that you received before calling this method again.
*
* @param context The {@link Context} to use.
+ * @param executor The executor on which you wish to execute callbacks.
+ * @param subscriptionId The subscription ID to use.
* @param callback A callback object on which you wish to receive results of asynchronous
* operations.
- * @param subscriptionId The subscription ID to use.
- * @param handler The handler you wish to receive callbacks on.
* @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
*/
public static @Nullable MbmsStreamingSession create(@NonNull Context context,
- final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
- @NonNull Handler handler) {
+ @NonNull Executor executor, int subscriptionId,
+ final @NonNull MbmsStreamingSessionCallback callback) {
if (!sIsInitialized.compareAndSet(false, true)) {
throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
}
- MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
- subscriptionId, handler);
+ MbmsStreamingSession session = new MbmsStreamingSession(context, executor,
+ subscriptionId, callback);
final int result = session.bindAndInitialize();
if (result != MbmsErrors.SUCCESS) {
sIsInitialized.set(false);
- handler.post(new Runnable() {
+ executor.execute(new Runnable() {
@Override
public void run() {
callback.onError(result, null);
@@ -148,22 +144,22 @@
/**
* Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
- * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+ * See {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)}.
*/
public static MbmsStreamingSession create(@NonNull Context context,
- @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
- return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+ @NonNull Executor executor, @NonNull MbmsStreamingSessionCallback callback) {
+ return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
}
/**
* Terminates this instance. Also terminates
* any streaming services spawned from this instance as if
- * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
+ * {@link StreamingService#close()} had been called on them. After this method returns,
* no further callbacks originating from the middleware will be enqueued on the provided
* instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
* enqueued will still be delivered.
*
- * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
+ * It is safe to call {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)} to
* obtain another instance of {@link MbmsStreamingSession} immediately after this method
* returns.
*
@@ -237,20 +233,20 @@
* {@link MbmsErrors.StreamingErrors}.
*
* @param serviceInfo The information about the service to stream.
+ * @param executor The executor on which you wish to execute callbacks for this stream.
* @param callback A callback that'll be called when something about the stream changes.
- * @param handler A handler that calls to {@code callback} should be called on.
* @return An instance of {@link StreamingService} through which the stream can be controlled.
* May be {@code null} if an error occurred.
*/
public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
- StreamingServiceCallback callback, @NonNull Handler handler) {
+ @NonNull Executor executor, StreamingServiceCallback callback) {
IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) {
throw new IllegalStateException("Middleware not yet bound");
}
InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
- callback, handler);
+ callback, executor);
StreamingService serviceForApp = new StreamingService(
mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/NetworkRegistrationState.aidl
similarity index 88%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/NetworkRegistrationState.aidl
index d750363..98cba77 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/NetworkRegistrationState.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-/** @hide */
-package android.telephony.data;
+package android.telephony;
-parcelable InterfaceAddress;
+parcelable NetworkRegistrationState;
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
new file mode 100644
index 0000000..bba779d
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Description of a mobile network registration state
+ * @hide
+ */
+@SystemApi
+public class NetworkRegistrationState implements Parcelable {
+ /**
+ * Network domain
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DOMAIN_", value = {DOMAIN_CS, DOMAIN_PS})
+ public @interface Domain {}
+
+ /** Circuit switching domain */
+ public static final int DOMAIN_CS = 1;
+ /** Packet switching domain */
+ public static final int DOMAIN_PS = 2;
+
+ /**
+ * Registration state
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "REG_STATE_",
+ value = {REG_STATE_NOT_REG_NOT_SEARCHING, REG_STATE_HOME, REG_STATE_NOT_REG_SEARCHING,
+ REG_STATE_DENIED, REG_STATE_UNKNOWN, REG_STATE_ROAMING})
+ public @interface RegState {}
+
+ /** Not registered. The device is not currently searching a new operator to register */
+ public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0;
+ /** Registered on home network */
+ public static final int REG_STATE_HOME = 1;
+ /** Not registered. The device is currently searching a new operator to register */
+ public static final int REG_STATE_NOT_REG_SEARCHING = 2;
+ /** Registration denied */
+ public static final int REG_STATE_DENIED = 3;
+ /** Registration state is unknown */
+ public static final int REG_STATE_UNKNOWN = 4;
+ /** Registered on roaming network */
+ public static final int REG_STATE_ROAMING = 5;
+
+ /**
+ * Supported service type
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SERVICE_TYPE_",
+ value = {SERVICE_TYPE_VOICE, SERVICE_TYPE_DATA, SERVICE_TYPE_SMS, SERVICE_TYPE_VIDEO,
+ SERVICE_TYPE_EMERGENCY})
+ public @interface ServiceType {}
+
+ public static final int SERVICE_TYPE_VOICE = 1;
+ public static final int SERVICE_TYPE_DATA = 2;
+ public static final int SERVICE_TYPE_SMS = 3;
+ public static final int SERVICE_TYPE_VIDEO = 4;
+ public static final int SERVICE_TYPE_EMERGENCY = 5;
+
+ /** {@link AccessNetworkConstants.TransportType}*/
+ private final int mTransportType;
+
+ @Domain
+ private final int mDomain;
+
+ @RegState
+ private final int mRegState;
+
+ private final int mAccessNetworkTechnology;
+
+ private final int mReasonForDenial;
+
+ private final boolean mEmergencyOnly;
+
+ private final int[] mAvailableServices;
+
+ @Nullable
+ private final CellIdentity mCellIdentity;
+
+ @Nullable
+ private VoiceSpecificRegistrationStates mVoiceSpecificStates;
+
+ @Nullable
+ private DataSpecificRegistrationStates mDataSpecificStates;
+
+ /**
+ * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
+ * @param domain Network domain. Must be DOMAIN_CS or DOMAIN_PS.
+ * @param regState Network registration state.
+ * @param accessNetworkTechnology See TelephonyManager NETWORK_TYPE_XXXX.
+ * @param reasonForDenial Reason for denial if the registration state is DENIED.
+ * @param availableServices The supported service.
+ * @param cellIdentity The identity representing a unique cell
+ */
+ public NetworkRegistrationState(int transportType, int domain, int regState,
+ int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+ int[] availableServices, @Nullable CellIdentity cellIdentity) {
+ mTransportType = transportType;
+ mDomain = domain;
+ mRegState = regState;
+ mAccessNetworkTechnology = accessNetworkTechnology;
+ mReasonForDenial = reasonForDenial;
+ mAvailableServices = availableServices;
+ mCellIdentity = cellIdentity;
+ mEmergencyOnly = emergencyOnly;
+ }
+
+ /**
+ * Constructor for voice network registration states.
+ * @hide
+ */
+ public NetworkRegistrationState(int transportType, int domain, int regState,
+ int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+ int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
+ int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
+ this(transportType, domain, regState, accessNetworkTechnology,
+ reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+ mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
+ systemIsInPrl, defaultRoamingIndicator);
+ }
+
+ /**
+ * Constructor for data network registration states.
+ * @hide
+ */
+ public NetworkRegistrationState(int transportType, int domain, int regState,
+ int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+ int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+ this(transportType, domain, regState, accessNetworkTechnology,
+ reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+ mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
+ }
+
+ protected NetworkRegistrationState(Parcel source) {
+ mTransportType = source.readInt();
+ mDomain = source.readInt();
+ mRegState = source.readInt();
+ mAccessNetworkTechnology = source.readInt();
+ mReasonForDenial = source.readInt();
+ mEmergencyOnly = source.readBoolean();
+ mAvailableServices = source.createIntArray();
+ mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
+ mVoiceSpecificStates = source.readParcelable(
+ VoiceSpecificRegistrationStates.class.getClassLoader());
+ mDataSpecificStates = source.readParcelable(
+ DataSpecificRegistrationStates.class.getClassLoader());
+ }
+
+ /**
+ * @return The transport type.
+ */
+ public int getTransportType() { return mTransportType; }
+
+ /**
+ * @return The network domain.
+ */
+ public @Domain int getDomain() { return mDomain; }
+
+ /**
+ * @return The registration state.
+ */
+ public @RegState int getRegState() {
+ return mRegState;
+ }
+
+ /**
+ * @return Whether emergency is enabled.
+ */
+ public boolean isEmergencyEnabled() { return mEmergencyOnly; }
+
+ /**
+ * @return List of available service types.
+ */
+ public int[] getAvailableServices() { return mAvailableServices; }
+
+ /**
+ * @return The access network technology. Must be one of TelephonyManager.NETWORK_TYPE_XXXX.
+ */
+ public int getAccessNetworkTechnology() {
+ return mAccessNetworkTechnology;
+ }
+
+ /**
+ * @return Reason for denial from network.
+ */
+ public int getReasonForDenial() {
+ return mReasonForDenial;
+ }
+
+ /**
+ * @return The cell information.
+ */
+ public CellIdentity getCellIdentity() {
+ return mCellIdentity;
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public VoiceSpecificRegistrationStates getVoiceSpecificStates() {
+ return mVoiceSpecificStates;
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public DataSpecificRegistrationStates getDataSpecificStates() {
+ return mDataSpecificStates;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private static String regStateToString(int regState) {
+ switch (regState) {
+ case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING";
+ case REG_STATE_HOME: return "HOME";
+ case REG_STATE_NOT_REG_SEARCHING: return "NOT_REG_SEARCHING";
+ case REG_STATE_DENIED: return "DENIED";
+ case REG_STATE_UNKNOWN: return "UNKNOWN";
+ case REG_STATE_ROAMING: return "ROAMING";
+ }
+ return "Unknown reg state " + regState;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("NetworkRegistrationState{")
+ .append("transportType=").append(mTransportType)
+ .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
+ .append(" regState=").append(regStateToString(mRegState))
+ .append(" accessNetworkTechnology=")
+ .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
+ .append(" reasonForDenial=").append(mReasonForDenial)
+ .append(" emergencyEnabled=").append(mEmergencyOnly)
+ .append(" supportedServices=").append(mAvailableServices)
+ .append(" cellIdentity=").append(mCellIdentity)
+ .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
+ .append(" dataSpecificStates=").append(mDataSpecificStates)
+ .append("}").toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
+ mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+ mVoiceSpecificStates, mDataSpecificStates);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof NetworkRegistrationState)) {
+ return false;
+ }
+
+ NetworkRegistrationState other = (NetworkRegistrationState) o;
+ return mTransportType == other.mTransportType
+ && mDomain == other.mDomain
+ && mRegState == other.mRegState
+ && mAccessNetworkTechnology == other.mAccessNetworkTechnology
+ && mReasonForDenial == other.mReasonForDenial
+ && mEmergencyOnly == other.mEmergencyOnly
+ && (mAvailableServices == other.mAvailableServices
+ || Arrays.equals(mAvailableServices, other.mAvailableServices))
+ && equals(mCellIdentity, other.mCellIdentity)
+ && equals(mVoiceSpecificStates, other.mVoiceSpecificStates)
+ && equals(mDataSpecificStates, other.mDataSpecificStates);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mTransportType);
+ dest.writeInt(mDomain);
+ dest.writeInt(mRegState);
+ dest.writeInt(mAccessNetworkTechnology);
+ dest.writeInt(mReasonForDenial);
+ dest.writeBoolean(mEmergencyOnly);
+ dest.writeIntArray(mAvailableServices);
+ dest.writeParcelable(mCellIdentity, 0);
+ dest.writeParcelable(mVoiceSpecificStates, 0);
+ dest.writeParcelable(mDataSpecificStates, 0);
+ }
+
+ public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
+ new Parcelable.Creator<NetworkRegistrationState>() {
+ @Override
+ public NetworkRegistrationState createFromParcel(Parcel source) {
+ return new NetworkRegistrationState(source);
+ }
+
+ @Override
+ public NetworkRegistrationState[] newArray(int size) {
+ return new NetworkRegistrationState[size];
+ }
+ };
+
+ private static boolean equals(Object o1, Object o2) {
+ if (o1 == o2) {
+ return true;
+ } else if (o1 == null) {
+ return false;
+ } else {
+ return o1.equals(o2);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index ea503c3..9726569 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -143,7 +143,11 @@
int incrementalResultsPeriodicity,
ArrayList<String> mccMncs) {
this.mScanType = scanType;
- this.mSpecifiers = specifiers.clone();
+ if (specifiers != null) {
+ this.mSpecifiers = specifiers.clone();
+ } else {
+ this.mSpecifiers = null;
+ }
this.mSearchPeriodicity = searchPeriodicity;
this.mMaxSearchTime = maxSearchTime;
this.mIncrementalResults = incrementalResults;
@@ -187,7 +191,7 @@
/** Returns the radio access technologies with bands or channels that need to be scanned. */
public RadioAccessSpecifier[] getSpecifiers() {
- return mSpecifiers.clone();
+ return mSpecifiers == null ? null : mSpecifiers.clone();
}
/**
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
new file mode 100644
index 0000000..35682a7
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.CallSuper;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of network service. Services that extend NetworkService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_NETWORK_SERVICE". The network service definition in the manifest must
+ * follow the following format:
+ * ...
+ * <service android:name=".xxxNetworkService"
+ * android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
+ * <intent-filter>
+ * <action android:name="android.telephony.NetworkService" />
+ * </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkService extends Service {
+
+ private final String TAG = NetworkService.class.getSimpleName();
+
+ public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
+ public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
+
+ private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER = 1;
+ private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER = 2;
+ private static final int NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS = 3;
+ private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE = 4;
+ private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE = 5;
+ private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE = 6;
+ private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED = 7;
+
+
+ private final HandlerThread mHandlerThread;
+
+ private final NetworkServiceHandler mHandler;
+
+ private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
+
+ /**
+ * The abstract class of the actual network service implementation. The network service provider
+ * must extend this class to support network connection. Note that each instance of network
+ * service is associated with one physical SIM slot.
+ */
+ public class NetworkServiceProvider {
+ private final int mSlotId;
+
+ private final List<INetworkServiceCallback>
+ mNetworkRegistrationStateChangedCallbacks = new ArrayList<>();
+
+ public NetworkServiceProvider(int slotId) {
+ mSlotId = slotId;
+ }
+
+ /**
+ * @return SIM slot id the network service associated with.
+ */
+ public final int getSlotId() {
+ return mSlotId;
+ }
+
+ /**
+ * API to get network registration state. The result will be passed to the callback.
+ * @param domain
+ * @param callback
+ * @return SIM slot id the network service associated with.
+ */
+ public void getNetworkRegistrationState(int domain, NetworkServiceCallback callback) {
+ callback.onGetNetworkRegistrationStateComplete(
+ NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ public final void notifyNetworkRegistrationStateChanged() {
+ mHandler.obtainMessage(NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED,
+ mSlotId, 0, null).sendToTarget();
+ }
+
+ private void registerForStateChanged(INetworkServiceCallback callback) {
+ synchronized (mNetworkRegistrationStateChangedCallbacks) {
+ mNetworkRegistrationStateChangedCallbacks.add(callback);
+ }
+ }
+
+ private void unregisterForStateChanged(INetworkServiceCallback callback) {
+ synchronized (mNetworkRegistrationStateChangedCallbacks) {
+ mNetworkRegistrationStateChangedCallbacks.remove(callback);
+ }
+ }
+
+ private void notifyStateChangedToCallbacks() {
+ for (INetworkServiceCallback callback : mNetworkRegistrationStateChangedCallbacks) {
+ try {
+ callback.onNetworkStateChanged();
+ } catch (RemoteException exception) {
+ // Doing nothing.
+ }
+ }
+ }
+
+ /**
+ * Called when the instance of network service is destroyed (e.g. got unbind or binder died).
+ */
+ @CallSuper
+ protected void onDestroy() {
+ mNetworkRegistrationStateChangedCallbacks.clear();
+ }
+ }
+
+ private class NetworkServiceHandler extends Handler {
+
+ NetworkServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ final int slotId = message.arg1;
+ final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
+
+ NetworkServiceProvider serviceProvider = mServiceMap.get(slotId);
+
+ switch (message.what) {
+ case NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER:
+ // If the service provider doesn't exist yet, we try to create it.
+ if (serviceProvider == null) {
+ mServiceMap.put(slotId, createNetworkServiceProvider(slotId));
+ }
+ break;
+ case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
+ // If the service provider doesn't exist yet, we try to create it.
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ mServiceMap.remove(slotId);
+ }
+ break;
+ case NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS:
+ for (int i = 0; i < mServiceMap.size(); i++) {
+ serviceProvider = mServiceMap.get(i);
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ }
+ }
+ mServiceMap.clear();
+ break;
+ case NETWORK_SERVICE_GET_REGISTRATION_STATE:
+ if (serviceProvider == null) break;
+ int domainId = message.arg2;
+ serviceProvider.getNetworkRegistrationState(domainId,
+ new NetworkServiceCallback(callback));
+
+ break;
+ case NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE:
+ if (serviceProvider == null) break;
+ serviceProvider.registerForStateChanged(callback);
+ break;
+ case NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE:
+ if (serviceProvider == null) break;
+ serviceProvider.unregisterForStateChanged(callback);
+ break;
+ case NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED:
+ if (serviceProvider == null) break;
+ serviceProvider.notifyStateChangedToCallbacks();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /** @hide */
+ protected NetworkService() {
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+
+ mHandler = new NetworkServiceHandler(mHandlerThread.getLooper());
+ log("network service created");
+ }
+
+ /**
+ * Create the instance of {@link NetworkServiceProvider}. Network service provider must override
+ * this method to facilitate the creation of {@link NetworkServiceProvider} instances. The system
+ * will call this method after binding the network service for each active SIM slot id.
+ *
+ * @param slotId SIM slot id the network service associated with.
+ * @return Network service object
+ */
+ protected abstract NetworkServiceProvider createNetworkServiceProvider(int slotId);
+
+ /** @hide */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent == null || !NETWORK_SERVICE_INTERFACE.equals(intent.getAction())) {
+ loge("Unexpected intent " + intent);
+ return null;
+ }
+
+ return mBinder;
+ }
+
+ /** @hide */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS, 0,
+ 0, null).sendToTarget();
+
+ return false;
+ }
+
+ /** @hide */
+ @Override
+ public void onDestroy() {
+ mHandlerThread.quit();
+ }
+
+ /**
+ * A wrapper around INetworkService that forwards calls to implementations of
+ * {@link NetworkService}.
+ */
+ private class INetworkServiceWrapper extends INetworkService.Stub {
+
+ @Override
+ public void createNetworkServiceProvider(int slotId) {
+ mHandler.obtainMessage(NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER, slotId,
+ 0, null).sendToTarget();
+ }
+
+ @Override
+ public void removeNetworkServiceProvider(int slotId) {
+ mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER, slotId,
+ 0, null).sendToTarget();
+ }
+
+ @Override
+ public void getNetworkRegistrationState(
+ int slotId, int domain, INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, slotId,
+ domain, callback).sendToTarget();
+ }
+
+ @Override
+ public void registerForNetworkRegistrationStateChanged(
+ int slotId, INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, slotId,
+ 0, callback).sendToTarget();
+ }
+
+ @Override
+ public void unregisterForNetworkRegistrationStateChanged(
+ int slotId,INetworkServiceCallback callback) {
+ mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, slotId,
+ 0, callback).sendToTarget();
+ }
+ }
+
+ private final void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private final void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
new file mode 100644
index 0000000..92ebf36
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.NetworkService.NetworkServiceProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+
+/**
+ * Network service callback. Object of this class is passed to NetworkServiceProvider upon
+ * calling getNetworkRegistrationState, to receive asynchronous feedback from NetworkServiceProvider
+ * upon onGetNetworkRegistrationStateComplete. It's like a wrapper of INetworkServiceCallback
+ * because INetworkServiceCallback can't be a parameter type in public APIs.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkServiceCallback {
+
+ private static final String mTag = NetworkServiceCallback.class.getSimpleName();
+
+ /**
+ * Result of network requests
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+ RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_FAILED})
+ public @interface Result {}
+
+ /** Request is completed successfully */
+ public static final int RESULT_SUCCESS = 0;
+ /** Request is not support */
+ public static final int RESULT_ERROR_UNSUPPORTED = 1;
+ /** Request contains invalid arguments */
+ public static final int RESULT_ERROR_INVALID_ARG = 2;
+ /** Service is busy */
+ public static final int RESULT_ERROR_BUSY = 3;
+ /** Request sent in illegal state */
+ public static final int RESULT_ERROR_ILLEGAL_STATE = 4;
+ /** Request failed */
+ public static final int RESULT_ERROR_FAILED = 5;
+
+ private final WeakReference<INetworkServiceCallback> mCallback;
+
+ /** @hide */
+ public NetworkServiceCallback(INetworkServiceCallback callback) {
+ mCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * Called to indicate result of
+ * {@link NetworkServiceProvider#getNetworkRegistrationState(int, NetworkServiceCallback)}
+ *
+ * @param result Result status like {@link NetworkServiceCallback#RESULT_SUCCESS} or
+ * {@link NetworkServiceCallback#RESULT_ERROR_UNSUPPORTED}
+ * @param state The state information to be returned to callback.
+ */
+ public void onGetNetworkRegistrationStateComplete(int result, NetworkRegistrationState state) {
+ INetworkServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onGetNetworkRegistrationStateComplete(result, state);
+ } catch (RemoteException e) {
+ Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index ff67116..fadfc91 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -22,6 +22,7 @@
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.android.i18n.phonenumbers.ShortNumberInfo;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -42,6 +43,8 @@
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -49,8 +52,31 @@
/**
* Various utilities for dealing with phone number strings.
*/
-public class PhoneNumberUtils
-{
+public class PhoneNumberUtils {
+ /** {@hide} */
+ @IntDef(prefix = "BCD_EXTENDED_TYPE_", value = {
+ BCD_EXTENDED_TYPE_EF_ADN,
+ BCD_EXTENDED_TYPE_CALLED_PARTY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BcdExtendType {}
+
+ /*
+ * The BCD extended type used to determine the extended char for the digit which is greater than
+ * 9.
+ *
+ * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
+ */
+ public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
+
+ /*
+ * The BCD extended type used to determine the extended char for the digit which is greater than
+ * 9.
+ *
+ * see TS 24.008 section 10.5.4.7 Called party BCD number
+ */
+ public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
+
/*
* Special characters
*
@@ -77,22 +103,6 @@
public static final int TOA_International = 0x91;
public static final int TOA_Unknown = 0x81;
- /*
- * The BCD extended type used to determine the extended char for the digit which is greater than
- * 9.
- *
- * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
- */
- public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
-
- /*
- * The BCD extended type used to determine the extended char for the digit which is greater than
- * 9.
- *
- * see TS 24.008 section 10.5.4.7 Called party BCD number
- */
- public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
-
static final String LOG_TAG = "PhoneNumberUtils";
private static final boolean DBG = false;
@@ -844,7 +854,7 @@
*
*/
public static String calledPartyBCDToString(
- byte[] bytes, int offset, int length, int bcdExtType) {
+ byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
boolean prependPlus = false;
StringBuilder ret = new StringBuilder(1 + length * 2);
@@ -944,7 +954,8 @@
}
private static void internalCalledPartyBCDFragmentToString(
- StringBuilder sb, byte [] bytes, int offset, int length, int bcdExtType) {
+ StringBuilder sb, byte [] bytes, int offset, int length,
+ @BcdExtendType int bcdExtType) {
for (int i = offset ; i < length + offset ; i++) {
byte b;
char c;
@@ -999,7 +1010,7 @@
* TOA byte. For example: SIM ADN extension fields
*/
public static String calledPartyBCDFragmentToString(
- byte[] bytes, int offset, int length, int bcdExtType) {
+ byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
StringBuilder ret = new StringBuilder(length * 2);
internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
return ret.toString();
@@ -1009,7 +1020,7 @@
* Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
* invalid code.
*/
- private static char bcdToChar(byte b, int bcdExtType) {
+ private static char bcdToChar(byte b, @BcdExtendType int bcdExtType) {
if (b < 0xa) {
return (char) ('0' + b);
}
@@ -1027,7 +1038,7 @@
return extended.charAt(b - 0xa);
}
- private static int charToBCD(char c, int bcdExtType) {
+ private static int charToBCD(char c, @BcdExtendType int bcdExtType) {
if ('0' <= c && c <= '9') {
return c - '0';
}
@@ -1134,7 +1145,7 @@
*
* @return BCD byte array
*/
- public static byte[] numberToCalledPartyBCD(String number, int bcdExtType) {
+ public static byte[] numberToCalledPartyBCD(String number, @BcdExtendType int bcdExtType) {
return numberToCalledPartyBCDHelper(number, false, bcdExtType);
}
@@ -1143,7 +1154,7 @@
* the return array.
*/
private static byte[] numberToCalledPartyBCDHelper(
- String number, boolean includeLength, int bcdExtType) {
+ String number, boolean includeLength, @BcdExtendType int bcdExtType) {
int numberLenReal = number.length();
int numberLenEffective = numberLenReal;
boolean hasPlus = number.indexOf('+') != -1;
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index c7e5131..0ee870a 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -244,7 +244,22 @@
*/
public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
- /*
+ /**
+ * Listen for changes to the user mobile data state
+ *
+ * @see #onUserMobileDataStateChanged
+ */
+ public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
+
+ /**
+ * Listen for changes to the physical channel configuration.
+ *
+ * @see #onPhysicalChannelConfigurationChanged
+ * @hide
+ */
+ public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x00100000;
+
+ /*
* Subscription used to listen to the phone state changes
* @hide
*/
@@ -349,10 +364,16 @@
case LISTEN_DATA_ACTIVATION_STATE:
PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
break;
+ case LISTEN_USER_MOBILE_DATA_STATE:
+ PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
+ break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
-
+ case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
+ PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
+ (List<PhysicalChannelConfig>)msg.obj);
+ break;
}
}
};
@@ -543,6 +564,24 @@
}
/**
+ * Callback invoked when the user mobile data state has changed
+ * @param enabled indicates whether the current user mobile data state is enabled or disabled.
+ */
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the current physical channel configuration has changed
+ *
+ * @param configs List of the current {@link PhysicalChannelConfig}s
+ * @hide
+ */
+ public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -654,6 +693,10 @@
send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
}
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
+ }
+
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.aidl b/telephony/java/android/telephony/PhysicalChannelConfig.aidl
new file mode 100644
index 0000000..651c103
--- /dev/null
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.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;
+
+parcelable PhysicalChannelConfig;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
new file mode 100644
index 0000000..651d68d
--- /dev/null
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+public final class PhysicalChannelConfig implements Parcelable {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING})
+ public @interface ConnectionStatus {}
+
+ /**
+ * UE has connection to cell for signalling and possibly data (3GPP 36.331, 25.331).
+ */
+ public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+ /**
+ * UE has connection to cell for data (3GPP 36.331, 25.331).
+ */
+ public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+ /**
+ * Connection status of the cell.
+ *
+ * <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
+ */
+ private int mCellConnectionStatus;
+
+ /**
+ * Cell bandwidth, in kHz.
+ */
+ private int mCellBandwidthDownlinkKhz;
+
+ public PhysicalChannelConfig(int status, int bandwidth) {
+ mCellConnectionStatus = status;
+ mCellBandwidthDownlinkKhz = bandwidth;
+ }
+
+ public PhysicalChannelConfig(Parcel in) {
+ mCellConnectionStatus = in.readInt();
+ mCellBandwidthDownlinkKhz = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mCellConnectionStatus);
+ dest.writeInt(mCellBandwidthDownlinkKhz);
+ }
+
+ /**
+ * @return Cell bandwidth, in kHz
+ */
+ public int getCellBandwidthDownlink() {
+ return mCellBandwidthDownlinkKhz;
+ }
+
+ /**
+ * Gets the connection status of the cell.
+ *
+ * @see #CONNECTION_PRIMARY_SERVING
+ * @see #CONNECTION_SECONDARY_SERVING
+ *
+ * @return Connection status of the cell
+ */
+ @ConnectionStatus
+ public int getConnectionStatus() {
+ return mCellConnectionStatus;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof PhysicalChannelConfig)) {
+ return false;
+ }
+
+ PhysicalChannelConfig config = (PhysicalChannelConfig) o;
+ return mCellConnectionStatus == config.mCellConnectionStatus
+ && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mCellBandwidthDownlinkKhz * 29) + (mCellConnectionStatus * 31);
+ }
+
+ public static final Parcelable.Creator<PhysicalChannelConfig> CREATOR =
+ new Parcelable.Creator<PhysicalChannelConfig>() {
+ public PhysicalChannelConfig createFromParcel(Parcel in) {
+ return new PhysicalChannelConfig(in);
+ }
+
+ public PhysicalChannelConfig[] newArray(int size) {
+ return new PhysicalChannelConfig[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.java b/telephony/java/android/telephony/RadioAccessSpecifier.java
index 5412c61..81e7ed0 100644
--- a/telephony/java/android/telephony/RadioAccessSpecifier.java
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.java
@@ -33,7 +33,7 @@
*
* This parameter must be provided or else the scan will be rejected.
*
- * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+ * See {@link AccessNetworkConstants.AccessNetworkType} for details.
*/
private int mRadioAccessNetwork;
@@ -43,7 +43,7 @@
* When no specific bands are specified (empty array or null), all the frequency bands
* supported by the modem will be scanned.
*
- * See {@link RadioNetworkConstants} for details.
+ * See {@link AccessNetworkConstants} for details.
*/
private int[] mBands;
@@ -56,7 +56,7 @@
* When no specific channels are specified (empty array or null), all the frequency channels
* supported by the modem will be scanned.
*
- * See {@link RadioNetworkConstants} for details.
+ * See {@link AccessNetworkConstants} for details.
*/
private int[] mChannels;
@@ -72,14 +72,22 @@
*/
public RadioAccessSpecifier(int ran, int[] bands, int[] channels) {
this.mRadioAccessNetwork = ran;
- this.mBands = bands.clone();
- this.mChannels = channels.clone();
+ if (bands != null) {
+ this.mBands = bands.clone();
+ } else {
+ this.mBands = null;
+ }
+ if (channels != null) {
+ this.mChannels = channels.clone();
+ } else {
+ this.mChannels = null;
+ }
}
/**
* Returns the radio access network that needs to be scanned.
*
- * The returned value is define in {@link RadioNetworkConstants.RadioAccessNetworks};
+ * The returned value is define in {@link AccessNetworkConstants.AccessNetworkType};
*/
public int getRadioAccessNetwork() {
return mRadioAccessNetwork;
@@ -88,17 +96,17 @@
/**
* Returns the frequency bands that need to be scanned.
*
- * The returned value is defined in either of {@link RadioNetworkConstants.GeranBands},
- * {@link RadioNetworkConstants.UtranBands} and {@link RadioNetworkConstants.EutranBands}, and
+ * The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
+ * {@link AccessNetworkConstants.UtranBand} and {@link AccessNetworkConstants.EutranBand}, and
* it depends on the returned value of {@link #getRadioAccessNetwork()}.
*/
public int[] getBands() {
- return mBands.clone();
+ return mBands == null ? null : mBands.clone();
}
/** Returns the frequency channels that need to be scanned. */
public int[] getChannels() {
- return mChannels.clone();
+ return mChannels == null ? null : mChannels.clone();
}
public static final Parcelable.Creator<RadioAccessSpecifier> CREATOR =
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 116e711..b6be3c5 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -16,12 +16,21 @@
package android.telephony;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Contains phone state and service related information.
*
@@ -29,6 +38,7 @@
*
* <ul>
* <li>Service state: IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF
+ * <li>Duplex mode: UNKNOWN, FDD, TDD
* <li>Roaming indicator
* <li>Operator name, short name and numeric id
* <li>Network selection mode
@@ -64,48 +74,52 @@
*/
public static final int STATE_POWER_OFF = 3;
- /**
- * RIL level registration state values from ril.h
- * ((const char **)response)[0] is registration state 0-6,
- * 0 - Not registered, MT is not currently searching
- * a new operator to register
- * 1 - Registered, home network
- * 2 - Not registered, but MT is currently searching
- * a new operator to register
- * 3 - Registration denied
- * 4 - Unknown
- * 5 - Registered, roaming
- * 10 - Same as 0, but indicates that emergency calls
- * are enabled.
- * 12 - Same as 2, but indicates that emergency calls
- * are enabled.
- * 13 - Same as 3, but indicates that emergency calls
- * are enabled.
- * 14 - Same as 4, but indicates that emergency calls
- * are enabled.
- * @hide
- */
- public static final int RIL_REG_STATE_NOT_REG = 0;
/** @hide */
- public static final int RIL_REG_STATE_HOME = 1;
- /** @hide */
- public static final int RIL_REG_STATE_SEARCHING = 2;
- /** @hide */
- public static final int RIL_REG_STATE_DENIED = 3;
- /** @hide */
- public static final int RIL_REG_STATE_UNKNOWN = 4;
- /** @hide */
- public static final int RIL_REG_STATE_ROAMING = 5;
- /** @hide */
- public static final int RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED = 10;
- /** @hide */
- public static final int RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED = 12;
- /** @hide */
- public static final int RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED = 13;
- /** @hide */
- public static final int RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED = 14;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
+ public @interface DuplexMode {}
/**
+ * Duplex mode for the phone is unknown.
+ */
+ public static final int DUPLEX_MODE_UNKNOWN = 0;
+
+ /**
+ * Duplex mode for the phone is frequency-division duplexing.
+ */
+ public static final int DUPLEX_MODE_FDD = 1;
+
+ /**
+ * Duplex mode for the phone is time-division duplexing.
+ */
+ public static final int DUPLEX_MODE_TDD = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "RIL_RADIO_TECHNOLOGY_" },
+ value = {
+ RIL_RADIO_TECHNOLOGY_UNKNOWN,
+ RIL_RADIO_TECHNOLOGY_GPRS,
+ RIL_RADIO_TECHNOLOGY_EDGE,
+ RIL_RADIO_TECHNOLOGY_UMTS,
+ RIL_RADIO_TECHNOLOGY_IS95A,
+ RIL_RADIO_TECHNOLOGY_IS95B,
+ RIL_RADIO_TECHNOLOGY_1xRTT,
+ RIL_RADIO_TECHNOLOGY_EVDO_0,
+ RIL_RADIO_TECHNOLOGY_EVDO_A,
+ RIL_RADIO_TECHNOLOGY_HSDPA,
+ RIL_RADIO_TECHNOLOGY_HSUPA,
+ RIL_RADIO_TECHNOLOGY_HSPA,
+ RIL_RADIO_TECHNOLOGY_EVDO_B,
+ RIL_RADIO_TECHNOLOGY_EHRPD,
+ RIL_RADIO_TECHNOLOGY_LTE,
+ RIL_RADIO_TECHNOLOGY_HSPAP,
+ RIL_RADIO_TECHNOLOGY_GSM,
+ RIL_RADIO_TECHNOLOGY_TD_SCDMA,
+ RIL_RADIO_TECHNOLOGY_IWLAN,
+ RIL_RADIO_TECHNOLOGY_LTE_CA})
+ public @interface RilRadioTechnology {}
+ /**
* Available radio technologies for GSM, UMTS and CDMA.
* Duplicates the constants from hardware/radio/include/ril.h
* This should only be used by agents working with the ril. Others
@@ -162,6 +176,11 @@
*/
public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
+ /**
+ * Number of radio technologies for GSM, UMTS and CDMA.
+ */
+ private static final int NEXT_RIL_RADIO_TECHNOLOGY = 20;
+
/** @hide */
public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
(1 << (RIL_RADIO_TECHNOLOGY_IS95A - 1))
@@ -172,22 +191,6 @@
| (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
| (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
- /**
- * Available registration states for GSM, UMTS and CDMA.
- */
- /** @hide */
- public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING = 0;
- /** @hide */
- public static final int REGISTRATION_STATE_HOME_NETWORK = 1;
- /** @hide */
- public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_SEARCHING = 2;
- /** @hide */
- public static final int REGISTRATION_STATE_REGISTRATION_DENIED = 3;
- /** @hide */
- public static final int REGISTRATION_STATE_UNKNOWN = 4;
- /** @hide */
- public static final int REGISTRATION_STATE_ROAMING = 5;
-
private int mVoiceRegState = STATE_OUT_OF_SERVICE;
private int mDataRegState = STATE_OUT_OF_SERVICE;
@@ -216,6 +219,11 @@
*/
public static final int ROAMING_TYPE_INTERNATIONAL = 3;
+ /**
+ * Unknown ID. Could be returned by {@link #getNetworkId()} or {@link #getSystemId()}
+ */
+ public static final int UNKNOWN_ID = -1;
+
private int mVoiceRoamingType;
private int mDataRoamingType;
private String mVoiceOperatorAlphaLong;
@@ -243,10 +251,15 @@
private boolean mIsUsingCarrierAggregation;
+ private int mChannelNumber;
+ private int[] mCellBandwidths = new int[0];
+
/* EARFCN stands for E-UTRA Absolute Radio Frequency Channel Number,
* Reference: 3GPP TS 36.104 5.4.3 */
private int mLteEarfcnRsrpBoost = 0;
+ private List<NetworkRegistrationState> mNetworkRegistrationStates = new ArrayList<>();
+
/**
* get String description of roaming type
* @hide
@@ -326,7 +339,10 @@
mIsEmergencyOnly = s.mIsEmergencyOnly;
mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
+ mChannelNumber = s.mChannelNumber;
+ mCellBandwidths = Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length);
mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
+ mNetworkRegistrationStates = new ArrayList<>(s.mNetworkRegistrationStates);
}
/**
@@ -357,6 +373,10 @@
mIsDataRoamingFromRegistration = in.readInt() != 0;
mIsUsingCarrierAggregation = in.readInt() != 0;
mLteEarfcnRsrpBoost = in.readInt();
+ mNetworkRegistrationStates = new ArrayList<>();
+ in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader());
+ mChannelNumber = in.readInt();
+ mCellBandwidths = in.createIntArray();
}
public void writeToParcel(Parcel out, int flags) {
@@ -384,6 +404,9 @@
out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
out.writeInt(mLteEarfcnRsrpBoost);
+ out.writeList(mNetworkRegistrationStates);
+ out.writeInt(mChannelNumber);
+ out.writeIntArray(mCellBandwidths);
}
public int describeContents() {
@@ -437,6 +460,43 @@
}
/**
+ * Get the current duplex mode
+ *
+ * @see #DUPLEX_MODE_UNKNOWN
+ * @see #DUPLEX_MODE_FDD
+ * @see #DUPLEX_MODE_TDD
+ *
+ * @return Current {@code DuplexMode} for the phone
+ */
+ @DuplexMode
+ public int getDuplexMode() {
+ // TODO(b/72117602) determine duplex mode from channel number, using 3GPP 36.101 sections
+ // 5.7.3-1 and 5.5-1
+ return DUPLEX_MODE_UNKNOWN;
+ }
+
+ /**
+ * Get the channel number of the current primary serving cell, or -1 if unknown
+ *
+ * <p>This is EARFCN for LTE, UARFCN for UMTS, and ARFCN for GSM.
+ *
+ * @return Channel number of primary serving cell
+ */
+ public int getChannelNumber() {
+ return mChannelNumber;
+ }
+
+ /**
+ * Get an array of cell bandwidths (kHz) for the current serving cells
+ *
+ * @return Current serving cell bandwidths
+ */
+ @Nullable
+ public int[] getCellBandwidths() {
+ return mCellBandwidths;
+ }
+
+ /**
* Get current roaming indicator of phone
* (note: not just decoding from TS 27.007 7.2)
*
@@ -664,6 +724,8 @@
+ (mDataRegState * 37)
+ mVoiceRoamingType
+ mDataRoamingType
+ + mChannelNumber
+ + Arrays.hashCode(mCellBandwidths)
+ (mIsManualNetworkSelection ? 1 : 0)
+ ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
+ ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
@@ -696,6 +758,8 @@
&& mIsManualNetworkSelection == s.mIsManualNetworkSelection
&& mVoiceRoamingType == s.mVoiceRoamingType
&& mDataRoamingType == s.mDataRoamingType
+ && mChannelNumber == s.mChannelNumber
+ && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
&& equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
&& equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
&& equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
@@ -712,13 +776,14 @@
s.mCdmaDefaultRoamingIndicator)
&& mIsEmergencyOnly == s.mIsEmergencyOnly
&& mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
- && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation);
+ && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation)
+ && mNetworkRegistrationStates.containsAll(s.mNetworkRegistrationStates);
}
/**
* Convert radio technology to String
*
- * @param radioTechnology
+ * @param rt radioTechnology
* @return String representation of the RAT
*
* @hide
@@ -824,6 +889,8 @@
.append("(" + rilServiceStateToString(mVoiceRegState) + ")")
.append(", mDataRegState=").append(mDataRegState)
.append("(" + rilServiceStateToString(mDataRegState) + ")")
+ .append(", mChannelNumber=").append(mChannelNumber)
+ .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
.append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
.append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
.append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
@@ -845,6 +912,7 @@
.append(", mIsDataRoamingFromRegistration=").append(mIsDataRoamingFromRegistration)
.append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
.append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+ .append(", mNetworkRegistrationStates=").append(mNetworkRegistrationStates)
.append("}").toString();
}
@@ -854,6 +922,8 @@
mDataRegState = state;
mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING;
mDataRoamingType = ROAMING_TYPE_NOT_ROAMING;
+ mChannelNumber = -1;
+ mCellBandwidths = new int[0];
mVoiceOperatorAlphaLong = null;
mVoiceOperatorAlphaShort = null;
mVoiceOperatorNumeric = null;
@@ -874,6 +944,7 @@
mIsDataRoamingFromRegistration = false;
mIsUsingCarrierAggregation = false;
mLteEarfcnRsrpBoost = 0;
+ mNetworkRegistrationStates = new ArrayList<>();
}
public void setStateOutOfService() {
@@ -901,6 +972,16 @@
if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
}
+ /** @hide */
+ public void setCellBandwidths(int[] bandwidths) {
+ mCellBandwidths = bandwidths;
+ }
+
+ /** @hide */
+ public void setChannelNumber(int channelNumber) {
+ mChannelNumber = channelNumber;
+ }
+
public void setRoaming(boolean roaming) {
mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
mDataRoamingType = mVoiceRoamingType;
@@ -1049,6 +1130,8 @@
mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost");
+ mChannelNumber = m.getInt("ChannelNumber");
+ mCellBandwidths = m.getIntArray("CellBandwidths");
}
/**
@@ -1080,6 +1163,8 @@
m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration);
m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation);
m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
+ m.putInt("ChannelNumber", mChannelNumber);
+ m.putIntArray("CellBandwidths", mCellBandwidths);
}
/** @hide */
@@ -1153,7 +1238,8 @@
return getRilDataRadioTechnology();
}
- private int rilRadioTechnologyToNetworkType(int rt) {
+ /** @hide */
+ public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rt) {
switch(rt) {
case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
return TelephonyManager.NETWORK_TYPE_GPRS;
@@ -1198,6 +1284,84 @@
}
/** @hide */
+ public static int rilRadioTechnologyToAccessNetworkType(@RilRadioTechnology int rt) {
+ switch(rt) {
+ case RIL_RADIO_TECHNOLOGY_GPRS:
+ case RIL_RADIO_TECHNOLOGY_EDGE:
+ case RIL_RADIO_TECHNOLOGY_GSM:
+ return AccessNetworkType.GERAN;
+ case RIL_RADIO_TECHNOLOGY_UMTS:
+ case RIL_RADIO_TECHNOLOGY_HSDPA:
+ case RIL_RADIO_TECHNOLOGY_HSPAP:
+ case RIL_RADIO_TECHNOLOGY_HSUPA:
+ case RIL_RADIO_TECHNOLOGY_HSPA:
+ case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ return AccessNetworkType.UTRAN;
+ case RIL_RADIO_TECHNOLOGY_IS95A:
+ case RIL_RADIO_TECHNOLOGY_IS95B:
+ case RIL_RADIO_TECHNOLOGY_1xRTT:
+ case RIL_RADIO_TECHNOLOGY_EVDO_0:
+ case RIL_RADIO_TECHNOLOGY_EVDO_A:
+ case RIL_RADIO_TECHNOLOGY_EVDO_B:
+ case RIL_RADIO_TECHNOLOGY_EHRPD:
+ return AccessNetworkType.CDMA2000;
+ case RIL_RADIO_TECHNOLOGY_LTE:
+ case RIL_RADIO_TECHNOLOGY_LTE_CA:
+ return AccessNetworkType.EUTRAN;
+ case RIL_RADIO_TECHNOLOGY_IWLAN:
+ return AccessNetworkType.IWLAN;
+ case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+ default:
+ return AccessNetworkType.UNKNOWN;
+ }
+ }
+
+ /** @hide */
+ public static int networkTypeToRilRadioTechnology(int networkType) {
+ switch(networkType) {
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_GPRS;
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EDGE;
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA;
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA;
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_IS95A;
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT;
+ case TelephonyManager.NETWORK_TYPE_EVDO_0:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0;
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A;
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B;
+ case TelephonyManager.NETWORK_TYPE_EHRPD:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP;
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_GSM;
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+ case TelephonyManager.NETWORK_TYPE_IWLAN:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+ case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
+ default:
+ return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ }
+ }
+
+
+ /** @hide */
public int getDataNetworkType() {
return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
}
@@ -1212,12 +1376,20 @@
return this.mCssIndicator ? 1 : 0;
}
- /** @hide */
+ /**
+ * Get the CDMA NID (Network Identification Number), a number uniquely identifying a network
+ * within a wireless system. (Defined in 3GPP2 C.S0023 3.4.8)
+ * @return The CDMA NID or {@link #UNKNOWN_ID} if not available.
+ */
public int getNetworkId() {
return this.mNetworkId;
}
- /** @hide */
+ /**
+ * Get the CDMA SID (System Identification Number), a number uniquely identifying a wireless
+ * system. (Defined in 3GPP2 C.S0023 3.4.8)
+ * @return The CDMA SID or {@link #UNKNOWN_ID} if not available.
+ */
public int getSystemId() {
return this.mSystemId;
}
@@ -1300,6 +1472,34 @@
return bearerBitmask;
}
+ /** @hide */
+ public static int convertNetworkTypeBitmaskToBearerBitmask(int networkTypeBitmask) {
+ if (networkTypeBitmask == 0) {
+ return 0;
+ }
+ int bearerBitmask = 0;
+ for (int bearerInt = 0; bearerInt < NEXT_RIL_RADIO_TECHNOLOGY; bearerInt++) {
+ if (bitmaskHasTech(networkTypeBitmask, rilRadioTechnologyToNetworkType(bearerInt))) {
+ bearerBitmask |= getBitmaskForTech(bearerInt);
+ }
+ }
+ return bearerBitmask;
+ }
+
+ /** @hide */
+ public static int convertBearerBitmaskToNetworkTypeBitmask(int bearerBitmask) {
+ if (bearerBitmask == 0) {
+ return 0;
+ }
+ int networkTypeBitmask = 0;
+ for (int bearerInt = 0; bearerInt < NEXT_RIL_RADIO_TECHNOLOGY; bearerInt++) {
+ if (bitmaskHasTech(bearerBitmask, bearerInt)) {
+ networkTypeBitmask |= getBitmaskForTech(rilRadioTechnologyToNetworkType(bearerInt));
+ }
+ }
+ return networkTypeBitmask;
+ }
+
/**
* Returns a merged ServiceState consisting of the base SS with voice settings from the
* voice SS. The voice SS is only used if it is IN_SERVICE (otherwise the base SS is returned).
@@ -1318,4 +1518,82 @@
return newSs;
}
+
+ /**
+ * Get all of the available network registration states.
+ *
+ * @return List of registration states
+ * @hide
+ */
+ @SystemApi
+ public List<NetworkRegistrationState> getNetworkRegistrationStates() {
+ synchronized (mNetworkRegistrationStates) {
+ return new ArrayList<>(mNetworkRegistrationStates);
+ }
+ }
+
+ /**
+ * Get the network registration states with given transport type.
+ *
+ * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
+ * @return List of registration states.
+ * @hide
+ */
+ @SystemApi
+ public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+ List<NetworkRegistrationState> list = new ArrayList<>();
+
+ synchronized (mNetworkRegistrationStates) {
+ for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+ if (networkRegistrationState.getTransportType() == transportType) {
+ list.add(networkRegistrationState);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Get the network registration states with given transport type and domain.
+ *
+ * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
+ * @param domain The network domain. Must be DOMAIN_CS or DOMAIN_PS.
+ * @return The matching NetworkRegistrationState.
+ * @hide
+ */
+ @SystemApi
+ public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
+ synchronized (mNetworkRegistrationStates) {
+ for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+ if (networkRegistrationState.getTransportType() == transportType
+ && networkRegistrationState.getDomain() == domain) {
+ return networkRegistrationState;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void addNetworkRegistrationState(NetworkRegistrationState regState) {
+ if (regState == null) return;
+
+ synchronized (mNetworkRegistrationStates) {
+ for (int i = 0; i < mNetworkRegistrationStates.size(); i++) {
+ NetworkRegistrationState curRegState = mNetworkRegistrationStates.get(i);
+ if (curRegState.getTransportType() == regState.getTransportType()
+ && curRegState.getDomain() == regState.getDomain()) {
+ mNetworkRegistrationStates.remove(i);
+ break;
+ }
+ }
+
+ mNetworkRegistrationStates.add(regState);
+ }
+ }
+
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index de02de7..fc2ef27 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -19,9 +19,13 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
import android.util.Log;
import android.content.res.Resources;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
* Contains phone signal strength related information.
*/
@@ -47,10 +51,15 @@
"none", "poor", "moderate", "good", "great"
};
- /** @hide */
- //Use int max, as -1 is a valid value in signal strength
- public static final int INVALID = 0x7FFFFFFF;
+ /**
+ * Use Integer.MAX_VALUE because -1 is a valid value in signal strength.
+ * @hide
+ */
+ public static final int INVALID = Integer.MAX_VALUE;
+ private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
+
+ /** Parameters reported by the Radio */
private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
private int mCdmaDbm; // This value is the RSSI value
@@ -63,13 +72,18 @@
private int mLteRsrq;
private int mLteRssnr;
private int mLteCqi;
- private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
- // signal strength level
private int mTdScdmaRscp;
- private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+ /** Parameters from the framework */
+ private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
+ // signal strength level
+ private boolean mIsGsm; // This value is set by the ServiceStateTracker
+ // onSignalStrengthResult.
private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
+ // The threshold of LTE RSRP for determining the display level of LTE signal bar.
+ private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
+
/**
* Create a new SignalStrength from a intent notifier Bundle
*
@@ -94,27 +108,12 @@
* @hide
*/
public SignalStrength() {
- mGsmSignalStrength = 99;
- mGsmBitErrorRate = -1;
- mCdmaDbm = -1;
- mCdmaEcio = -1;
- mEvdoDbm = -1;
- mEvdoEcio = -1;
- mEvdoSnr = -1;
- mLteSignalStrength = 99;
- mLteRsrp = INVALID;
- mLteRsrq = INVALID;
- mLteRssnr = INVALID;
- mLteCqi = INVALID;
- mLteRsrpBoost = 0;
- mTdScdmaRscp = INVALID;
- isGsm = true;
- mUseOnlyRsrpForLteLevel = false;
+ this(true);
}
/**
* This constructor is used to create SignalStrength with default
- * values and set the isGsmFlag with the value passed in the input
+ * values and set the gsmFlag with the value passed in the input
*
* @param gsmFlag true if Gsm Phone,false if Cdma phone
* @return newly created SignalStrength
@@ -133,133 +132,26 @@
mLteRsrq = INVALID;
mLteRssnr = INVALID;
mLteCqi = INVALID;
- mLteRsrpBoost = 0;
mTdScdmaRscp = INVALID;
- isGsm = gsmFlag;
+ mLteRsrpBoost = 0;
+ mIsGsm = gsmFlag;
mUseOnlyRsrpForLteLevel = false;
+ setLteRsrpThresholds(getDefaultLteRsrpThresholds());
}
/**
- * Constructor
+ * Constructor with all fields present
*
* @hide
*/
- public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ public SignalStrength(
+ int gsmSignalStrength, int gsmBitErrorRate,
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
- initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag, lteLevelBaseOnRsrp);
- mTdScdmaRscp = tdScdmaRscp;
- }
-
- /**
- * Constructor
- *
- * @hide
- */
- public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int tdScdmaRscp, boolean gsmFlag) {
- initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
- mTdScdmaRscp = tdScdmaRscp;
- }
-
- /**
- * Constructor
- *
- * @hide
- */
- public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- boolean gsmFlag) {
- initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
- }
-
- /**
- * Constructor
- *
- * @hide
- */
- public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- boolean gsmFlag) {
- initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsmFlag, false);
- }
-
- /**
- * Copy constructors
- *
- * @param s Source SignalStrength
- *
- * @hide
- */
- public SignalStrength(SignalStrength s) {
- copyFrom(s);
- }
-
- /**
- * Initialize gsm/cdma values, sets lte values to defaults.
- *
- * @param gsmSignalStrength
- * @param gsmBitErrorRate
- * @param cdmaDbm
- * @param cdmaEcio
- * @param evdoDbm
- * @param evdoEcio
- * @param evdoSnr
- * @param gsm
- *
- * @hide
- */
- public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- boolean gsm) {
- initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsm, false);
- }
-
- /**
- * Initialize all the values
- *
- * @param gsmSignalStrength
- * @param gsmBitErrorRate
- * @param cdmaDbm
- * @param cdmaEcio
- * @param evdoDbm
- * @param evdoEcio
- * @param evdoSnr
- * @param lteSignalStrength
- * @param lteRsrp
- * @param lteRsrq
- * @param lteRssnr
- * @param lteCqi
- * @param lteRsrpBoost
- * @param gsm
- * @param useOnlyRsrpForLteLevel
- *
- * @hide
- */
- public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
- int cdmaDbm, int cdmaEcio,
- int evdoDbm, int evdoEcio, int evdoSnr,
- int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, boolean gsm, boolean useOnlyRsrpForLteLevel) {
+ int tdScdmaRscp,
+ // values Added by config
+ int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
mGsmSignalStrength = gsmSignalStrength;
mGsmBitErrorRate = gsmBitErrorRate;
mCdmaDbm = cdmaDbm;
@@ -272,14 +164,41 @@
mLteRsrq = lteRsrq;
mLteRssnr = lteRssnr;
mLteCqi = lteCqi;
- mLteRsrpBoost = lteRsrpBoost;
mTdScdmaRscp = INVALID;
- isGsm = gsm;
- mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
+ mLteRsrpBoost = lteRsrpBoost;
+ mIsGsm = gsmFlag;
+ mUseOnlyRsrpForLteLevel = lteLevelBaseOnRsrp;
+ setLteRsrpThresholds(getDefaultLteRsrpThresholds());
if (DBG) log("initialize: " + toString());
}
/**
+ * Constructor for only values provided by Radio HAL
+ *
+ * @hide
+ */
+ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+ int tdScdmaRscp) {
+ this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+ lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 0, true, false);
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source SignalStrength
+ *
+ * @hide
+ */
+ public SignalStrength(SignalStrength s) {
+ copyFrom(s);
+ }
+
+ /**
* @hide
*/
protected void copyFrom(SignalStrength s) {
@@ -295,10 +214,11 @@
mLteRsrq = s.mLteRsrq;
mLteRssnr = s.mLteRssnr;
mLteCqi = s.mLteCqi;
- mLteRsrpBoost = s.mLteRsrpBoost;
mTdScdmaRscp = s.mTdScdmaRscp;
- isGsm = s.isGsm;
+ mLteRsrpBoost = s.mLteRsrpBoost;
+ mIsGsm = s.mIsGsm;
mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
+ setLteRsrpThresholds(s.mLteRsrpThresholds);
}
/**
@@ -321,37 +241,11 @@
mLteRsrq = in.readInt();
mLteRssnr = in.readInt();
mLteCqi = in.readInt();
- mLteRsrpBoost = in.readInt();
mTdScdmaRscp = in.readInt();
- isGsm = (in.readInt() != 0);
- mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
- }
-
- /**
- * Make a SignalStrength object from the given parcel as passed up by
- * the ril which does not have isGsm. isGsm will be changed by ServiceStateTracker
- * so the default is a don't care.
- *
- * @hide
- */
- public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) {
- if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
-
- SignalStrength ss = new SignalStrength();
- ss.mGsmSignalStrength = in.readInt();
- ss.mGsmBitErrorRate = in.readInt();
- ss.mCdmaDbm = in.readInt();
- ss.mCdmaEcio = in.readInt();
- ss.mEvdoDbm = in.readInt();
- ss.mEvdoEcio = in.readInt();
- ss.mEvdoSnr = in.readInt();
- ss.mLteSignalStrength = in.readInt();
- ss.mLteRsrp = in.readInt();
- ss.mLteRsrq = in.readInt();
- ss.mLteRssnr = in.readInt();
- ss.mLteCqi = in.readInt();
- ss.mTdScdmaRscp = in.readInt();
- return ss;
+ mLteRsrpBoost = in.readInt();
+ mIsGsm = in.readBoolean();
+ mUseOnlyRsrpForLteLevel = in.readBoolean();
+ in.readIntArray(mLteRsrpThresholds);
}
/**
@@ -370,10 +264,11 @@
out.writeInt(mLteRsrq);
out.writeInt(mLteRssnr);
out.writeInt(mLteCqi);
- out.writeInt(mLteRsrpBoost);
out.writeInt(mTdScdmaRscp);
- out.writeInt(isGsm ? 1 : 0);
- out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
+ out.writeInt(mLteRsrpBoost);
+ out.writeBoolean(mIsGsm);
+ out.writeBoolean(mUseOnlyRsrpForLteLevel);
+ out.writeIntArray(mLteRsrpThresholds);
}
/**
@@ -436,24 +331,24 @@
}
/**
- * Fix {@link #isGsm} based on the signal strength data.
+ * Fix {@link #mIsGsm} based on the signal strength data.
*
* @hide
*/
public void fixType() {
- isGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ mIsGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
/**
* @param true - Gsm, Lte phones
* false - Cdma phones
*
- * Used by voice phone to set the isGsm
+ * Used by voice phone to set the mIsGsm
* flag
* @hide
*/
public void setGsm(boolean gsmFlag) {
- isGsm = gsmFlag;
+ mIsGsm = gsmFlag;
}
/**
@@ -480,6 +375,22 @@
}
/**
+ * Sets the threshold array for determining the display level of LTE signal bar.
+ *
+ * @param lteRsrpThresholds int array for determining the display level.
+ *
+ * @hide
+ */
+ public void setLteRsrpThresholds(int[] lteRsrpThresholds) {
+ if ((lteRsrpThresholds == null)
+ || (lteRsrpThresholds.length != LTE_RSRP_THRESHOLDS_NUM)) {
+ Log.wtf(LOG_TAG, "setLteRsrpThresholds - lteRsrpThresholds is invalid.");
+ return;
+ }
+ System.arraycopy(lteRsrpThresholds, 0, mLteRsrpThresholds, 0, LTE_RSRP_THRESHOLDS_NUM);
+ }
+
+ /**
* Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
* 27.007 8.5
*/
@@ -568,7 +479,7 @@
* while 4 represents a very strong signal strength.
*/
public int getLevel() {
- int level = isGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
+ int level = mIsGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
if (DBG) log("getLevel=" + level);
return level;
}
@@ -580,15 +491,13 @@
*/
public int getAsuLevel() {
int asuLevel = 0;
- if (isGsm) {
- if (getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
- asuLevel = getGsmAsuLevel();
- } else {
- asuLevel = getTdScdmaAsuLevel();
- }
- } else {
+ if (mIsGsm) {
+ if (mLteRsrp != SignalStrength.INVALID) {
asuLevel = getLteAsuLevel();
+ } else if (mTdScdmaRscp != SignalStrength.INVALID) {
+ asuLevel = getTdScdmaAsuLevel();
+ } else {
+ asuLevel = getGsmAsuLevel();
}
} else {
int cdmaAsuLevel = getCdmaAsuLevel();
@@ -833,25 +742,18 @@
*/
int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
- int[] threshRsrp = Resources.getSystem().getIntArray(
- com.android.internal.R.array.config_lteDbmThresholds);
- if (threshRsrp.length != 6) {
- Log.wtf(LOG_TAG, "getLteLevel - config_lteDbmThresholds has invalid num of elements."
- + " Cannot evaluate RSRP signal.");
- } else {
- if (mLteRsrp > threshRsrp[5]) {
- rsrpIconLevel = -1;
- } else if (mLteRsrp >= (threshRsrp[4] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
- } else if (mLteRsrp >= (threshRsrp[3] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
- } else if (mLteRsrp >= (threshRsrp[2] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
- } else if (mLteRsrp >= (threshRsrp[1] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_POOR;
- } else if (mLteRsrp >= threshRsrp[0]) {
- rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- }
+ if (mLteRsrp > mLteRsrpThresholds[5]) {
+ rsrpIconLevel = -1;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[4] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_POOR;
+ } else if (mLteRsrp >= mLteRsrpThresholds[0]) {
+ rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
if (useOnlyRsrpForLteLevel()) {
@@ -937,7 +839,7 @@
* @return true if this is for GSM
*/
public boolean isGsm() {
- return this.isGsm;
+ return this.mIsGsm;
}
/**
@@ -1009,8 +911,8 @@
+ (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
+ (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
- + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
- + (mUseOnlyRsrpForLteLevel ? 1 : 0));
+ + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (mIsGsm ? 1 : 0)
+ + (mUseOnlyRsrpForLteLevel ? 1 : 0) + (Arrays.hashCode(mLteRsrpThresholds)));
}
/**
@@ -1044,8 +946,9 @@
&& mLteCqi == s.mLteCqi
&& mLteRsrpBoost == s.mLteRsrpBoost
&& mTdScdmaRscp == s.mTdScdmaRscp
- && isGsm == s.isGsm
- && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel);
+ && mIsGsm == s.mIsGsm
+ && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
+ && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds));
}
/**
@@ -1068,9 +971,10 @@
+ " " + mLteCqi
+ " " + mLteRsrpBoost
+ " " + mTdScdmaRscp
- + " " + (isGsm ? "gsm|lte" : "cdma")
+ + " " + (mIsGsm ? "gsm|lte" : "cdma")
+ " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
- "use_rsrp_and_rssnr_for_lte_level"));
+ "use_rsrp_and_rssnr_for_lte_level")
+ + " " + (Arrays.toString(mLteRsrpThresholds)));
}
/** Returns the signal strength related to GSM. */
@@ -1122,10 +1026,14 @@
mLteRsrq = m.getInt("LteRsrq");
mLteRssnr = m.getInt("LteRssnr");
mLteCqi = m.getInt("LteCqi");
- mLteRsrpBoost = m.getInt("lteRsrpBoost");
+ mLteRsrpBoost = m.getInt("LteRsrpBoost");
mTdScdmaRscp = m.getInt("TdScdma");
- isGsm = m.getBoolean("isGsm");
- mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
+ mIsGsm = m.getBoolean("IsGsm");
+ mUseOnlyRsrpForLteLevel = m.getBoolean("UseOnlyRsrpForLteLevel");
+ ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
+ for (int i = 0; i < lteRsrpThresholds.size(); i++) {
+ mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
+ }
}
/**
@@ -1147,10 +1055,25 @@
m.putInt("LteRsrq", mLteRsrq);
m.putInt("LteRssnr", mLteRssnr);
m.putInt("LteCqi", mLteCqi);
- m.putInt("lteRsrpBoost", mLteRsrpBoost);
+ m.putInt("LteRsrpBoost", mLteRsrpBoost);
m.putInt("TdScdma", mTdScdmaRscp);
- m.putBoolean("isGsm", isGsm);
- m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+ m.putBoolean("IsGsm", mIsGsm);
+ m.putBoolean("UseOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+ ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
+ for (int value : mLteRsrpThresholds) {
+ lteRsrpThresholds.add(value);
+ }
+ m.putIntegerArrayList("lteRsrpThresholds", lteRsrpThresholds);
+ }
+
+ /**
+ * Gets the default threshold array for determining the display level of LTE signal bar.
+ *
+ * @return int array for determining the display level.
+ */
+ private int[] getDefaultLteRsrpThresholds() {
+ return CarrierConfigManager.getDefaultConfig().getIntArray(
+ CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
}
/**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 62b0f9b..9a45e7b 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -339,6 +339,7 @@
* Send a text based SMS without writing it into the SMS Provider.
*
* <p>Requires Permission:
+ * {@link android.Manifest.permission#SEND_SMS} and
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
* privileges.
* </p>
@@ -347,7 +348,10 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ android.Manifest.permission.SEND_SMS
+ })
public void sendTextMessageWithoutPersisting(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -1123,7 +1127,11 @@
// SMS send failure result codes
- /** No error. {@hide}*/
+ /**
+ * No error.
+ * @hide
+ */
+ @SystemApi
static public final int RESULT_ERROR_NONE = 0;
/** Generic failure cause */
static public final int RESULT_ERROR_GENERIC_FAILURE = 1;
@@ -1135,12 +1143,113 @@
static public final int RESULT_ERROR_NO_SERVICE = 4;
/** Failed because we reached the sending queue limit. */
static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5;
- /** Failed because FDN is enabled. {@hide} */
+ /**
+ * Failed because FDN is enabled.
+ * @hide
+ */
+ @SystemApi
static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6;
/** Failed because user denied the sending of this short code. */
static public final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;
/** Failed because the user has denied this app ever send premium short codes. */
static public final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;
+ /**
+ * Failed because the radio was not available
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_RADIO_NOT_AVAILABLE = 9;
+ /**
+ * Failed because of network rejection
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_NETWORK_REJECT = 10;
+ /**
+ * Failed because of invalid arguments
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_INVALID_ARGUMENTS = 11;
+ /**
+ * Failed because of an invalid state
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_INVALID_STATE = 12;
+ /**
+ * Failed because there is no memory
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_NO_MEMORY = 13;
+ /**
+ * Failed because the sms format is not valid
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_INVALID_SMS_FORMAT = 14;
+ /**
+ * Failed because of a system error
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_SYSTEM_ERROR = 15;
+ /**
+ * Failed because of a modem error
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_MODEM_ERROR = 16;
+ /**
+ * Failed because of a network error
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_NETWORK_ERROR = 17;
+ /**
+ * Failed because of an encoding error
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_ENCODING_ERROR = 18;
+ /**
+ * Failed because of an invalid smsc address
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_INVALID_SMSC_ADDRESS = 19;
+ /**
+ * Failed because the operation is not allowed
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_OPERATION_NOT_ALLOWED = 20;
+ /**
+ * Failed because of an internal error
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_INTERNAL_ERROR = 21;
+ /**
+ * Failed because there are no resources
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_NO_RESOURCES = 22;
+ /**
+ * Failed because the operation was cancelled
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_CANCELLED = 23;
+ /**
+ * Failed because the request is not supported
+ * @hide
+ */
+ @SystemApi
+ static public final int RESULT_REQUEST_NOT_SUPPORTED = 24;
+
static private final String PHONE_PACKAGE_NAME = "com.android.phone";
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index dcdda86..6e083c2 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Binder;
import android.os.Parcel;
import android.content.res.Resources;
@@ -534,8 +535,16 @@
/**
* Returns the originating address (sender) of this SMS message in String
- * form or null if unavailable
+ * form or null if unavailable.
+ *
+ * <p>If the address is a GSM-formatted address, it will be in a format specified by 3GPP
+ * 23.040 Sec 9.1.2.5. If it is a CDMA address, it will be a format specified by 3GPP2
+ * C.S005-D Table 2.7.1.3.2.4-2. The choice of format is carrier-specific, so callers of the
+ * should be careful to avoid assumptions about the returned content.
+ *
+ * @return a String representation of the address; null if unavailable.
*/
+ @Nullable
public String getOriginatingAddress() {
return mWrappedSmsMessage.getOriginatingAddress();
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4e1c15f..77413d9c 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -34,6 +35,8 @@
import android.util.DisplayMetrics;
import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
/**
* A Parcelable class for Subscription Information.
@@ -126,14 +129,31 @@
private UiccAccessRule[] mAccessRules;
/**
+ * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
+ * for an eUICC card.
+ */
+ private String mCardId;
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, int mcc, int mnc, String countryIso) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+ null /* accessRules */, null /* accessRules */);
+ }
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
- Bitmap icon, int mcc, int mnc, String countryIso) {
+ Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] accessRules) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
- null /* accessRules */);
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */);
}
/**
@@ -142,7 +162,7 @@
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules) {
+ @Nullable UiccAccessRule[] accessRules, String cardId) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -158,6 +178,7 @@
this.mCountryIso = countryIso;
this.mIsEmbedded = isEmbedded;
this.mAccessRules = accessRules;
+ this.mCardId = cardId;
}
/**
@@ -314,12 +335,7 @@
return this.mCountryIso;
}
- /**
- * @return whether the subscription is an embedded one.
- * @hide
- *
- * TODO(b/35851809): Make this public.
- */
+ /** @return whether the subscription is an embedded one. */
public boolean isEmbedded() {
return this.mIsEmbedded;
}
@@ -333,9 +349,9 @@
* @return whether the app is authorized to manage this subscription per its metadata.
* @throws UnsupportedOperationException if this subscription is not embedded.
* @hide
- *
- * TODO(b/35851809): Make this public.
+ * @deprecated - Do not use.
*/
+ @Deprecated
public boolean canManageSubscription(Context context) {
return canManageSubscription(context, context.getPackageName());
}
@@ -349,7 +365,9 @@
* @return whether the app is authorized to manage this subscription per its metadata.
* @throws UnsupportedOperationException if this subscription is not embedded.
* @hide
+ * @deprecated - Do not use.
*/
+ @Deprecated
public boolean canManageSubscription(Context context, String packageName) {
if (!isEmbedded()) {
throw new UnsupportedOperationException("Not an embedded subscription");
@@ -377,14 +395,22 @@
* @return the {@link UiccAccessRule}s dictating who is authorized to manage this subscription.
* @throws UnsupportedOperationException if this subscription is not embedded.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
- public @Nullable UiccAccessRule[] getAccessRules() {
+ @SystemApi
+ public @Nullable List<UiccAccessRule> getAccessRules() {
if (!isEmbedded()) {
throw new UnsupportedOperationException("Not an embedded subscription");
}
- return mAccessRules;
+ if (mAccessRules == null) return null;
+ return Arrays.asList(mAccessRules);
+ }
+
+ /**
+ * @return the ID of the SIM card which contains the subscription.
+ * @hide
+ */
+ public String getCardId() {
+ return this.mCardId;
}
public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
@@ -405,10 +431,11 @@
Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
boolean isEmbedded = source.readBoolean();
UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+ String cardId = source.readString();
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules);
+ isEmbedded, accessRules, cardId);
}
@Override
@@ -434,6 +461,7 @@
mIconBitmap.writeToParcel(dest, flags);
dest.writeBoolean(mIsEmbedded);
dest.writeTypedArray(mAccessRules, flags);
+ dest.writeString(mCardId);
}
@Override
@@ -459,11 +487,13 @@
@Override
public String toString() {
String iccIdToPrint = givePrintableIccid(mIccId);
+ String cardIdToPrint = givePrintableIccid(mCardId);
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
+ " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ " mnc " + mMnc + " isEmbedded " + mIsEmbedded
- + " accessRules " + Arrays.toString(mAccessRules) + "}";
+ + " accessRules " + Arrays.toString(mAccessRules)
+ + " cardId=" + cardIdToPrint + "}";
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c84db95..d09d426 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -23,6 +23,8 @@
import android.annotation.SystemService;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.INetworkPolicyManager;
@@ -271,6 +273,14 @@
public static final String IS_EMBEDDED = "is_embedded";
/**
+ * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
+ * current enabled profile on the card, while for eUICC card it is the EID of the card.
+ * <P>Type: TEXT (String)</P>
+ * @hide
+ */
+ public static final String CARD_ID = "card_id";
+
+ /**
* TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
* {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
* <p>TYPE: BLOB
@@ -361,6 +371,42 @@
public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
/**
+ * TelephonyProvider column name for enable Volte.
+ *@hide
+ */
+ public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+
+ /**
+ * TelephonyProvider column name for enable VT (Video Telephony over IMS)
+ *@hide
+ */
+ public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+
+ /**
+ * TelephonyProvider column name for enable Wifi calling
+ *@hide
+ */
+ public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+
+ /**
+ * TelephonyProvider column name for Wifi calling mode
+ *@hide
+ */
+ public static final String WFC_IMS_MODE = "wfc_ims_mode";
+
+ /**
+ * TelephonyProvider column name for Wifi calling mode in roaming
+ *@hide
+ */
+ public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+
+ /**
+ * TelephonyProvider column name for enable Wifi calling in roaming
+ *@hide
+ */
+ public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+
+ /**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
*
@@ -497,9 +543,9 @@
* onSubscriptionsChanged overridden.
*/
public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
- String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (DBG) {
- logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+ logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
+ " listener=" + listener);
}
try {
@@ -508,7 +554,7 @@
ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
if (tr != null) {
- tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+ tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
}
} catch (RemoteException ex) {
// Should not happen
@@ -707,10 +753,13 @@
* if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
* then by {@link SubscriptionInfo#getSubscriptionId}.
* </ul>
- * @hide
*
- * TODO(b/35851809): Make this a SystemApi.
+ * <p>
+ * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+ * for #getAvailableSubscriptionInfoList to be invoked.
+ * @hide
*/
+ @SystemApi
public List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
List<SubscriptionInfo> result = null;
@@ -748,9 +797,6 @@
* if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
* then by {@link SubscriptionInfo#getSubscriptionId}.
* </ul>
- * @hide
- *
- * TODO(b/35851809): Make this public.
*/
public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
List<SubscriptionInfo> result = null;
@@ -776,9 +822,8 @@
*
* <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public void requestEmbeddedSubscriptionInfoListRefresh() {
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
@@ -1624,4 +1669,51 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Checks whether the app with the given context is authorized to manage the given subscription
+ * according to its metadata. Only supported for embedded subscriptions (if
+ * {@code SubscriptionInfo#isEmbedded} returns true).
+ *
+ * @param info The subscription to check.
+ * @return whether the app is authorized to manage this subscription per its metadata.
+ * @throws UnsupportedOperationException if this subscription is not embedded.
+ */
+ public boolean canManageSubscription(SubscriptionInfo info) {
+ return canManageSubscription(info, mContext.getPackageName());
+ }
+
+ /**
+ * Checks whether the given app is authorized to manage the given subscription according to its
+ * metadata. Only supported for embedded subscriptions (if {@code SubscriptionInfo#isEmbedded}
+ * returns true).
+ *
+ * @param info The subscription to check.
+ * @param packageName Package name of the app to check.
+ * @return whether the app is authorized to manage this subscription per its metadata.
+ * @throws UnsupportedOperationException if this subscription is not embedded.
+ * @hide
+ */
+ public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
+ if (!info.isEmbedded()) {
+ throw new UnsupportedOperationException("Not an embedded subscription");
+ }
+ if (info.getAccessRules() == null) {
+ return false;
+ }
+ PackageManager packageManager = mContext.getPackageManager();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown package: " + packageName, e);
+ }
+ for (UiccAccessRule rule : info.getAccessRules()) {
+ if (rule.getCarrierPrivilegeStatus(packageInfo)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index d7b6142..e7f0cc2 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -33,6 +33,7 @@
import android.telephony.ServiceState;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Patterns;
@@ -1102,6 +1103,16 @@
"android.provider.Telephony.MMS_DOWNLOADED";
/**
+ * Broadcast Action: A debug code has been entered in the dialer. These "secret codes"
+ * are used to activate developer menus by dialing certain codes. And they are of the
+ * form {@code *#*#<code>#*#*}. The intent will have the data URI:
+ * {@code android_secret_code://<code>}.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SECRET_CODE_ACTION =
+ "android.provider.Telephony.SECRET_CODE";
+
+ /**
* Broadcast action: When the default SMS package changes,
* the previous default SMS package and the new default SMS
* package are sent this broadcast to notify them of the change.
@@ -2564,6 +2575,35 @@
public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
/**
+ * The {@code content://} style URL to be called from DevicePolicyManagerService,
+ * can manage DPC-owned APNs.
+ * @hide
+ */
+ public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
+
+ /**
+ * The {@code content://} style URL to be called from Telephony to query APNs.
+ * When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only
+ * non-DPC-owned APNs are returned.
+ * @hide
+ */
+ public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered");
+
+ /**
+ * The {@code content://} style URL to be called from DevicePolicyManagerService
+ * or Telephony to manage whether DPC-owned APNs are enforced.
+ * @hide
+ */
+ public static final Uri ENFORCE_MANAGED_URI = Uri.parse(
+ "content://telephony/carriers/enforce_managed");
+
+ /**
+ * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
+ * @hide
+ */
+ public static final String ENFORCE_KEY = "enforced";
+
+ /**
* The default sort order for this table.
*/
public static final String DEFAULT_SORT_ORDER = "name ASC";
@@ -2692,7 +2732,9 @@
* This should be spread to other technologies,
* but is currently only used for LTE (14) and eHRPD (13).
* <P>Type: INTEGER</P>
+ * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead
*/
+ @Deprecated
public static final String BEARER = "bearer";
/**
@@ -2703,10 +2745,21 @@
* Bitmask for a radio tech R is (1 << (R - 1))
* <P>Type: INTEGER</P>
* @hide
+ * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead
*/
+ @Deprecated
public static final String BEARER_BITMASK = "bearer_bitmask";
/**
+ * Radio technology (network type) bitmask.
+ * To check what values can be contained, refer to the NETWORK_TYPE_ constants in
+ * {@link android.telephony.TelephonyManager}.
+ * Bitmask for a radio tech R is (1 << (R - 1))
+ * <P>Type: INTEGER</P>
+ */
+ public static final String NETWORK_TYPE_BITMASK = "network_type_bitmask";
+
+ /**
* MVNO type:
* {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
* <P>Type: TEXT</P>
@@ -3295,67 +3348,118 @@
}
/**
- * Contains carrier identification information.
- * @hide
+ * Contains carrier identification information for the current subscriptions.
+ * @see SubscriptionManager#getActiveSubscriptionIdList()
*/
public static final class CarrierIdentification implements BaseColumns {
/**
- * Numeric operator ID (as String). {@code MCC + MNC}
- * <P>Type: TEXT </P>
+ * Not instantiable.
+ * @hide
*/
- public static final String MCCMNC = "mccmnc";
+ private CarrierIdentification() {}
/**
- * Group id level 1 (as String).
- * <P>Type: TEXT </P>
+ * The {@code content://} style URI for this provider.
*/
- public static final String GID1 = "gid1";
+ public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification");
/**
- * Group id level 2 (as String).
- * <P>Type: TEXT </P>
+ * The authority string for the CarrierIdentification Provider
+ * @hide
*/
- public static final String GID2 = "gid2";
+ public static final String AUTHORITY = "carrier_identification";
+
/**
- * Public Land Mobile Network name.
- * <P>Type: TEXT </P>
+ * Generates a content {@link Uri} used to receive updates on carrier identity change
+ * on the given subscriptionId
+ * <p>
+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+ * carrier identity {@link TelephonyManager#getAndroidCarrierIdForSubscription()}
+ * while your app is running. You can also use a {@link JobService} to ensure your app
+ * is notified of changes to the {@link Uri} even when it is not running.
+ * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+ * updates to the {@link Uri}.
+ *
+ * @param subscriptionId the subscriptionId to receive updates on
+ * @return the Uri used to observe carrier identity changes
*/
- public static final String PLMN = "plmn";
+ public static Uri getUriForSubscriptionId(int subscriptionId) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(
+ String.valueOf(subscriptionId)).build();
+ }
/**
- * Prefix xpattern of IMSI (International Mobile Subscriber Identity).
- * <P>Type: TEXT </P>
- */
- public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
-
- /**
- * Service Provider Name.
- * <P>Type: TEXT </P>
- */
- public static final String SPN = "spn";
-
- /**
- * Prefer APN name.
- * <P>Type: TEXT </P>
- */
- public static final String APN = "apn";
-
- /**
- * User facing carrier name.
+ * A user facing carrier name.
+ * @see TelephonyManager#getAndroidCarrierNameForSubscription()
* <P>Type: TEXT </P>
*/
public static final String NAME = "carrier_name";
/**
* A unique carrier id
+ * @see TelephonyManager#getAndroidCarrierIdForSubscription()
* <P>Type: INTEGER </P>
*/
public static final String CID = "carrier_id";
/**
- * The {@code content://} URI for this table.
+ * Contains mappings between matching rules with carrier id for all carriers.
+ * @hide
*/
- public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification");
+ public static final class All implements BaseColumns {
+ /**
+ * Numeric operator ID (as String). {@code MCC + MNC}
+ * <P>Type: TEXT </P>
+ */
+ public static final String MCCMNC = "mccmnc";
+
+ /**
+ * Group id level 1 (as String).
+ * <P>Type: TEXT </P>
+ */
+ public static final String GID1 = "gid1";
+
+ /**
+ * Group id level 2 (as String).
+ * <P>Type: TEXT </P>
+ */
+ public static final String GID2 = "gid2";
+
+ /**
+ * Public Land Mobile Network name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String PLMN = "plmn";
+
+ /**
+ * Prefix xpattern of IMSI (International Mobile Subscriber Identity).
+ * <P>Type: TEXT </P>
+ */
+ public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
+
+ /**
+ * Service Provider Name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String SPN = "spn";
+
+ /**
+ * Prefer APN name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String APN = "apn";
+
+ /**
+ * Prefix of Integrated Circuit Card Identifier.
+ * <P>Type: TEXT </P>
+ */
+ public static final String ICCID_PREFIX = "iccid_prefix";
+
+ /**
+ * The {@code content://} URI for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification/all");
+ }
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4505994..0c92a68 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -22,14 +22,13 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.WorkerThread;
import android.app.ActivityThread;
import android.app.PendingIntent;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
@@ -43,17 +42,19 @@
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.ITelecomService;
@@ -61,7 +62,6 @@
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyProperties;
@@ -893,6 +893,61 @@
public static final String EVENT_CALL_FORWARDED =
"android.telephony.event.EVENT_CALL_FORWARDED";
+ /**
+ * {@link android.telecom.Connection} event used to indicate that a supplementary service
+ * notification has been received.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to include the following extras:
+ * <ul>
+ * <li>{@link #EXTRA_NOTIFICATION_TYPE} - the notification type.</li>
+ * <li>{@link #EXTRA_NOTIFICATION_CODE} - the notification code.</li>
+ * <li>{@link #EXTRA_NOTIFICATION_MESSAGE} - human-readable message associated with the
+ * supplementary service notification.</li>
+ * </ul>
+ * @hide
+ */
+ public static final String EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION =
+ "android.telephony.event.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION";
+
+ /**
+ * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+ * the type of supplementary service notification which occurred.
+ * Will be either
+ * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_1}
+ * or
+ * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_2}
+ * <p>
+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_TYPE =
+ "android.telephony.extra.NOTIFICATION_TYPE";
+
+ /**
+ * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+ * the supplementary service notification which occurred.
+ * <p>
+ * Depending on the {@link #EXTRA_NOTIFICATION_TYPE}, the code will be one of the {@code CODE_*}
+ * codes defined in {@link com.android.internal.telephony.gsm.SuppServiceNotification}.
+ * <p>
+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_CODE =
+ "android.telephony.extra.NOTIFICATION_CODE";
+
+ /**
+ * {@link CharSequence} extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION}
+ * which contains a human-readable message which can be displayed to the user for the
+ * supplementary service notification.
+ * <p>
+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_MESSAGE =
+ "android.telephony.extra.NOTIFICATION_MESSAGE";
+
/* Visual voicemail protocols */
/**
@@ -957,6 +1012,64 @@
*/
public static final int USSD_ERROR_SERVICE_UNAVAIL = -2;
+ /**
+ * An unknown carrier id. It could either be subscription unavailable or the subscription
+ * carrier cannot be recognized. Unrecognized carriers here means
+ * {@link #getSimOperator() MCC+MNC} cannot be identified.
+ */
+ public static final int UNKNOWN_CARRIER_ID = -1;
+
+ /**
+ * Broadcast Action: The subscription carrier identity has changed.
+ * This intent could be sent on the following events:
+ * <ul>
+ * <li>Subscription absent. Carrier identity could change from a valid id to
+ * {@link TelephonyManager#UNKNOWN_CARRIER_ID}.</li>
+ * <li>Subscription loaded. Carrier identity could change from
+ * {@link TelephonyManager#UNKNOWN_CARRIER_ID} to a valid id.</li>
+ * <li>The subscription carrier is recognized after a remote update.</li>
+ * </ul>
+ * The intent will have the following extra values:
+ * <ul>
+ * <li>{@link #EXTRA_CARRIER_ID} The up-to-date carrier id of the current subscription id.
+ * </li>
+ * <li>{@link #EXTRA_CARRIER_NAME} The up-to-date carrier name of the current subscription.
+ * </li>
+ * <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
+ * identity.
+ * </li>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED =
+ "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
+
+ /**
+ * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
+ * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+ * the current subscription.
+ * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
+ * the carrier cannot be identified.
+ */
+ public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
+
+ /**
+ * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
+ * indicates the updated carrier name of the current subscription.
+ * {@see TelephonyManager#getSubscriptionCarrierName()}
+ * <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
+ * usually the brand name of the subsidiary (e.g. T-Mobile).
+ */
+ public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
+
+ /**
+ * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
+ * subscription which has changed.
+ */
+ public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
+
+
//
//
// Device Info
@@ -1099,12 +1212,14 @@
}
/**
- * Returns the NAI. Return null if NAI is not available.
- *
+ * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- /** {@hide}*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getNai() {
- return getNai(getSlotIndex());
+ return getNaiBySubscriberId(getSubId());
}
/**
@@ -1115,11 +1230,18 @@
/** {@hide}*/
public String getNai(int slotIndex) {
int[] subId = SubscriptionManager.getSubId(slotIndex);
+ if (subId == null) {
+ return null;
+ }
+ return getNaiBySubscriberId(subId[0]);
+ }
+
+ private String getNaiBySubscriberId(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- String nai = info.getNaiForSubscriber(subId[0], mContext.getOpPackageName());
+ String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName());
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Rlog.v(TAG, "Nai = " + nai);
}
@@ -2025,6 +2147,110 @@
* carrier restrictions.
*/
public static final int SIM_STATE_CARD_RESTRICTED = 9;
+ /**
+ * SIM card state: Loaded: SIM card applications have been loaded
+ * @hide
+ */
+ @SystemApi
+ public static final int SIM_STATE_LOADED = 10;
+ /**
+ * SIM card state: SIM Card is present
+ * @hide
+ */
+ @SystemApi
+ public static final int SIM_STATE_PRESENT = 11;
+
+ /**
+ * Extra included in {@link #ACTION_SIM_CARD_STATE_CHANGED} and
+ * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
+
+ /**
+ * Broadcast Action: The sim card state has changed.
+ * The intent will have the following extra values:</p>
+ * <dl>
+ * <dt>{@link #EXTRA_SIM_STATE}</dt>
+ * <dd>The sim card state. One of:
+ * <dl>
+ * <dt>{@link #SIM_STATE_ABSENT}</dt>
+ * <dd>SIM card not found</dd>
+ * <dt>{@link #SIM_STATE_CARD_IO_ERROR}</dt>
+ * <dd>SIM card IO error</dd>
+ * <dt>{@link #SIM_STATE_CARD_RESTRICTED}</dt>
+ * <dd>SIM card is restricted</dd>
+ * <dt>{@link #SIM_STATE_PRESENT}</dt>
+ * <dd>SIM card is present</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ *
+ * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+ *
+ * <p class="note">The current state can also be queried using {@link #getSimCardState()}.
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SIM_CARD_STATE_CHANGED =
+ "android.telephony.action.SIM_CARD_STATE_CHANGED";
+
+ /**
+ * Broadcast Action: The sim application state has changed.
+ * The intent will have the following extra values:</p>
+ * <dl>
+ * <dt>{@link #EXTRA_SIM_STATE}</dt>
+ * <dd>The sim application state. One of:
+ * <dl>
+ * <dt>{@link #SIM_STATE_NOT_READY}</dt>
+ * <dd>SIM card applications not ready</dd>
+ * <dt>{@link #SIM_STATE_PIN_REQUIRED}</dt>
+ * <dd>SIM card PIN locked</dd>
+ * <dt>{@link #SIM_STATE_PUK_REQUIRED}</dt>
+ * <dd>SIM card PUK locked</dd>
+ * <dt>{@link #SIM_STATE_NETWORK_LOCKED}</dt>
+ * <dd>SIM card network locked</dd>
+ * <dt>{@link #SIM_STATE_PERM_DISABLED}</dt>
+ * <dd>SIM card permanently disabled due to PUK failures</dd>
+ * <dt>{@link #SIM_STATE_LOADED}</dt>
+ * <dd>SIM card data loaded</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ *
+ * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+ *
+ * <p class="note">The current state can also be queried using
+ * {@link #getSimApplicationState()}.
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SIM_APPLICATION_STATE_CHANGED =
+ "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+
+ /**
+ * Broadcast Action: Status of the SIM slots on the device has changed.
+ *
+ * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+ *
+ * <p class="note">The status can be queried using
+ * {@link #getUiccSlotsInfo()}
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SIM_SLOT_STATUS_CHANGED =
+ "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
/**
* @return true if a ICC card is present
@@ -2071,6 +2297,14 @@
* @see #SIM_STATE_CARD_RESTRICTED
*/
public int getSimState() {
+ int simState = getSimStateIncludingLoaded();
+ if (simState == SIM_STATE_LOADED) {
+ simState = SIM_STATE_READY;
+ }
+ return simState;
+ }
+
+ private int getSimStateIncludingLoaded() {
int slotIndex = getSlotIndex();
// slotIndex may be invalid due to sim being absent. In that case query all slots to get
// sim state
@@ -2089,7 +2323,63 @@
"state as absent");
return SIM_STATE_ABSENT;
}
- return getSimState(slotIndex);
+ return SubscriptionManager.getSimStateForSlotIndex(slotIndex);
+ }
+
+ /**
+ * Returns a constant indicating the state of the default SIM card.
+ *
+ * @see #SIM_STATE_UNKNOWN
+ * @see #SIM_STATE_ABSENT
+ * @see #SIM_STATE_CARD_IO_ERROR
+ * @see #SIM_STATE_CARD_RESTRICTED
+ * @see #SIM_STATE_PRESENT
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getSimCardState() {
+ int simCardState = getSimState();
+ switch (simCardState) {
+ case SIM_STATE_UNKNOWN:
+ case SIM_STATE_ABSENT:
+ case SIM_STATE_CARD_IO_ERROR:
+ case SIM_STATE_CARD_RESTRICTED:
+ return simCardState;
+ default:
+ return SIM_STATE_PRESENT;
+ }
+ }
+
+ /**
+ * Returns a constant indicating the state of the card applications on the default SIM card.
+ *
+ * @see #SIM_STATE_UNKNOWN
+ * @see #SIM_STATE_PIN_REQUIRED
+ * @see #SIM_STATE_PUK_REQUIRED
+ * @see #SIM_STATE_NETWORK_LOCKED
+ * @see #SIM_STATE_NOT_READY
+ * @see #SIM_STATE_PERM_DISABLED
+ * @see #SIM_STATE_LOADED
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getSimApplicationState() {
+ int simApplicationState = getSimStateIncludingLoaded();
+ switch (simApplicationState) {
+ case SIM_STATE_UNKNOWN:
+ case SIM_STATE_ABSENT:
+ case SIM_STATE_CARD_IO_ERROR:
+ case SIM_STATE_CARD_RESTRICTED:
+ return SIM_STATE_UNKNOWN;
+ case SIM_STATE_READY:
+ // Ready is not a valid state anymore. The state that is broadcast goes from
+ // NOT_READY to either LOCKED or LOADED.
+ return SIM_STATE_NOT_READY;
+ default:
+ return simApplicationState;
+ }
}
/**
@@ -2110,6 +2400,9 @@
*/
public int getSimState(int slotIndex) {
int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex);
+ if (simState == SIM_STATE_LOADED) {
+ simState = SIM_STATE_READY;
+ }
return simState;
}
@@ -2331,6 +2624,53 @@
}
}
+ /**
+ * Gets all the UICC slots.
+ *
+ * @return UiccSlotInfo array.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public UiccSlotInfo[] getUiccSlotsInfo() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return null;
+ }
+ return telephony.getUiccSlotsInfo();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. For
+ * example, passing the physicalSlots array [1, 0] means mapping the first item 1, which is
+ * physical slot index 1, to the logical slot 0; and mapping the second item 0, which is
+ * physical slot index 0, to the logical slot 1. The index of the array means the index of the
+ * logical slots.
+ *
+ * @param physicalSlots Index i in the array representing physical slot for phone i. The array
+ * size should be same as {@link #getPhoneCount()}.
+ * @return boolean Return true if the switch succeeds, false if the switch fails.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean switchSlots(int[] physicalSlots) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return false;
+ }
+ return telephony.switchSlots(physicalSlots);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
//
//
// Subscriber Info
@@ -3540,6 +3880,9 @@
* To unregister a listener, pass the listener object and set the
* events argument to
* {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+ * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+ * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+ * {@link SecurityException} will be thrown otherwise.
*
* @param listener The {@link PhoneStateListener} object to register
* (or unregister)
@@ -4005,7 +4348,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.iccTransmitApduBasicChannel(subId, cla,
+ return telephony.iccTransmitApduBasicChannel(subId, getOpPackageName(), cla,
instruction, p1, p2, p3, data);
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
@@ -4288,7 +4631,7 @@
}
/**
- * Sets the telephony property with the value specified.
+ * Sets a per-phone telephony property with the value specified.
*
* @hide
*/
@@ -4332,12 +4675,24 @@
return;
}
- Rlog.d(TAG, "setTelephonyProperty: success phoneId=" + phoneId +
- " property=" + property + " value: " + value + " propVal=" + propVal);
SystemProperties.set(property, propVal);
}
/**
+ * Sets a global telephony property with the value specified.
+ *
+ * @hide
+ */
+ public static void setTelephonyProperty(String property, String value) {
+ if (value == null) {
+ value = "";
+ }
+ Rlog.d(TAG, "setTelephonyProperty: success" + " property=" +
+ property + " value: " + value);
+ SystemProperties.set(property, value);
+ }
+
+ /**
* Convenience function for retrieving a value from the secure settings
* value list as an integer. Note that internally setting values are
* always stored as strings; this function converts the string to an
@@ -4426,7 +4781,7 @@
}
/**
- * Gets the telephony property.
+ * Gets a per-phone telephony property.
*
* @hide
*/
@@ -4442,6 +4797,19 @@
return propVal == null ? defaultVal : propVal;
}
+ /**
+ * Gets a global telephony property.
+ *
+ * See also getTelephonyProperty(phoneId, property, defaultVal). Most telephony properties are
+ * per-phone.
+ *
+ * @hide
+ */
+ public static String getTelephonyProperty(String property, String defaultVal) {
+ String propVal = SystemProperties.get(property);
+ return propVal == null ? defaultVal : propVal;
+ }
+
/** @hide */
public int getSimCount() {
// FIXME Need to get it from Telephony Dev Controller when that gets implemented!
@@ -4494,28 +4862,6 @@
}
}
- /**
- * Returns the response of ISIM Authetification through RIL.
- * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
- * @return the response of ISIM Authetification, or null if not available
- * @hide
- * @deprecated
- * @see getIccAuthentication with appType=PhoneConstants.APPTYPE_ISIM
- */
- public String getIsimChallengeResponse(String nonce){
- try {
- IPhoneSubInfo info = getSubscriberInfo();
- if (info == null)
- return null;
- return info.getIsimChallengeResponse(nonce);
- } catch (RemoteException ex) {
- return null;
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
- return null;
- }
- }
-
// ICC SIM Application Types
/** UICC application type is SIM */
public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
@@ -4637,57 +4983,60 @@
}
}
- /** @hide */
- @IntDef({ImsFeature.EMERGENCY_MMTEL, ImsFeature.MMTEL, ImsFeature.RCS})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Feature {}
-
/**
- * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
- * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
- * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
- * it is unavailable.
+ * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
+ * status updates, if not already enabled.
* @hide
*/
- public @Nullable IImsMMTelFeature getImsMMTelFeatureAndListen(int slotIndex,
- IImsServiceFeatureCallback callback) {
+ public void enableIms(int slotId) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getMMTelFeatureAndListen(slotIndex, callback);
+ telephony.enableIms(slotId);
}
} catch (RemoteException e) {
- Rlog.e(TAG, "getImsMMTelFeatureAndListen, RemoteException: "
+ Rlog.e(TAG, "enableIms, RemoteException: "
+ e.getMessage());
}
- return null;
}
/**
- * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
- * feature for emergency calling or {@link null} if the service is not available. If an
- * MMTelFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
- * listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
+ * Disables IMS for the framework. This will trigger IMS de-registration and trigger ImsFeature
+ * status updates to disabled.
+ * @hide
+ */
+ public void disableIms(int slotId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.disableIms(slotId);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "disableIms, RemoteException: "
+ + e.getMessage());
+ }
+ }
+
+ /**
+ * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
+ * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
+ * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+ * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
* @param callback Listener that will send updates to ImsManager when there are updates to
* ImsServiceController.
- * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
+ * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
* it is unavailable.
* @hide
*/
- public @Nullable IImsMMTelFeature getImsEmergencyMMTelFeatureAndListen(int slotIndex,
+ public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
IImsServiceFeatureCallback callback) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getEmergencyMMTelFeatureAndListen(slotIndex, callback);
+ return telephony.getMmTelFeatureAndListen(slotIndex, callback);
}
} catch (RemoteException e) {
- Rlog.e(TAG, "getImsEmergencyMMTelFeatureAndListen, RemoteException: "
+ Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
+ e.getMessage());
}
return null;
@@ -4720,6 +5069,44 @@
}
/**
+ * @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
+ * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
+ * @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
+ * Corresponds to features defined in ImsFeature.
+ * @hide
+ */
+ public @Nullable IImsRegistration getImsRegistration(int slotIndex, int feature) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getImsRegistration(slotIndex, feature);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * @return the {@IImsConfig} interface that corresponds with the slot index and feature.
+ * @param slotIndex The SIM slot corresponding to the ImsService ImsConfig is active for.
+ * @param feature An integer indicating the feature that we wish to get the ImsConfig for.
+ * Corresponds to features defined in ImsFeature.
+ * @hide
+ */
+ public @Nullable IImsConfig getImsConfig(int slotIndex, int feature) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getImsConfig(slotIndex, feature);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
* Set IMS registration state
*
* @param Registration state
@@ -5676,39 +6063,38 @@
* @param enable Whether to enable mobile data.
*
* @see #hasCarrierPrivileges
+ * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(boolean enable) {
- setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
+ setUserMobileDataEnabled(enable);
}
- /** @hide */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setDataEnabled(int subId, boolean enable) {
- try {
- Log.d(TAG, "setDataEnabled: enabled=" + enable);
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.setDataEnabled(subId, enable);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
- }
- }
-
-
/**
- * @deprecated use {@link #isDataEnabled()} instead.
+ * @hide
+ * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+ */
+ @SystemApi
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDataEnabled(int subId, boolean enable) {
+ setUserMobileDataEnabled(subId, enable);
+ }
+
+ /**
+ * @deprecated use {@link #isUserMobileDataEnabled()} instead.
* @hide
*/
@SystemApi
@Deprecated
public boolean getDataEnabled() {
- return isDataEnabled();
+ return isUserMobileDataEnabled();
}
/**
- * Returns whether mobile data is enabled or not.
+ * Returns whether mobile data is enabled or not per user setting. There are other factors
+ * that could disable mobile data, but they are not considered here.
*
* If this object has been created with {@link #createForSubscriptionId}, applies to the given
* subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
@@ -5725,28 +6111,21 @@
* @return true if mobile data is enabled.
*
* @see #hasCarrierPrivileges
+ * @deprecated use {@link #isUserMobileDataEnabled()} instead.
*/
- @SuppressWarnings("deprecation")
+ @Deprecated
public boolean isDataEnabled() {
- return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ return isUserMobileDataEnabled();
}
/**
- * @deprecated use {@link #isDataEnabled(int)} instead.
+ * @deprecated use {@link #isUserMobileDataEnabled()} instead.
* @hide
*/
+ @Deprecated
@SystemApi
public boolean getDataEnabled(int subId) {
- boolean retVal = false;
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- retVal = telephony.getDataEnabled(subId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
- } catch (NullPointerException e) {
- }
- return retVal;
+ return isUserMobileDataEnabled(subId);
}
/** @hide */
@@ -5860,122 +6239,105 @@
}
/**
- * This function retrieves value for setting "name+subId", and if that is not found
- * retrieves value for setting "name", and if that is not found throws
- * SettingNotFoundException
- *
- * @hide
- */
- public static int getIntWithSubId(ContentResolver cr, String name, int subId)
- throws SettingNotFoundException {
- try {
- return Settings.Global.getInt(cr, name + subId);
- } catch (SettingNotFoundException e) {
- try {
- int val = Settings.Global.getInt(cr, name);
- Settings.Global.putInt(cr, name + subId, val);
-
- /* We are now moving from 'setting' to 'setting+subId', and using the value stored
- * for 'setting' as default. Reset the default (since it may have a user set
- * value). */
- int default_val = val;
- if (name.equals(Settings.Global.MOBILE_DATA)) {
- default_val = "true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.mobiledata", "true")) ? 1 : 0;
- } else if (name.equals(Settings.Global.DATA_ROAMING)) {
- default_val = "true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.dataroaming", "false")) ? 1 : 0;
- }
-
- if (default_val != val) {
- Settings.Global.putInt(cr, name, default_val);
- }
-
- return val;
- } catch (SettingNotFoundException exc) {
- throw new SettingNotFoundException(name);
- }
- }
- }
-
- /**
- * Returns the IMS Registration Status
- * @hide
- */
- public boolean isImsRegistered() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony == null)
- return false;
- return telephony.isImsRegistered();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
- return false;
- }
- }
-
- /**
- * Returns the IMS Registration Status for a particular Subscription ID
+ * Returns the IMS Registration Status for a particular Subscription ID.
*
* @param subId Subscription ID
* @return true if IMS status is registered, false if the IMS status is not registered or a
* RemoteException occurred.
- *
* @hide
*/
public boolean isImsRegistered(int subId) {
- try {
- return getITelephony().isImsRegisteredForSubscriber(subId);
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
- return false;
- }
- }
-
- /**
- * Returns the Status of Volte
- * @hide
- */
- public boolean isVolteAvailable() {
- try {
- return getITelephony().isVolteAvailable();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
- return false;
- }
- }
-
- /**
- * Returns the Status of video telephony (VT)
- * @hide
- */
- public boolean isVideoTelephonyAvailable() {
try {
- return getITelephony().isVideoTelephonyAvailable();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
+ return getITelephony().isImsRegistered(subId);
+ } catch (RemoteException | NullPointerException ex) {
return false;
}
}
/**
- * Returns the Status of Wi-Fi Calling
+ * Returns the IMS Registration Status for a particular Subscription ID, which is determined
+ * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an
+ * invalid subscription ID is used during creation, will the default subscription ID will be
+ * used.
+ *
+ * @return true if IMS status is registered, false if the IMS status is not registered or a
+ * RemoteException occurred.
+ * @see SubscriptionManager#getDefaultSubscriptionId()
+ * @hide
+ */
+ public boolean isImsRegistered() {
+ try {
+ return getITelephony().isImsRegistered(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * The current status of Voice over LTE for the subscription associated with this instance when
+ * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+ * used during creation, the default subscription ID will be used.
+ * @return true if Voice over LTE is available or false if it is unavailable or unknown.
+ * @see SubscriptionManager#getDefaultSubscriptionId()
+ * @hide
+ */
+ public boolean isVolteAvailable() {
+ try {
+ return getITelephony().isVolteAvailable(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * The availability of Video Telephony (VT) for the subscription ID specified when this instance
+ * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+ * used during creation, the default subscription ID will be used. To query the
+ * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}.
+ * @return true if VT is available, or false if it is unavailable or unknown.
+ * @hide
+ */
+ public boolean isVideoTelephonyAvailable() {
+ try {
+ return getITelephony().isVideoTelephonyAvailable(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified.
+ * @param subId the subscription ID.
+ * @return true if VoWiFi is available, or false if it is unavailable or unknown.
* @hide
*/
public boolean isWifiCallingAvailable() {
try {
- return getITelephony().isWifiCallingAvailable();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
+ return getITelephony().isWifiCallingAvailable(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
return false;
}
}
+ /**
+ * The technology that IMS is registered for for the MMTEL feature.
+ * @param subId subscription ID to get IMS registration technology for.
+ * @return The IMS registration technology that IMS is registered to for the MMTEL feature.
+ * Valid return results are:
+ * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration,
+ * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
+ * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
+ * result is unavailable.
+ * @hide
+ */
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
+ try {
+ return getITelephony().getImsRegTechnologyForMmTel(getSubId());
+ } catch (RemoteException ex) {
+ return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+ }
+ }
+
/**
* Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
*
@@ -6595,6 +6957,61 @@
}
/**
+ * Returns carrier id of the current subscription.
+ * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
+ * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an
+ * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+ * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+ *
+ * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
+ * as an Android platform-wide identifier for carriers.
+ *
+ * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
+ * subscription is unavailable or the carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ */
+ public int getAndroidCarrierIdForSubscription() {
+ try {
+ ITelephony service = getITelephony();
+ return service.getSubscriptionCarrierId(getSubId());
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ ex.rethrowAsRuntimeException();
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing.
+ throw new IllegalStateException("Telephony service unavailable");
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
+ /**
+ * Returns carrier name of the current subscription.
+ * <p>Carrier name is a user-facing name of carrier id
+ * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+ * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
+ * should have a single carrier name. Carrier name is not a canonical identity,
+ * use {@link #getAndroidCarrierIdForSubscription()} instead.
+ * <p>The returned carrier name is unlocalized.
+ *
+ * @return Carrier name of the current subscription. Return {@code null} if the subscription is
+ * unavailable or the carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ */
+ public CharSequence getAndroidCarrierNameForSubscription() {
+ try {
+ ITelephony service = getITelephony();
+ return service.getSubscriptionCarrierName(getSubId());
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ ex.rethrowAsRuntimeException();
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing.
+ throw new IllegalStateException("Telephony service unavailable");
+ }
+ return null;
+ }
+
+ /**
* Return the application ID for the app type like {@link APPTYPE_CSIM}.
*
* Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
@@ -6937,4 +7354,101 @@
}
return null;
}
+
+ /**
+ * Turns mobile data on or off.
+ * If the {@link TelephonyManager} object has been created with
+ * {@link #createForSubscriptionId}, this API applies to the given subId.
+ * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+ * calling app has carrier privileges.
+ *
+ * @param enable Whether to enable mobile data.
+ *
+ * @see #hasCarrierPrivileges
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setUserMobileDataEnabled(boolean enable) {
+ setUserMobileDataEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
+ }
+
+ /**
+ * Returns whether mobile data is enabled or not per user setting. There are other factors
+ * that could disable mobile data, but they are not considered here.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires one of the following permissions:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
+ * calling app has carrier privileges.
+ *
+ * <p>Note that this does not take into account any data restrictions that may be present on the
+ * calling app. Such restrictions may be inspected with
+ * {@link ConnectivityManager#getRestrictBackgroundStatus}.
+ *
+ * @return true if mobile data is enabled.
+ *
+ * @see #hasCarrierPrivileges
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.ACCESS_NETWORK_STATE,
+ android.Manifest.permission.MODIFY_PHONE_STATE
+ })
+ public boolean isUserMobileDataEnabled() {
+ return isUserMobileDataEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ }
+
+ /**
+ * @hide
+ * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
+ * policyDataEnabled etc to give a final decision.
+ */
+ public boolean isMobileDataEnabled() {
+ boolean retVal = false;
+ try {
+ int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ retVal = telephony.isDataEnabled(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isDataEnabled", e);
+ } catch (NullPointerException e) {
+ }
+ return retVal;
+ }
+
+ /**
+ * Utility class of {@link #isUserMobileDataEnabled()};
+ */
+ private boolean isUserMobileDataEnabled(int subId) {
+ boolean retVal = false;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ retVal = telephony.isUserDataEnabled(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+ } catch (NullPointerException e) {
+ }
+ return retVal;
+ }
+
+ /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ private void setUserMobileDataEnabled(int subId, boolean enable) {
+ try {
+ Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setUserDataEnabled(subId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index e42a758..c3f8a19 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -16,6 +16,7 @@
package android.telephony;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
import android.os.Parcel;
@@ -32,15 +33,15 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import java.util.Objects;
/**
* Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control
* specification.
*
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class UiccAccessRule implements Parcelable {
private static final String TAG = "UiccAccessRule";
@@ -205,6 +206,21 @@
}
@Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ UiccAccessRule that = (UiccAccessRule) obj;
+ return Arrays.equals(mCertificateHash, that.mCertificateHash)
+ && Objects.equals(mPackageName, that.mPackageName)
+ && mAccessType == that.mAccessType;
+ }
+
+ @Override
public String toString() {
return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " +
mPackageName + " access: " + mAccessType;
diff --git a/telephony/java/com/android/ims/ImsSsData.aidl b/telephony/java/android/telephony/UiccSlotInfo.aidl
similarity index 83%
copy from telephony/java/com/android/ims/ImsSsData.aidl
copy to telephony/java/android/telephony/UiccSlotInfo.aidl
index 33f8306..5571a6c 100644
--- a/telephony/java/com/android/ims/ImsSsData.aidl
+++ b/telephony/java/android/telephony/UiccSlotInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony;
-parcelable ImsSsData;
+parcelable UiccSlotInfo;
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
new file mode 100644
index 0000000..0c17147
--- /dev/null
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -0,0 +1,170 @@
+/*
+ * 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;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+import android.annotation.IntDef;
+
+/**
+ * Class for the information of a UICC slot.
+ * @hide
+ */
+@SystemApi
+public class UiccSlotInfo implements Parcelable {
+ /**
+ * Card state.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CARD_STATE_INFO_" }, value = {
+ CARD_STATE_INFO_ABSENT,
+ CARD_STATE_INFO_PRESENT,
+ CARD_STATE_INFO_ERROR,
+ CARD_STATE_INFO_RESTRICTED
+ })
+ public @interface CardStateInfo {}
+
+ /** Card state absent. */
+ public static final int CARD_STATE_INFO_ABSENT = 1;
+
+ /** Card state present. */
+ public static final int CARD_STATE_INFO_PRESENT = 2;
+
+ /** Card state error. */
+ public static final int CARD_STATE_INFO_ERROR = 3;
+
+ /** Card state restricted. */
+ public static final int CARD_STATE_INFO_RESTRICTED = 4;
+
+ private final boolean mIsActive;
+ private final boolean mIsEuicc;
+ private final String mCardId;
+ private final @CardStateInfo int mCardStateInfo;
+ private final int mLogicalSlotIdx;
+
+ public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
+ @Override
+ public UiccSlotInfo createFromParcel(Parcel in) {
+ return new UiccSlotInfo(in);
+ }
+
+ @Override
+ public UiccSlotInfo[] newArray(int size) {
+ return new UiccSlotInfo[size];
+ }
+ };
+
+ private UiccSlotInfo(Parcel in) {
+ mIsActive = in.readByte() != 0;
+ mIsEuicc = in.readByte() != 0;
+ mCardId = in.readString();
+ mCardStateInfo = in.readInt();
+ mLogicalSlotIdx = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mIsActive ? 1 : 0));
+ dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+ dest.writeString(mCardId);
+ dest.writeInt(mCardStateInfo);
+ dest.writeInt(mLogicalSlotIdx);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
+ @CardStateInfo int cardStateInfo, int logicalSlotIdx) {
+ this.mIsActive = isActive;
+ this.mIsEuicc = isEuicc;
+ this.mCardId = cardId;
+ this.mCardStateInfo = cardStateInfo;
+ this.mLogicalSlotIdx = logicalSlotIdx;
+ }
+
+ public boolean getIsActive() {
+ return mIsActive;
+ }
+
+ public boolean getIsEuicc() {
+ return mIsEuicc;
+ }
+
+ public String getCardId() {
+ return mCardId;
+ }
+
+ @CardStateInfo
+ public int getCardStateInfo() {
+ return mCardStateInfo;
+ }
+
+ public int getLogicalSlotIdx() {
+ return mLogicalSlotIdx;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ UiccSlotInfo that = (UiccSlotInfo) obj;
+ return (mIsActive == that.mIsActive)
+ && (mIsEuicc == that.mIsEuicc)
+ && (mCardId == that.mCardId)
+ && (mCardStateInfo == that.mCardStateInfo)
+ && (mLogicalSlotIdx == that.mLogicalSlotIdx);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + (mIsActive ? 1 : 0);
+ result = 31 * result + (mIsEuicc ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mCardId);
+ result = 31 * result + mCardStateInfo;
+ result = 31 * result + mLogicalSlotIdx;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "UiccSlotInfo (mIsActive="
+ + mIsActive
+ + ", mIsEuicc="
+ + mIsEuicc
+ + ", mCardId="
+ + mCardId
+ + ", cardState="
+ + mCardStateInfo
+ + ", phoneId="
+ + mLogicalSlotIdx
+ + ")";
+ }
+}
diff --git a/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java
new file mode 100644
index 0000000..871ee4d
--- /dev/null
+++ b/telephony/java/android/telephony/VoiceSpecificRegistrationStates.java
@@ -0,0 +1,114 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to voice network registration.
+ * @hide
+ */
+public class VoiceSpecificRegistrationStates implements Parcelable{
+ /**
+ * oncurrent services support indicator. if
+ * registered on a CDMA system.
+ * false - Concurrent services not supported,
+ * true - Concurrent services supported
+ */
+ public final boolean cssSupported;
+
+ /**
+ * TSB-58 Roaming Indicator if registered
+ * on a CDMA or EVDO system or -1 if not.
+ * Valid values are 0-255.
+ */
+ public final int roamingIndicator;
+
+ /**
+ * indicates whether the current system is in the
+ * PRL if registered on a CDMA or EVDO system or -1 if
+ * not. 0=not in the PRL, 1=in the PRL
+ */
+ public final int systemIsInPrl;
+
+ /**
+ * default Roaming Indicator from the PRL,
+ * if registered on a CDMA or EVDO system or -1 if not.
+ * Valid values are 0-255.
+ */
+ public final int defaultRoamingIndicator;
+
+ VoiceSpecificRegistrationStates(boolean cssSupported, int roamingIndicator, int systemIsInPrl,
+ int defaultRoamingIndicator) {
+ this.cssSupported = cssSupported;
+ this.roamingIndicator = roamingIndicator;
+ this.systemIsInPrl = systemIsInPrl;
+ this.defaultRoamingIndicator = defaultRoamingIndicator;
+ }
+
+ private VoiceSpecificRegistrationStates(Parcel source) {
+ this.cssSupported = source.readBoolean();
+ this.roamingIndicator = source.readInt();
+ this.systemIsInPrl = source.readInt();
+ this.defaultRoamingIndicator = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(cssSupported);
+ dest.writeInt(roamingIndicator);
+ dest.writeInt(systemIsInPrl);
+ dest.writeInt(defaultRoamingIndicator);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "VoiceSpecificRegistrationStates {"
+ + " mCssSupported=" + cssSupported
+ + " mRoamingIndicator=" + roamingIndicator
+ + " mSystemIsInPrl=" + systemIsInPrl
+ + " mDefaultRoamingIndicator=" + defaultRoamingIndicator + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cssSupported, roamingIndicator, systemIsInPrl,
+ defaultRoamingIndicator);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof VoiceSpecificRegistrationStates)) {
+ return false;
+ }
+
+ VoiceSpecificRegistrationStates other = (VoiceSpecificRegistrationStates) o;
+ return this.cssSupported == other.cssSupported
+ && this.roamingIndicator == other.roamingIndicator
+ && this.systemIsInPrl == other.systemIsInPrl
+ && this.defaultRoamingIndicator == other.defaultRoamingIndicator;
+ }
+
+
+ public static final Parcelable.Creator<VoiceSpecificRegistrationStates> CREATOR =
+ new Parcelable.Creator<VoiceSpecificRegistrationStates>() {
+ @Override
+ public VoiceSpecificRegistrationStates createFromParcel(Parcel source) {
+ return new VoiceSpecificRegistrationStates(source);
+ }
+
+ @Override
+ public VoiceSpecificRegistrationStates[] newArray(int size) {
+ return new VoiceSpecificRegistrationStates[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/data/ApnSetting.aidl
similarity index 86%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/data/ApnSetting.aidl
index d750363..381e5e8 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/data/ApnSetting.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-/** @hide */
package android.telephony.data;
-parcelable InterfaceAddress;
+parcelable ApnSetting;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
new file mode 100644
index 0000000..73a05af
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -0,0 +1,1351 @@
+/*
+ * 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.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.hardware.radio.V1_0.ApnTypes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class representing an APN configuration.
+ */
+public class ApnSetting implements Parcelable {
+
+ static final String LOG_TAG = "ApnSetting";
+ private static final boolean VDBG = false;
+
+ private final String mEntryName;
+ private final String mApnName;
+ private final InetAddress mProxy;
+ private final int mPort;
+ private final URL mMmsc;
+ private final InetAddress mMmsProxy;
+ private final int mMmsPort;
+ private final String mUser;
+ private final String mPassword;
+ private final int mAuthType;
+ private final List<String> mTypes;
+ private final int mTypesBitmap;
+ private final int mId;
+ private final String mOperatorNumeric;
+ private final String mProtocol;
+ private final String mRoamingProtocol;
+ private final int mMtu;
+
+ private final boolean mCarrierEnabled;
+
+ private final int mNetworkTypeBitmask;
+
+ private final int mProfileId;
+
+ private final boolean mModemCognitive;
+ private final int mMaxConns;
+ private final int mWaitTime;
+ private final int mMaxConnsTime;
+
+ private final String mMvnoType;
+ private final String mMvnoMatchData;
+
+ private boolean mPermanentFailed = false;
+
+ /**
+ * Returns the types bitmap of the APN.
+ *
+ * @return types bitmap of the APN
+ * @hide
+ */
+ public int getTypesBitmap() {
+ return mTypesBitmap;
+ }
+
+ /**
+ * Returns the MTU size of the mobile interface to which the APN connected.
+ *
+ * @return the MTU size of the APN
+ * @hide
+ */
+ public int getMtu() {
+ return mMtu;
+ }
+
+ /**
+ * Returns the profile id to which the APN saved in modem.
+ *
+ * @return the profile id of the APN
+ * @hide
+ */
+ public int getProfileId() {
+ return mProfileId;
+ }
+
+ /**
+ * Returns if the APN setting is to be set in modem.
+ *
+ * @return is the APN setting to be set in modem
+ * @hide
+ */
+ public boolean getModemCognitive() {
+ return mModemCognitive;
+ }
+
+ /**
+ * Returns the max connections of this APN.
+ *
+ * @return the max connections of this APN
+ * @hide
+ */
+ public int getMaxConns() {
+ return mMaxConns;
+ }
+
+ /**
+ * Returns the wait time for retry of the APN.
+ *
+ * @return the wait time for retry of the APN
+ * @hide
+ */
+ public int getWaitTime() {
+ return mWaitTime;
+ }
+
+ /**
+ * Returns the time to limit max connection for the APN.
+ *
+ * @return the time to limit max connection for the APN
+ * @hide
+ */
+ public int getMaxConnsTime() {
+ return mMaxConnsTime;
+ }
+
+ /**
+ * Returns the MVNO data. Examples:
+ * "spn": A MOBILE, BEN NL
+ * "imsi": 302720x94, 2060188
+ * "gid": 4E, 33
+ * "iccid": 898603 etc..
+ *
+ * @return the mvno match data
+ * @hide
+ */
+ public String getMvnoMatchData() {
+ return mMvnoMatchData;
+ }
+
+ /**
+ * Indicates this APN setting is permanently failed and cannot be
+ * retried by the retry manager anymore.
+ *
+ * @return if this APN setting is permanently failed
+ * @hide
+ */
+ public boolean getPermanentFailed() {
+ return mPermanentFailed;
+ }
+
+ /**
+ * Sets if this APN setting is permanently failed.
+ *
+ * @param permanentFailed if this APN setting is permanently failed
+ * @hide
+ */
+ public void setPermanentFailed(boolean permanentFailed) {
+ mPermanentFailed = permanentFailed;
+ }
+
+ /**
+ * Returns the entry name of the APN.
+ *
+ * @return the entry name for the APN
+ */
+ public String getEntryName() {
+ return mEntryName;
+ }
+
+ /**
+ * Returns the name of the APN.
+ *
+ * @return APN name
+ */
+ public String getApnName() {
+ return mApnName;
+ }
+
+ /**
+ * Returns the proxy address of the APN.
+ *
+ * @return proxy address.
+ */
+ public InetAddress getProxy() {
+ return mProxy;
+ }
+
+ /**
+ * Returns the proxy port of the APN.
+ *
+ * @return proxy port
+ */
+ public int getPort() {
+ return mPort;
+ }
+ /**
+ * Returns the MMSC URL of the APN.
+ *
+ * @return MMSC URL.
+ */
+ public URL getMmsc() {
+ return mMmsc;
+ }
+
+ /**
+ * Returns the MMS proxy address of the APN.
+ *
+ * @return MMS proxy address.
+ */
+ public InetAddress getMmsProxy() {
+ return mMmsProxy;
+ }
+
+ /**
+ * Returns the MMS proxy port of the APN.
+ *
+ * @return MMS proxy port
+ */
+ public int getMmsPort() {
+ return mMmsPort;
+ }
+
+ /**
+ * Returns the APN username of the APN.
+ *
+ * @return APN username
+ */
+ public String getUser() {
+ return mUser;
+ }
+
+ /**
+ * Returns the APN password of the APN.
+ *
+ * @return APN password
+ */
+ public String getPassword() {
+ return mPassword;
+ }
+
+ /** @hide */
+ @IntDef({
+ AUTH_TYPE_NONE,
+ AUTH_TYPE_PAP,
+ AUTH_TYPE_CHAP,
+ AUTH_TYPE_PAP_OR_CHAP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AuthType {}
+
+ /**
+ * Returns the authentication type of the APN.
+ *
+ * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
+ *
+ * @return authentication type
+ */
+ @AuthType
+ public int getAuthType() {
+ return mAuthType;
+ }
+
+ /** @hide */
+ @StringDef({
+ TYPE_DEFAULT,
+ TYPE_MMS,
+ TYPE_SUPL,
+ TYPE_DUN,
+ TYPE_HIPRI,
+ TYPE_FOTA,
+ TYPE_IMS,
+ TYPE_CBS,
+ TYPE_IA,
+ TYPE_EMERGENCY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApnType {}
+
+ /**
+ * Returns the list of APN types of the APN.
+ *
+ * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+ *
+ * @return the list of APN types
+ */
+ @ApnType
+ public List<String> getTypes() {
+ return mTypes;
+ }
+
+ /**
+ * Returns the unique database id for this entry.
+ *
+ * @return the unique database id
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the numeric operator ID for the APN. Usually
+ * {@link android.provider.Telephony.Carriers#MCC} +
+ * {@link android.provider.Telephony.Carriers#MNC}.
+ *
+ * @return the numeric operator ID
+ */
+ public String getOperatorNumeric() {
+ return mOperatorNumeric;
+ }
+
+ /** @hide */
+ @StringDef({
+ PROTOCOL_IP,
+ PROTOCOL_IPV6,
+ PROTOCOL_IPV4V6,
+ PROTOCOL_PPP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtocolType {}
+
+ /**
+ * Returns the protocol to use to connect to this APN.
+ *
+ * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+ * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+ *
+ * @return the protocol
+ */
+ @ProtocolType
+ public String getProtocol() {
+ return mProtocol;
+ }
+
+ /**
+ * Returns the protocol to use to connect to this APN when roaming.
+ *
+ * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+ *
+ * @return the roaming protocol
+ */
+ public String getRoamingProtocol() {
+ return mRoamingProtocol;
+ }
+
+ /**
+ * Returns the current status of APN.
+ *
+ * {@code true} : enabled APN.
+ * {@code false} : disabled APN.
+ *
+ * @return the current status
+ */
+ public boolean isEnabled() {
+ return mCarrierEnabled;
+ }
+
+ /**
+ * Returns a bitmask describing the Radio Technologies(Network Types) which this APN may use.
+ *
+ * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+ *
+ * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+ * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+ *
+ * @return a bitmask describing the Radio Technologies(Network Types)
+ */
+ public int getNetworkTypeBitmask() {
+ return mNetworkTypeBitmask;
+ }
+
+ /** @hide */
+ @StringDef({
+ MVNO_TYPE_SPN,
+ MVNO_TYPE_IMSI,
+ MVNO_TYPE_GID,
+ MVNO_TYPE_ICCID,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MvnoType {}
+
+ /**
+ * Returns the MVNO match type for this APN.
+ *
+ * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
+ *
+ * @return the MVNO match type
+ */
+ @MvnoType
+ public String getMvnoType() {
+ return mMvnoType;
+ }
+
+ private ApnSetting(Builder builder) {
+ this.mEntryName = builder.mEntryName;
+ this.mApnName = builder.mApnName;
+ this.mProxy = builder.mProxy;
+ this.mPort = builder.mPort;
+ this.mMmsc = builder.mMmsc;
+ this.mMmsProxy = builder.mMmsProxy;
+ this.mMmsPort = builder.mMmsPort;
+ this.mUser = builder.mUser;
+ this.mPassword = builder.mPassword;
+ this.mAuthType = builder.mAuthType;
+ this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
+ this.mTypesBitmap = builder.mTypesBitmap;
+ this.mId = builder.mId;
+ this.mOperatorNumeric = builder.mOperatorNumeric;
+ this.mProtocol = builder.mProtocol;
+ this.mRoamingProtocol = builder.mRoamingProtocol;
+ this.mMtu = builder.mMtu;
+ this.mCarrierEnabled = builder.mCarrierEnabled;
+ this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
+ this.mProfileId = builder.mProfileId;
+ this.mModemCognitive = builder.mModemCognitive;
+ this.mMaxConns = builder.mMaxConns;
+ this.mWaitTime = builder.mWaitTime;
+ this.mMaxConnsTime = builder.mMaxConnsTime;
+ this.mMvnoType = builder.mMvnoType;
+ this.mMvnoMatchData = builder.mMvnoMatchData;
+ }
+
+ /** @hide */
+ public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
+ String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
+ int mmsPort, String user, String password, int authType, List<String> types,
+ String protocol, String roamingProtocol, boolean carrierEnabled,
+ int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
+ int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+ return new Builder()
+ .setId(id)
+ .setOperatorNumeric(operatorNumeric)
+ .setEntryName(entryName)
+ .setApnName(apnName)
+ .setProxy(proxy)
+ .setPort(port)
+ .setMmsc(mmsc)
+ .setMmsProxy(mmsProxy)
+ .setMmsPort(mmsPort)
+ .setUser(user)
+ .setPassword(password)
+ .setAuthType(authType)
+ .setTypes(types)
+ .setProtocol(protocol)
+ .setRoamingProtocol(roamingProtocol)
+ .setCarrierEnabled(carrierEnabled)
+ .setNetworkTypeBitmask(networkTypeBitmask)
+ .setProfileId(profileId)
+ .setModemCognitive(modemCognitive)
+ .setMaxConns(maxConns)
+ .setWaitTime(waitTime)
+ .setMaxConnsTime(maxConnsTime)
+ .setMtu(mtu)
+ .setMvnoType(mvnoType)
+ .setMvnoMatchData(mvnoMatchData)
+ .build();
+ }
+
+ /** @hide */
+ public static ApnSetting makeApnSetting(Cursor cursor) {
+ String[] types = parseTypes(
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+ int networkTypeBitmask = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
+ if (networkTypeBitmask == 0) {
+ final int bearerBitmask = cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.BEARER_BITMASK));
+ networkTypeBitmask =
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
+ }
+
+ return makeApnSetting(
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
+ inetAddressFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
+ portFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
+ URLFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
+ inetAddressFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
+ portFromString(cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT))),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
+ Arrays.asList(types),
+ cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.ROAMING_PROTOCOL)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.CARRIER_ENABLED)) == 1,
+ networkTypeBitmask,
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MODEM_COGNITIVE)) == 1,
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MAX_CONNS_TIME)),
+ cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MVNO_TYPE)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Telephony.Carriers.MVNO_MATCH_DATA)));
+ }
+
+ /** @hide */
+ public static ApnSetting makeApnSetting(ApnSetting apn) {
+ return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
+ apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
+ apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
+ apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
+ apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
+ apn.mMvnoType, apn.mMvnoMatchData);
+ }
+
+ /** @hide */
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ApnSettingV4] ")
+ .append(mEntryName)
+ .append(", ").append(mId)
+ .append(", ").append(mOperatorNumeric)
+ .append(", ").append(mApnName)
+ .append(", ").append(inetAddressToString(mProxy))
+ .append(", ").append(URLToString(mMmsc))
+ .append(", ").append(inetAddressToString(mMmsProxy))
+ .append(", ").append(portToString(mMmsPort))
+ .append(", ").append(portToString(mPort))
+ .append(", ").append(mAuthType).append(", ");
+ for (int i = 0; i < mTypes.size(); i++) {
+ sb.append(mTypes.get(i));
+ if (i < mTypes.size() - 1) {
+ sb.append(" | ");
+ }
+ }
+ sb.append(", ").append(mProtocol);
+ sb.append(", ").append(mRoamingProtocol);
+ sb.append(", ").append(mCarrierEnabled);
+ sb.append(", ").append(mProfileId);
+ sb.append(", ").append(mModemCognitive);
+ sb.append(", ").append(mMaxConns);
+ sb.append(", ").append(mWaitTime);
+ sb.append(", ").append(mMaxConnsTime);
+ sb.append(", ").append(mMtu);
+ sb.append(", ").append(mMvnoType);
+ sb.append(", ").append(mMvnoMatchData);
+ sb.append(", ").append(mPermanentFailed);
+ sb.append(", ").append(mNetworkTypeBitmask);
+ return sb.toString();
+ }
+
+ /**
+ * Returns true if there are MVNO params specified.
+ * @hide
+ */
+ public boolean hasMvnoParams() {
+ return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+ }
+
+ /** @hide */
+ public boolean canHandleType(String type) {
+ if (!mCarrierEnabled) return false;
+ boolean wildcardable = true;
+ if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
+ for (String t : mTypes) {
+ // DEFAULT handles all, and HIPRI is handled by DEFAULT
+ if (t.equalsIgnoreCase(type)
+ || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
+ || (t.equalsIgnoreCase(TYPE_DEFAULT)
+ && type.equalsIgnoreCase(TYPE_HIPRI))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // check whether the types of two APN same (even only one type of each APN is same)
+ private boolean typeSameAny(ApnSetting first, ApnSetting second) {
+ if (VDBG) {
+ StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
+ for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
+ apnType1.append(first.mTypes.get(index1));
+ apnType1.append(",");
+ }
+
+ StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
+ for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
+ apnType2.append(second.mTypes.get(index1));
+ apnType2.append(",");
+ }
+ Rlog.d(LOG_TAG, "APN1: is " + apnType1);
+ Rlog.d(LOG_TAG, "APN2: is " + apnType2);
+ }
+
+ for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
+ for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
+ if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
+ || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
+ || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
+ if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
+ return true;
+ }
+ }
+ }
+
+ if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+ return false;
+ }
+
+ // TODO - if we have this function we should also have hashCode.
+ // Also should handle changes in type order and perhaps case-insensitivity
+ /** @hide */
+ public boolean equals(Object o) {
+ if (o instanceof ApnSetting == false) {
+ return false;
+ }
+
+ ApnSetting other = (ApnSetting) o;
+
+ return mEntryName.equals(other.mEntryName)
+ && Objects.equals(mId, other.mId)
+ && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+ && Objects.equals(mApnName, other.mApnName)
+ && Objects.equals(mProxy, other.mProxy)
+ && Objects.equals(mMmsc, other.mMmsc)
+ && Objects.equals(mMmsProxy, other.mMmsProxy)
+ && Objects.equals(mMmsPort, other.mMmsPort)
+ && Objects.equals(mPort,other.mPort)
+ && Objects.equals(mUser, other.mUser)
+ && Objects.equals(mPassword, other.mPassword)
+ && Objects.equals(mAuthType, other.mAuthType)
+ && Objects.equals(mTypes, other.mTypes)
+ && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+ && Objects.equals(mProtocol, other.mProtocol)
+ && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
+ && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
+ && Objects.equals(mProfileId, other.mProfileId)
+ && Objects.equals(mModemCognitive, other.mModemCognitive)
+ && Objects.equals(mMaxConns, other.mMaxConns)
+ && Objects.equals(mWaitTime, other.mWaitTime)
+ && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
+ && Objects.equals(mMtu, other.mMtu)
+ && Objects.equals(mMvnoType, other.mMvnoType)
+ && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+ && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask);
+ }
+
+ /**
+ * Compare two APN settings
+ *
+ * Note: This method does not compare 'mId', 'mNetworkTypeBitmask'. We only use this for
+ * determining if tearing a data call is needed when conditions change. See
+ * cleanUpConnectionsOnUpdatedApns in DcTracker.
+ *
+ * @param o the other object to compare
+ * @param isDataRoaming True if the device is on data roaming
+ * @return True if the two APN settings are same
+ * @hide
+ */
+ public boolean equals(Object o, boolean isDataRoaming) {
+ if (!(o instanceof ApnSetting)) {
+ return false;
+ }
+
+ ApnSetting other = (ApnSetting) o;
+
+ return mEntryName.equals(other.mEntryName)
+ && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+ && Objects.equals(mApnName, other.mApnName)
+ && Objects.equals(mProxy, other.mProxy)
+ && Objects.equals(mMmsc, other.mMmsc)
+ && Objects.equals(mMmsProxy, other.mMmsProxy)
+ && Objects.equals(mMmsPort, other.mMmsPort)
+ && Objects.equals(mPort, other.mPort)
+ && Objects.equals(mUser, other.mUser)
+ && Objects.equals(mPassword, other.mPassword)
+ && Objects.equals(mAuthType, other.mAuthType)
+ && Objects.equals(mTypes, other.mTypes)
+ && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+ && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
+ && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
+ && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
+ && Objects.equals(mProfileId, other.mProfileId)
+ && Objects.equals(mModemCognitive, other.mModemCognitive)
+ && Objects.equals(mMaxConns, other.mMaxConns)
+ && Objects.equals(mWaitTime, other.mWaitTime)
+ && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
+ && Objects.equals(mMtu, other.mMtu)
+ && Objects.equals(mMvnoType, other.mMvnoType)
+ && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+ }
+
+ /**
+ * Check if neither mention DUN and are substantially similar
+ *
+ * @param other The other APN settings to compare
+ * @return True if two APN settings are similar
+ * @hide
+ */
+ public boolean similar(ApnSetting other) {
+ return (!this.canHandleType(TYPE_DUN)
+ && !other.canHandleType(TYPE_DUN)
+ && Objects.equals(this.mApnName, other.mApnName)
+ && !typeSameAny(this, other)
+ && xorEqualsInetAddress(this.mProxy, other.mProxy)
+ && xorEqualsPort(this.mPort, other.mPort)
+ && xorEquals(this.mProtocol, other.mProtocol)
+ && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
+ && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
+ && Objects.equals(this.mProfileId, other.mProfileId)
+ && Objects.equals(this.mMvnoType, other.mMvnoType)
+ && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
+ && xorEqualsURL(this.mMmsc, other.mMmsc)
+ && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
+ && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+ && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEquals(String first, String second) {
+ return (Objects.equals(first, second)
+ || TextUtils.isEmpty(first)
+ || TextUtils.isEmpty(second));
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
+ return first == null || second == null || first.equals(second);
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEqualsURL(URL first, URL second) {
+ return first == null || second == null || first.equals(second);
+ }
+
+ // Equal or one is not specified.
+ private boolean xorEqualsPort(int first, int second) {
+ return first == -1 || second == -1 || Objects.equals(first, second);
+ }
+
+ // Helper function to convert APN string into a 32-bit bitmask.
+ private static int getApnBitmask(String apn) {
+ switch (apn) {
+ case TYPE_DEFAULT: return ApnTypes.DEFAULT;
+ case TYPE_MMS: return ApnTypes.MMS;
+ case TYPE_SUPL: return ApnTypes.SUPL;
+ case TYPE_DUN: return ApnTypes.DUN;
+ case TYPE_HIPRI: return ApnTypes.HIPRI;
+ case TYPE_FOTA: return ApnTypes.FOTA;
+ case TYPE_IMS: return ApnTypes.IMS;
+ case TYPE_CBS: return ApnTypes.CBS;
+ case TYPE_IA: return ApnTypes.IA;
+ case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
+ case TYPE_ALL: return ApnTypes.ALL;
+ default: return ApnTypes.NONE;
+ }
+ }
+
+ private String deParseTypes(List<String> types) {
+ if (types == null) {
+ return null;
+ }
+ return TextUtils.join(",", types);
+ }
+
+ private String nullToEmpty(String stringValue) {
+ return stringValue == null ? "" : stringValue;
+ }
+
+ /** @hide */
+ // Called by DPM.
+ public ContentValues toContentValues() {
+ ContentValues apnValue = new ContentValues();
+ apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
+ apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
+ apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
+ apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
+ apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
+ apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
+ apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
+ apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
+ ? "" : inetAddressToString(mMmsProxy));
+ apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
+ apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
+ apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
+ String apnType = deParseTypes(mTypes);
+ apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
+ apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
+ apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
+ apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
+ apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+ apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
+
+ return apnValue;
+ }
+
+ /**
+ * @param types comma delimited list of APN types
+ * @return array of APN types
+ * @hide
+ */
+ public static String[] parseTypes(String types) {
+ String[] result;
+ // If unset, set to DEFAULT.
+ if (TextUtils.isEmpty(types)) {
+ result = new String[1];
+ result[0] = TYPE_ALL;
+ } else {
+ result = types.split(",");
+ }
+ return result;
+ }
+
+ private static URL URLFromString(String url) {
+ try {
+ return TextUtils.isEmpty(url) ? null : new URL(url);
+ } catch (MalformedURLException e) {
+ Log.e(LOG_TAG, "Can't parse URL from string.");
+ return null;
+ }
+ }
+
+ private static String URLToString(URL url) {
+ return url == null ? "" : url.toString();
+ }
+
+ private static InetAddress inetAddressFromString(String inetAddress) {
+ if (TextUtils.isEmpty(inetAddress)) {
+ return null;
+ }
+ try {
+ return InetAddress.getByName(inetAddress);
+ } catch (UnknownHostException e) {
+ Log.e(LOG_TAG, "Can't parse InetAddress from string: unknown host.");
+ return null;
+ }
+ }
+
+ private static String inetAddressToString(InetAddress inetAddress) {
+ if (inetAddress == null) {
+ return null;
+ }
+ final String inetAddressString = inetAddress.toString();
+ if (TextUtils.isEmpty(inetAddressString)) {
+ return null;
+ }
+ final String hostName = inetAddressString.substring(0, inetAddressString.indexOf("/"));
+ final String address = inetAddressString.substring(inetAddressString.indexOf("/") + 1);
+ if (TextUtils.isEmpty(hostName) && TextUtils.isEmpty(address)) {
+ return null;
+ }
+ return TextUtils.isEmpty(hostName) ? address : hostName;
+ }
+
+ private static int portFromString(String strPort) {
+ int port = -1;
+ if (!TextUtils.isEmpty(strPort)) {
+ try {
+ port = Integer.parseInt(strPort);
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Can't parse port from String");
+ }
+ }
+ return port;
+ }
+
+ private static String portToString(int port) {
+ return port == -1 ? "" : Integer.toString(port);
+ }
+
+ // Implement Parcelable.
+ @Override
+ /** @hide */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ /** @hide */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mOperatorNumeric);
+ dest.writeString(mEntryName);
+ dest.writeString(mApnName);
+ dest.writeValue(mProxy);
+ dest.writeInt(mPort);
+ dest.writeValue(mMmsc);
+ dest.writeValue(mMmsProxy);
+ dest.writeInt(mMmsPort);
+ dest.writeString(mUser);
+ dest.writeString(mPassword);
+ dest.writeInt(mAuthType);
+ dest.writeStringArray(mTypes.toArray(new String[0]));
+ dest.writeString(mProtocol);
+ dest.writeString(mRoamingProtocol);
+ dest.writeInt(mCarrierEnabled ? 1: 0);
+ dest.writeString(mMvnoType);
+ dest.writeInt(mNetworkTypeBitmask);
+ }
+
+ private static ApnSetting readFromParcel(Parcel in) {
+ final int id = in.readInt();
+ final String operatorNumeric = in.readString();
+ final String entryName = in.readString();
+ final String apnName = in.readString();
+ final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+ final int port = in.readInt();
+ final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+ final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+ final int mmsPort = in.readInt();
+ final String user = in.readString();
+ final String password = in.readString();
+ final int authType = in.readInt();
+ final List<String> types = Arrays.asList(in.readStringArray());
+ final String protocol = in.readString();
+ final String roamingProtocol = in.readString();
+ final boolean carrierEnabled = in.readInt() > 0;
+ final String mvnoType = in.readString();
+ final int networkTypeBitmask = in.readInt();
+
+ return makeApnSetting(id, operatorNumeric, entryName, apnName,
+ proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
+ roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+ 0, 0, 0, 0, mvnoType, null);
+ }
+
+ public static final Parcelable.Creator<ApnSetting> CREATOR =
+ new Parcelable.Creator<ApnSetting>() {
+ @Override
+ public ApnSetting createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public ApnSetting[] newArray(int size) {
+ return new ApnSetting[size];
+ }
+ };
+
+ /**
+ * APN types for data connections. These are usage categories for an APN
+ * entry. One APN entry may support multiple APN types, eg, a single APN
+ * may service regular internet traffic ("default") as well as MMS-specific
+ * connections.<br/>
+ * ALL is a special type to indicate that this APN entry can
+ * service all data connections.
+ */
+ public static final String TYPE_ALL = "*";
+ /** APN type for default data traffic */
+ public static final String TYPE_DEFAULT = "default";
+ /** APN type for MMS traffic */
+ public static final String TYPE_MMS = "mms";
+ /** APN type for SUPL assisted GPS */
+ public static final String TYPE_SUPL = "supl";
+ /** APN type for DUN traffic */
+ public static final String TYPE_DUN = "dun";
+ /** APN type for HiPri traffic */
+ public static final String TYPE_HIPRI = "hipri";
+ /** APN type for FOTA */
+ public static final String TYPE_FOTA = "fota";
+ /** APN type for IMS */
+ public static final String TYPE_IMS = "ims";
+ /** APN type for CBS */
+ public static final String TYPE_CBS = "cbs";
+ /** APN type for IA Initial Attach APN */
+ public static final String TYPE_IA = "ia";
+ /** APN type for Emergency PDN. This is not an IA apn, but is used
+ * for access to carrier services in an emergency call situation. */
+ public static final String TYPE_EMERGENCY = "emergency";
+ /**
+ * Array of all APN types
+ *
+ * @hide
+ */
+ public static final String[] ALL_TYPES = {
+ TYPE_DEFAULT,
+ TYPE_MMS,
+ TYPE_SUPL,
+ TYPE_DUN,
+ TYPE_HIPRI,
+ TYPE_FOTA,
+ TYPE_IMS,
+ TYPE_CBS,
+ TYPE_IA,
+ TYPE_EMERGENCY
+ };
+
+ // Possible values for authentication types.
+ public static final int AUTH_TYPE_NONE = 0;
+ public static final int AUTH_TYPE_PAP = 1;
+ public static final int AUTH_TYPE_CHAP = 2;
+ public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+ // Possible values for protocol.
+ public static final String PROTOCOL_IP = "IP";
+ public static final String PROTOCOL_IPV6 = "IPV6";
+ public static final String PROTOCOL_IPV4V6 = "IPV4V6";
+ public static final String PROTOCOL_PPP = "PPP";
+
+ // Possible values for MVNO type.
+ public static final String MVNO_TYPE_SPN = "spn";
+ public static final String MVNO_TYPE_IMSI = "imsi";
+ public static final String MVNO_TYPE_GID = "gid";
+ public static final String MVNO_TYPE_ICCID = "iccid";
+
+ public static class Builder{
+ private String mEntryName;
+ private String mApnName;
+ private InetAddress mProxy;
+ private int mPort = -1;
+ private URL mMmsc;
+ private InetAddress mMmsProxy;
+ private int mMmsPort = -1;
+ private String mUser;
+ private String mPassword;
+ private int mAuthType;
+ private List<String> mTypes;
+ private int mTypesBitmap;
+ private int mId;
+ private String mOperatorNumeric;
+ private String mProtocol;
+ private String mRoamingProtocol;
+ private int mMtu;
+ private int mNetworkTypeBitmask;
+ private boolean mCarrierEnabled;
+ private int mProfileId;
+ private boolean mModemCognitive;
+ private int mMaxConns;
+ private int mWaitTime;
+ private int mMaxConnsTime;
+ private String mMvnoType;
+ private String mMvnoMatchData;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {}
+
+ /**
+ * Sets the unique database id for this entry.
+ *
+ * @param id the unique database id to set for this entry
+ */
+ private Builder setId(int id) {
+ this.mId = id;
+ return this;
+ }
+
+ /**
+ * Set the MTU size of the mobile interface to which the APN connected.
+ *
+ * @param mtu the MTU size to set for the APN
+ * @hide
+ */
+ public Builder setMtu(int mtu) {
+ this.mMtu = mtu;
+ return this;
+ }
+
+ /**
+ * Sets the profile id to which the APN saved in modem.
+ *
+ * @param profileId the profile id to set for the APN
+ * @hide
+ */
+ public Builder setProfileId(int profileId) {
+ this.mProfileId = profileId;
+ return this;
+ }
+
+ /**
+ * Sets if the APN setting is to be set in modem.
+ *
+ * @param modemCognitive if the APN setting is to be set in modem
+ * @hide
+ */
+ public Builder setModemCognitive(boolean modemCognitive) {
+ this.mModemCognitive = modemCognitive;
+ return this;
+ }
+
+ /**
+ * Sets the max connections of this APN.
+ *
+ * @param maxConns the max connections of this APN
+ * @hide
+ */
+ public Builder setMaxConns(int maxConns) {
+ this.mMaxConns = maxConns;
+ return this;
+ }
+
+ /**
+ * Sets the wait time for retry of the APN.
+ *
+ * @param waitTime the wait time for retry of the APN
+ * @hide
+ */
+ public Builder setWaitTime(int waitTime) {
+ this.mWaitTime = waitTime;
+ return this;
+ }
+
+ /**
+ * Sets the time to limit max connection for the APN.
+ *
+ * @param maxConnsTime the time to limit max connection for the APN
+ * @hide
+ */
+ public Builder setMaxConnsTime(int maxConnsTime) {
+ this.mMaxConnsTime = maxConnsTime;
+ return this;
+ }
+
+ /**
+ * Sets the MVNO match data for the APN.
+ *
+ * @param mvnoMatchData the MVNO match data for the APN
+ * @hide
+ */
+ public Builder setMvnoMatchData(String mvnoMatchData) {
+ this.mMvnoMatchData = mvnoMatchData;
+ return this;
+ }
+
+ /**
+ * Sets the entry name of the APN.
+ *
+ * @param entryName the entry name to set for the APN
+ */
+ public Builder setEntryName(String entryName) {
+ this.mEntryName = entryName;
+ return this;
+ }
+
+ /**
+ * Sets the name of the APN.
+ *
+ * @param apnName the name to set for the APN
+ */
+ public Builder setApnName(String apnName) {
+ this.mApnName = apnName;
+ return this;
+ }
+
+ /**
+ * Sets the proxy address of the APN.
+ *
+ * @param proxy the proxy address to set for the APN
+ */
+ public Builder setProxy(InetAddress proxy) {
+ this.mProxy = proxy;
+ return this;
+ }
+
+ /**
+ * Sets the proxy port of the APN.
+ *
+ * @param port the proxy port to set for the APN
+ */
+ public Builder setPort(int port) {
+ this.mPort = port;
+ return this;
+ }
+
+ /**
+ * Sets the MMSC URL of the APN.
+ *
+ * @param mmsc the MMSC URL to set for the APN
+ */
+ public Builder setMmsc(URL mmsc) {
+ this.mMmsc = mmsc;
+ return this;
+ }
+
+ /**
+ * Sets the MMS proxy address of the APN.
+ *
+ * @param mmsProxy the MMS proxy address to set for the APN
+ */
+ public Builder setMmsProxy(InetAddress mmsProxy) {
+ this.mMmsProxy = mmsProxy;
+ return this;
+ }
+
+ /**
+ * Sets the MMS proxy port of the APN.
+ *
+ * @param mmsPort the MMS proxy port to set for the APN
+ */
+ public Builder setMmsPort(int mmsPort) {
+ this.mMmsPort = mmsPort;
+ return this;
+ }
+
+ /**
+ * Sets the APN username of the APN.
+ *
+ * @param user the APN username to set for the APN
+ */
+ public Builder setUser(String user) {
+ this.mUser = user;
+ return this;
+ }
+
+ /**
+ * Sets the APN password of the APN.
+ *
+ * @see android.provider.Telephony.Carriers#PASSWORD
+ * @param password the APN password to set for the APN
+ */
+ public Builder setPassword(String password) {
+ this.mPassword = password;
+ return this;
+ }
+
+ /**
+ * Sets the authentication type of the APN.
+ *
+ * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
+ *
+ * @param authType the authentication type to set for the APN
+ */
+ public Builder setAuthType(@AuthType int authType) {
+ this.mAuthType = authType;
+ return this;
+ }
+
+ /**
+ * Sets the list of APN types of the APN.
+ *
+ * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+ *
+ * @param types the list of APN types to set for the APN
+ */
+ public Builder setTypes(@ApnType List<String> types) {
+ this.mTypes = types;
+ int apnBitmap = 0;
+ for (int i = 0; i < mTypes.size(); i++) {
+ mTypes.set(i, mTypes.get(i).toLowerCase());
+ apnBitmap |= getApnBitmask(mTypes.get(i));
+ }
+ this.mTypesBitmap = apnBitmap;
+ return this;
+ }
+
+ /**
+ * Set the numeric operator ID for the APN.
+ *
+ * @param operatorNumeric the numeric operator ID to set for this entry
+ */
+ public Builder setOperatorNumeric(String operatorNumeric) {
+ this.mOperatorNumeric = operatorNumeric;
+ return this;
+ }
+
+ /**
+ * Sets the protocol to use to connect to this APN.
+ *
+ * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+ * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+ *
+ * @param protocol the protocol to set to use to connect to this APN
+ */
+ public Builder setProtocol(@ProtocolType String protocol) {
+ this.mProtocol = protocol;
+ return this;
+ }
+
+ /**
+ * Sets the protocol to use to connect to this APN when roaming.
+ *
+ * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
+ */
+ public Builder setRoamingProtocol(String roamingProtocol) {
+ this.mRoamingProtocol = roamingProtocol;
+ return this;
+ }
+
+ /**
+ * Sets the current status for this APN.
+ *
+ * @param carrierEnabled the current status to set for this APN
+ */
+ public Builder setCarrierEnabled(boolean carrierEnabled) {
+ this.mCarrierEnabled = carrierEnabled;
+ return this;
+ }
+
+ /**
+ * Sets Radio Technology (Network Type) info for this APN.
+ *
+ * @param networkTypeBitmask the Radio Technology (Network Type) info
+ */
+ public Builder setNetworkTypeBitmask(int networkTypeBitmask) {
+ this.mNetworkTypeBitmask = networkTypeBitmask;
+ return this;
+ }
+
+ /**
+ * Sets the MVNO match type for this APN.
+ *
+ * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
+ *
+ * @param mvnoType the MVNO match type to set for this APN
+ */
+ public Builder setMvnoType(@MvnoType String mvnoType) {
+ this.mMvnoType = mvnoType;
+ return this;
+ }
+
+ public ApnSetting build() {
+ return new ApnSetting(this);
+ }
+ }
+}
+
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index da51c86..25f5133 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -20,12 +20,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.LinkAddress;
import android.os.Parcel;
import android.os.Parcelable;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Description of the response of a setup data call connection request.
@@ -40,7 +42,7 @@
private final int mActive;
private final String mType;
private final String mIfname;
- private final List<InterfaceAddress> mAddresses;
+ private final List<LinkAddress> mAddresses;
private final List<InetAddress> mDnses;
private final List<InetAddress> mGateways;
private final List<String> mPcscfs;
@@ -71,7 +73,7 @@
*/
public DataCallResponse(int status, int suggestedRetryTime, int cid, int active,
@Nullable String type, @Nullable String ifname,
- @Nullable List<InterfaceAddress> addresses,
+ @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnses,
@Nullable List<InetAddress> gateways,
@Nullable List<String> pcscfs, int mtu) {
@@ -96,7 +98,7 @@
mType = source.readString();
mIfname = source.readString();
mAddresses = new ArrayList<>();
- source.readList(mAddresses, InterfaceAddress.class.getClassLoader());
+ source.readList(mAddresses, LinkAddress.class.getClassLoader());
mDnses = new ArrayList<>();
source.readList(mDnses, InetAddress.class.getClassLoader());
mGateways = new ArrayList<>();
@@ -140,10 +142,10 @@
public String getIfname() { return mIfname; }
/**
- * @return A list of {@link InterfaceAddress}
+ * @return A list of {@link LinkAddress}
*/
@NonNull
- public List<InterfaceAddress> getAddresses() { return mAddresses; }
+ public List<LinkAddress> getAddresses() { return mAddresses; }
/**
* @return A list of DNS server addresses, e.g., "192.0.1.3" or
@@ -219,17 +221,8 @@
@Override
public int hashCode() {
- return mStatus * 31
- + mSuggestedRetryTime * 37
- + mCid * 41
- + mActive * 43
- + mType.hashCode() * 47
- + mIfname.hashCode() * 53
- + mAddresses.hashCode() * 59
- + mDnses.hashCode() * 61
- + mGateways.hashCode() * 67
- + mPcscfs.hashCode() * 71
- + mMtu * 73;
+ return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mType, mIfname, mAddresses,
+ mDnses, mGateways, mPcscfs, mMtu);
}
@Override
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
new file mode 100644
index 0000000..e8c1cb1
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Rlog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of data service. Services that extend DataService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow
+ * the following format:
+ * ...
+ * <service android:name=".xxxDataService"
+ * android:permission="android.permission.BIND_DATA_SERVICE" >
+ * <intent-filter>
+ * <action android:name="android.telephony.data.DataService" />
+ * </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class DataService extends Service {
+ private static final String TAG = DataService.class.getSimpleName();
+
+ public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+ public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
+
+ /** {@hide} */
+ @IntDef(prefix = "REQUEST_REASON_", value = {
+ REQUEST_REASON_NORMAL,
+ REQUEST_REASON_HANDOVER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SetupDataReason {}
+
+ /** {@hide} */
+ @IntDef(prefix = "REQUEST_REASON_", value = {
+ REQUEST_REASON_NORMAL,
+ REQUEST_REASON_SHUTDOWN,
+ REQUEST_REASON_HANDOVER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeactivateDataReason {}
+
+
+ /** The reason of the data request is normal */
+ public static final int REQUEST_REASON_NORMAL = 1;
+
+ /** The reason of the data request is device shutdown */
+ public static final int REQUEST_REASON_SHUTDOWN = 2;
+
+ /** The reason of the data request is IWLAN handover */
+ public static final int REQUEST_REASON_HANDOVER = 3;
+
+ private static final int DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER = 1;
+ private static final int DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER = 2;
+ private static final int DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS = 3;
+ private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 4;
+ private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 5;
+ private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 6;
+ private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 7;
+ private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 8;
+ private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 9;
+ private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 10;
+ private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 11;
+
+ private final HandlerThread mHandlerThread;
+
+ private final DataServiceHandler mHandler;
+
+ private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
+
+ /** @hide */
+ @VisibleForTesting
+ public final IDataServiceWrapper mBinder = new IDataServiceWrapper();
+
+ /**
+ * The abstract class of the actual data service implementation. The data service provider
+ * must extend this class to support data connection. Note that each instance of data service
+ * provider is associated with one physical SIM slot.
+ */
+ public class DataServiceProvider {
+
+ private final int mSlotId;
+
+ private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
+
+ /**
+ * Constructor
+ * @param slotId SIM slot id the data service provider associated with.
+ */
+ public DataServiceProvider(int slotId) {
+ mSlotId = slotId;
+ }
+
+ /**
+ * @return SIM slot id the data service provider associated with.
+ */
+ public final int getSlotId() {
+ return mSlotId;
+ }
+
+ /**
+ * Setup a data connection. The data service provider must implement this method to support
+ * establishing a packet data connection. When completed or error, the service must invoke
+ * the provided callback to notify the platform.
+ *
+ * @param accessNetworkType Access network type that the data call will be established on.
+ * Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
+ * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
+ * @param isRoaming True if the device is data roaming.
+ * @param allowRoaming True if data roaming is allowed by the user.
+ * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
+ * {@link #REQUEST_REASON_HANDOVER}.
+ * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
+ * link properties of the existing data connection, otherwise null.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
+ */
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, @SetupDataReason int reason,
+ @Nullable LinkProperties linkProperties,
+ @Nullable DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ /**
+ * Deactivate a data connection. The data service provider must implement this method to
+ * support data connection tear down. When completed or error, the service must invoke the
+ * provided callback to notify the platform.
+ *
+ * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
+ * int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
+ * @param reason The reason for data deactivation. Must be {@link #REQUEST_REASON_NORMAL},
+ * {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
+ *
+ */
+ public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
+ @Nullable DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Set an APN to initial attach network.
+ *
+ * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
+ * @param isRoaming True if the device is data roaming.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
+ */
+ public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ @Nullable DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Send current carrier's data profiles to the data service for data call setup. This is
+ * only for CDMA carrier that can change the profile through OTA. The data service should
+ * always uses the latest data profile sent by the framework.
+ *
+ * @param dps A list of data profiles.
+ * @param isRoaming True if the device is data roaming.
+ * @param callback The result callback for this request. Null if the client does not care
+ * about the result.
+ */
+ public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+ @Nullable DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+ }
+
+ /**
+ * Get the active data call list.
+ *
+ * @param callback The result callback for this request.
+ */
+ public void getDataCallList(@NonNull DataServiceCallback callback) {
+ // The default implementation is to return unsupported.
+ callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+ }
+
+ private void registerForDataCallListChanged(IDataServiceCallback callback) {
+ synchronized (mDataCallListChangedCallbacks) {
+ mDataCallListChangedCallbacks.add(callback);
+ }
+ }
+
+ private void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+ synchronized (mDataCallListChangedCallbacks) {
+ mDataCallListChangedCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Notify the system that current data call list changed. Data service must invoke this
+ * method whenever there is any data call status changed.
+ *
+ * @param dataCallList List of the current active data call.
+ */
+ public final void notifyDataCallListChanged(List<DataCallResponse> dataCallList) {
+ synchronized (mDataCallListChangedCallbacks) {
+ for (IDataServiceCallback callback : mDataCallListChangedCallbacks) {
+ mHandler.obtainMessage(DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED, mSlotId,
+ 0, new DataCallListChangedIndication(dataCallList, callback))
+ .sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
+ */
+ @CallSuper
+ protected void onDestroy() {
+ mDataCallListChangedCallbacks.clear();
+ }
+ }
+
+ private static final class SetupDataCallRequest {
+ public final int accessNetworkType;
+ public final DataProfile dataProfile;
+ public final boolean isRoaming;
+ public final boolean allowRoaming;
+ public final int reason;
+ public final LinkProperties linkProperties;
+ public final IDataServiceCallback callback;
+ SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, LinkProperties linkProperties,
+ IDataServiceCallback callback) {
+ this.accessNetworkType = accessNetworkType;
+ this.dataProfile = dataProfile;
+ this.isRoaming = isRoaming;
+ this.allowRoaming = allowRoaming;
+ this.linkProperties = linkProperties;
+ this.reason = reason;
+ this.callback = callback;
+ }
+ }
+
+ private static final class DeactivateDataCallRequest {
+ public final int cid;
+ public final int reason;
+ public final IDataServiceCallback callback;
+ DeactivateDataCallRequest(int cid, int reason, IDataServiceCallback callback) {
+ this.cid = cid;
+ this.reason = reason;
+ this.callback = callback;
+ }
+ }
+
+ private static final class SetInitialAttachApnRequest {
+ public final DataProfile dataProfile;
+ public final boolean isRoaming;
+ public final IDataServiceCallback callback;
+ SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback) {
+ this.dataProfile = dataProfile;
+ this.isRoaming = isRoaming;
+ this.callback = callback;
+ }
+ }
+
+ private static final class SetDataProfileRequest {
+ public final List<DataProfile> dps;
+ public final boolean isRoaming;
+ public final IDataServiceCallback callback;
+ SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback) {
+ this.dps = dps;
+ this.isRoaming = isRoaming;
+ this.callback = callback;
+ }
+ }
+
+ private static final class DataCallListChangedIndication {
+ public final List<DataCallResponse> dataCallList;
+ public final IDataServiceCallback callback;
+ DataCallListChangedIndication(List<DataCallResponse> dataCallList,
+ IDataServiceCallback callback) {
+ this.dataCallList = dataCallList;
+ this.callback = callback;
+ }
+ }
+
+ private class DataServiceHandler extends Handler {
+
+ DataServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ IDataServiceCallback callback;
+ final int slotId = message.arg1;
+ DataServiceProvider serviceProvider = mServiceMap.get(slotId);
+
+ switch (message.what) {
+ case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
+ serviceProvider = createDataServiceProvider(message.arg1);
+ if (serviceProvider != null) {
+ mServiceMap.put(slotId, serviceProvider);
+ }
+ break;
+ case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ mServiceMap.remove(slotId);
+ }
+ break;
+ case DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS:
+ for (int i = 0; i < mServiceMap.size(); i++) {
+ serviceProvider = mServiceMap.get(i);
+ if (serviceProvider != null) {
+ serviceProvider.onDestroy();
+ }
+ }
+ mServiceMap.clear();
+ break;
+ case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
+ if (serviceProvider == null) break;
+ SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
+ serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
+ setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
+ setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
+ setupDataCallRequest.linkProperties,
+ (setupDataCallRequest.callback != null)
+ ? new DataServiceCallback(setupDataCallRequest.callback)
+ : null);
+
+ break;
+ case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
+ if (serviceProvider == null) break;
+ DeactivateDataCallRequest deactivateDataCallRequest =
+ (DeactivateDataCallRequest) message.obj;
+ serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
+ deactivateDataCallRequest.reason,
+ (deactivateDataCallRequest.callback != null)
+ ? new DataServiceCallback(deactivateDataCallRequest.callback)
+ : null);
+ break;
+ case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
+ if (serviceProvider == null) break;
+ SetInitialAttachApnRequest setInitialAttachApnRequest =
+ (SetInitialAttachApnRequest) message.obj;
+ serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+ setInitialAttachApnRequest.isRoaming,
+ (setInitialAttachApnRequest.callback != null)
+ ? new DataServiceCallback(setInitialAttachApnRequest.callback)
+ : null);
+ break;
+ case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
+ if (serviceProvider == null) break;
+ SetDataProfileRequest setDataProfileRequest =
+ (SetDataProfileRequest) message.obj;
+ serviceProvider.setDataProfile(setDataProfileRequest.dps,
+ setDataProfileRequest.isRoaming,
+ (setDataProfileRequest.callback != null)
+ ? new DataServiceCallback(setDataProfileRequest.callback)
+ : null);
+ break;
+ case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
+ if (serviceProvider == null) break;
+
+ serviceProvider.getDataCallList(new DataServiceCallback(
+ (IDataServiceCallback) message.obj));
+ break;
+ case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
+ if (serviceProvider == null) break;
+ serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+ break;
+ case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
+ if (serviceProvider == null) break;
+ callback = (IDataServiceCallback) message.obj;
+ serviceProvider.unregisterForDataCallListChanged(callback);
+ break;
+ case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
+ if (serviceProvider == null) break;
+ DataCallListChangedIndication indication =
+ (DataCallListChangedIndication) message.obj;
+ try {
+ indication.callback.onDataCallListChanged(indication.dataCallList);
+ } catch (RemoteException e) {
+ loge("Failed to call onDataCallListChanged. " + e);
+ }
+ break;
+ }
+ }
+ }
+
+ /** @hide */
+ protected DataService() {
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+
+ mHandler = new DataServiceHandler(mHandlerThread.getLooper());
+ log("Data service created");
+ }
+
+ /**
+ * Create the instance of {@link DataServiceProvider}. Data service provider must override
+ * this method to facilitate the creation of {@link DataServiceProvider} instances. The system
+ * will call this method after binding the data service for each active SIM slot id.
+ *
+ * @param slotId SIM slot id the data service associated with.
+ * @return Data service object
+ */
+ public abstract DataServiceProvider createDataServiceProvider(int slotId);
+
+ /** @hide */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent == null || !DATA_SERVICE_INTERFACE.equals(intent.getAction())) {
+ loge("Unexpected intent " + intent);
+ return null;
+ }
+ return mBinder;
+ }
+
+ /** @hide */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget();
+ return false;
+ }
+
+ /** @hide */
+ @Override
+ public void onDestroy() {
+ mHandlerThread.quit();
+ }
+
+ /**
+ * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
+ */
+ private class IDataServiceWrapper extends IDataService.Stub {
+ @Override
+ public void createDataServiceProvider(int slotId) {
+ mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotId, 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void removeDataServiceProvider(int slotId) {
+ mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotId, 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
+ boolean isRoaming, boolean allowRoaming, int reason,
+ LinkProperties linkProperties, IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
+ new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
+ allowRoaming, reason, linkProperties, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void deactivateDataCall(int slotId, int cid, int reason,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
+ new DeactivateDataCallRequest(cid, reason, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotId, 0,
+ new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
+ .sendToTarget();
+ }
+
+ @Override
+ public void setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback) {
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotId, 0,
+ new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
+ }
+
+ @Override
+ public void getDataCallList(int slotId, IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("getDataCallList: callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, slotId, 0,
+ callback).sendToTarget();
+ }
+
+ @Override
+ public void registerForDataCallListChanged(int slotId, IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("registerForDataCallListChanged: callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotId,
+ 0, callback).sendToTarget();
+ }
+
+ @Override
+ public void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("unregisterForDataCallListChanged: callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotId,
+ 0, callback).sendToTarget();
+ }
+ }
+
+ private void log(String s) {
+ Rlog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
new file mode 100644
index 0000000..4af31b5
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.net.LinkProperties;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+import android.telephony.data.DataService.DataServiceProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * Data service callback, which is for bound data service to invoke for solicited and unsolicited
+ * response. The caller is responsible to create a callback object for each single asynchronous
+ * request.
+ *
+ * @hide
+ */
+@SystemApi
+public class DataServiceCallback {
+
+ private static final String TAG = DataServiceCallback.class.getSimpleName();
+
+ /**
+ * Result of data requests
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+ RESULT_ERROR_ILLEGAL_STATE})
+ public @interface ResultCode {}
+
+ /** Request is completed successfully */
+ public static final int RESULT_SUCCESS = 0;
+ /** Request is not support */
+ public static final int RESULT_ERROR_UNSUPPORTED = 1;
+ /** Request contains invalid arguments */
+ public static final int RESULT_ERROR_INVALID_ARG = 2;
+ /** Service is busy */
+ public static final int RESULT_ERROR_BUSY = 3;
+ /** Request sent in illegal state */
+ public static final int RESULT_ERROR_ILLEGAL_STATE = 4;
+
+ private final WeakReference<IDataServiceCallback> mCallback;
+
+ /** @hide */
+ public DataServiceCallback(IDataServiceCallback callback) {
+ mCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
+ * DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)} .
+ *
+ * @param result The result code. Must be one of the {@link ResultCode}.
+ * @param response Setup data call response.
+ */
+ public void onSetupDataCallComplete(@ResultCode int result, DataCallResponse response) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetupDataCallComplete(result, response);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
+ * int, DataServiceCallback)}
+ *
+ * @param result The result code. Must be one of the {@link ResultCode}.
+ */
+ public void onDeactivateDataCallComplete(@ResultCode int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onDeactivateDataCallComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
+ * DataProfile, boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link ResultCode}.
+ */
+ public void onSetInitialAttachApnComplete(@ResultCode int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetInitialAttachApnComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
+ * boolean, DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link ResultCode}.
+ */
+ @SystemApi
+ public void onSetDataProfileComplete(@ResultCode int result) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onSetDataProfileComplete(result);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate result for the request {@link DataServiceProvider#getDataCallList(
+ * DataServiceCallback)}.
+ *
+ * @param result The result code. Must be one of the {@link ResultCode}.
+ * @param dataCallList List of the current active data connection.
+ */
+ public void onGetDataCallListComplete(@ResultCode int result,
+ List<DataCallResponse> dataCallList) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onGetDataCallListComplete(result, dataCallList);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "Failed to onGetDataCallListComplete on the remote");
+ }
+ }
+ }
+
+ /**
+ * Called to indicate that data connection list changed.
+ *
+ * @param dataCallList List of the current active data connection.
+ */
+ public void onDataCallListChanged(List<DataCallResponse> dataCallList) {
+ IDataServiceCallback callback = mCallback.get();
+ if (callback != null) {
+ try {
+ callback.onDataCallListChanged(dataCallList);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
+ }
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
new file mode 100644
index 0000000..d4d9be8
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.net.LinkProperties;
+import android.telephony.data.DataProfile;
+import android.telephony.data.IDataServiceCallback;
+
+/**
+ * {@hide}
+ */
+oneway interface IDataService
+{
+ void createDataServiceProvider(int slotId);
+ void removeDataServiceProvider(int slotId);
+ void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
+ boolean allowRoaming, int reason, in LinkProperties linkProperties,
+ IDataServiceCallback callback);
+ void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
+ void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
+ IDataServiceCallback callback);
+ void setDataProfile(int slotId, in List<DataProfile> dps, boolean isRoaming,
+ IDataServiceCallback callback);
+ void getDataCallList(int slotId, IDataServiceCallback callback);
+ void registerForDataCallListChanged(int slotId, IDataServiceCallback callback);
+ void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback);
+}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
new file mode 100644
index 0000000..856185b
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.telephony.data.DataCallResponse;
+
+/**
+ * The call back interface
+ * @hide
+ */
+oneway interface IDataServiceCallback
+{
+ void onSetupDataCallComplete(int result, in DataCallResponse dataCallResponse);
+ void onDeactivateDataCallComplete(int result);
+ void onSetInitialAttachApnComplete(int result);
+ void onSetDataProfileComplete(int result);
+ void onGetDataCallListComplete(int result, in List<DataCallResponse> dataCallList);
+ void onDataCallListChanged(in List<DataCallResponse> dataCallList);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.java b/telephony/java/android/telephony/data/InterfaceAddress.java
deleted file mode 100644
index 00d212a..0000000
--- a/telephony/java/android/telephony/data/InterfaceAddress.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.data;
-
-import android.annotation.SystemApi;
-import android.net.NetworkUtils;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * This class represents a Network Interface address. In short it's an IP address, a subnet mask
- * when the address is an IPv4 one. An IP address and a network prefix length in the case of IPv6
- * address.
- *
- * @hide
- */
-@SystemApi
-public final class InterfaceAddress implements Parcelable {
-
- private final InetAddress mInetAddress;
-
- private final int mPrefixLength;
-
- /**
- * @param inetAddress A {@link InetAddress} of the address
- * @param prefixLength The network prefix length for this address.
- */
- public InterfaceAddress(InetAddress inetAddress, int prefixLength) {
- mInetAddress = inetAddress;
- mPrefixLength = prefixLength;
- }
-
- /**
- * @param address The address in string format
- * @param prefixLength The network prefix length for this address.
- * @throws UnknownHostException
- */
- public InterfaceAddress(String address, int prefixLength) throws UnknownHostException {
- InetAddress ia;
- try {
- ia = NetworkUtils.numericToInetAddress(address);
- } catch (IllegalArgumentException e) {
- throw new UnknownHostException("Non-numeric ip addr=" + address);
- }
- mInetAddress = ia;
- mPrefixLength = prefixLength;
- }
-
- public InterfaceAddress(Parcel source) {
- mInetAddress = (InetAddress) source.readSerializable();
- mPrefixLength = source.readInt();
- }
-
- /**
- * @return an InetAddress for this address.
- */
- public InetAddress getAddress() { return mInetAddress; }
-
- /**
- * @return The network prefix length for this address.
- */
- public int getNetworkPrefixLength() { return mPrefixLength; }
-
- @Override
- public boolean equals (Object o) {
- if (this == o) return true;
-
- if (o == null || !(o instanceof InterfaceAddress)) {
- return false;
- }
-
- InterfaceAddress other = (InterfaceAddress) o;
- return this.mInetAddress.equals(other.mInetAddress)
- && this.mPrefixLength == other.mPrefixLength;
- }
-
- @Override
- public int hashCode() {
- return mInetAddress.hashCode() * 31 + mPrefixLength * 37;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public String toString() {
- return mInetAddress + "/" + mPrefixLength;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeSerializable(mInetAddress);
- dest.writeInt(mPrefixLength);
- }
-
- public static final Parcelable.Creator<InterfaceAddress> CREATOR =
- new Parcelable.Creator<InterfaceAddress>() {
- @Override
- public InterfaceAddress createFromParcel(Parcel source) {
- return new InterfaceAddress(source);
- }
-
- @Override
- public InterfaceAddress[] newArray(int size) {
- return new InterfaceAddress[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index 01041c8..88db22b 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -16,18 +16,17 @@
package android.telephony.euicc;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.UiccAccessRule;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import com.android.internal.util.Preconditions;
-/**
- * Information about a subscription which is available for download.
- *
- * TODO(b/35851809): Make this public.
- * @hide
- */
+/** Information about a subscription which is available for download. */
public final class DownloadableSubscription implements Parcelable {
public static final Creator<DownloadableSubscription> CREATOR =
@@ -46,11 +45,12 @@
/**
* Activation code. May be null for subscriptions which are not based on activation codes, e.g.
* to download a default subscription assigned to this device.
+ * Should use getEncodedActivationCode() instead.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
+ * @deprecated - Do not use. This will be private. Use getEncodedActivationCode() instead.
*/
@Nullable
+ @Deprecated
public final String encodedActivationCode;
@Nullable private String confirmationCode;
@@ -58,8 +58,16 @@
// see getCarrierName and setCarrierName
@Nullable
private String carrierName;
+
// see getAccessRules and setAccessRules
- private UiccAccessRule[] accessRules;
+ @Nullable
+ private List<UiccAccessRule> accessRules;
+
+ /** Gets the activation code. */
+ @Nullable
+ public String getEncodedActivationCode() {
+ return encodedActivationCode;
+ }
/** @hide */
private DownloadableSubscription(String encodedActivationCode) {
@@ -70,7 +78,59 @@
encodedActivationCode = in.readString();
confirmationCode = in.readString();
carrierName = in.readString();
- accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
+ accessRules = new ArrayList<UiccAccessRule>();
+ in.readTypedList(accessRules, UiccAccessRule.CREATOR);
+ }
+
+ private DownloadableSubscription(String encodedActivationCode, String confirmationCode,
+ String carrierName, List<UiccAccessRule> accessRules) {
+ this.encodedActivationCode = encodedActivationCode;
+ this.confirmationCode = confirmationCode;
+ this.carrierName = carrierName;
+ this.accessRules = accessRules;
+ }
+
+ /** @hide */
+ @SystemApi
+ public static final class Builder {
+ @Nullable private String encodedActivationCode;
+ @Nullable private String confirmationCode;
+ @Nullable private String carrierName;
+ List<UiccAccessRule> accessRules;
+
+ public Builder() {}
+
+ public Builder(DownloadableSubscription baseSubscription) {
+ encodedActivationCode = baseSubscription.getEncodedActivationCode();
+ confirmationCode = baseSubscription.getConfirmationCode();
+ carrierName = baseSubscription.getCarrierName();
+ accessRules = baseSubscription.getAccessRules();
+ }
+
+ public DownloadableSubscription build() {
+ return new DownloadableSubscription(encodedActivationCode, confirmationCode,
+ carrierName, accessRules);
+ }
+
+ public Builder setEncodedActivationCode(String value) {
+ encodedActivationCode = value;
+ return this;
+ }
+
+ public Builder setConfirmationCode(String value) {
+ confirmationCode = value;
+ return this;
+ }
+
+ public Builder setCarrierName(String value) {
+ carrierName = value;
+ return this;
+ }
+
+ public Builder setAccessRules(List<UiccAccessRule> value) {
+ accessRules = value;
+ return this;
+ }
}
/**
@@ -87,7 +147,10 @@
/**
* Sets the confirmation code.
+ * @hide
+ * @deprecated - Do not use.
*/
+ @Deprecated
public void setConfirmationCode(String confirmationCode) {
this.confirmationCode = confirmationCode;
}
@@ -103,9 +166,9 @@
/**
* Set the user-visible carrier name.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
+ * @deprecated - Do not use.
*/
+ @Deprecated
public void setCarrierName(String carrierName) {
this.carrierName = carrierName;
}
@@ -117,44 +180,51 @@
* those created with {@link #forActivationCode}). May be populated with
* {@link EuiccManager#getDownloadableSubscriptionMetadata}.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
@Nullable
public String getCarrierName() {
return carrierName;
}
/**
- * Returns the {@link UiccAccessRule}s dictating access to this subscription.
+ * Returns the {@link UiccAccessRule}s in list dictating access to this subscription.
*
* <p>Only present for downloadable subscriptions that were queried from a server (as opposed to
* those created with {@link #forActivationCode}). May be populated with
* {@link EuiccManager#getDownloadableSubscriptionMetadata}.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
- public UiccAccessRule[] getAccessRules() {
+ @SystemApi
+ public List<UiccAccessRule> getAccessRules() {
return accessRules;
}
/**
* Set the {@link UiccAccessRule}s dictating access to this subscription.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
+ * @deprecated - Do not use.
*/
- public void setAccessRules(UiccAccessRule[] accessRules) {
+ @Deprecated
+ public void setAccessRules(List<UiccAccessRule> accessRules) {
this.accessRules = accessRules;
}
+ /**
+ * @hide
+ * @deprecated - Do not use.
+ */
+ @Deprecated
+ public void setAccessRules(UiccAccessRule[] accessRules) {
+ this.accessRules = Arrays.asList(accessRules);
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(encodedActivationCode);
dest.writeString(confirmationCode);
dest.writeString(carrierName);
- dest.writeTypedArray(accessRules, flags);
+ dest.writeTypedList(accessRules);
}
@Override
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
new file mode 100644
index 0000000..6be7725
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -0,0 +1,719 @@
+/*
+ * 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.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.euicc.EuiccProfileInfo;
+import android.util.Log;
+
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IDeleteProfileCallback;
+import com.android.internal.telephony.euicc.IDisableProfileCallback;
+import com.android.internal.telephony.euicc.IEuiccCardController;
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetProfileCallback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IGetSmdsAddressCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IResetMemoryCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
+import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.ISetNicknameCallback;
+import com.android.internal.telephony.euicc.ISwitchToProfileCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * EuiccCardManager is the application interface to an eSIM card.
+ * @hide
+ */
+@SystemApi
+public class EuiccCardManager {
+ private static final String TAG = "EuiccCardManager";
+
+ /** Reason for canceling a profile download session */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CANCEL_REASON_" }, value = {
+ CANCEL_REASON_END_USER_REJECTED,
+ CANCEL_REASON_POSTPONED,
+ CANCEL_REASON_TIMEOUT,
+ CANCEL_REASON_PPR_NOT_ALLOWED
+ })
+ /** @hide */
+ public @interface CancelReason {}
+
+ /**
+ * The end user has rejected the download. The profile will be put into the error state and
+ * cannot be downloaded again without the operator's change.
+ */
+ public static final int CANCEL_REASON_END_USER_REJECTED = 0;
+
+ /** The download has been postponed and can be restarted later. */
+ public static final int CANCEL_REASON_POSTPONED = 1;
+
+ /** The download has been timed out and can be restarted later. */
+ public static final int CANCEL_REASON_TIMEOUT = 2;
+
+ /**
+ * The profile to be downloaded cannot be installed due to its policy rule is not allowed by
+ * the RAT (Rules Authorisation Table) on the eUICC or by other installed profiles. The
+ * download can be restarted later.
+ */
+ public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3;
+
+ /** Options for resetting eUICC memory */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "RESET_OPTION_" }, value = {
+ RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
+ RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
+ RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
+ })
+ /** @hide */
+ public @interface ResetOption {}
+
+ /** Deletes all operational profiles. */
+ public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1;
+
+ /** Deletes all field-loaded testing profiles. */
+ public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 1 << 1;
+
+ /** Resets the default SM-DP+ address. */
+ public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 1 << 2;
+
+ /** Result code of execution with no error. */
+ public static final int RESULT_OK = 0;
+
+ /** Result code of an unknown error. */
+ public static final int RESULT_UNKNOWN_ERROR = -1;
+
+ /**
+ * Callback to receive the result of an eUICC card API.
+ *
+ * @param <T> Type of the result.
+ */
+ public interface ResultCallback<T> {
+ /**
+ * This method will be called when an eUICC card API call is completed.
+ *
+ * @param resultCode This can be {@link #RESULT_OK} or other positive values returned by the
+ * eUICC.
+ * @param result The result object. It can be null if the {@code resultCode} is not
+ * {@link #RESULT_OK}.
+ */
+ void onComplete(int resultCode, T result);
+ }
+
+ private final Context mContext;
+
+ /** @hide */
+ public EuiccCardManager(Context context) {
+ mContext = context;
+ }
+
+ private IEuiccCardController getIEuiccCardController() {
+ return IEuiccCardController.Stub.asInterface(
+ ServiceManager.getService("euicc_card_controller"));
+ }
+
+ /**
+ * Requests all the profiles on eUicc.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code and all the profiles.
+ */
+ public void requestAllProfiles(String cardId, Executor executor,
+ ResultCallback<EuiccProfileInfo[]> callback) {
+ try {
+ getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(), cardId,
+ new IGetAllProfilesCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
+ executor.execute(() -> callback.onComplete(resultCode, profiles));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getAllProfiles", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the profile of the given iccid.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code and profile.
+ */
+ public void requestProfile(String cardId, String iccid, Executor executor,
+ ResultCallback<EuiccProfileInfo> callback) {
+ try {
+ getIEuiccCardController().getProfile(mContext.getOpPackageName(), cardId, iccid,
+ new IGetProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo profile) {
+ executor.execute(() -> callback.onComplete(resultCode, profile));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disables the profile of the given iccid.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code.
+ */
+ public void disableProfile(String cardId, String iccid, boolean refresh, Executor executor,
+ ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
+ refresh, new IDisableProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ executor.execute(() -> callback.onComplete(resultCode, null));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling disableProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Switches from the current profile to another profile. The current profile will be disabled
+ * and the specified profile will be enabled.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile to switch to.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+ */
+ public void switchToProfile(String cardId, String iccid, boolean refresh, Executor executor,
+ ResultCallback<EuiccProfileInfo> callback) {
+ try {
+ getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
+ refresh, new ISwitchToProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo profile) {
+ executor.execute(() -> callback.onComplete(resultCode, profile));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling switchToProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the nickname of the profile of the given iccid.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
+ * @param nickname The nickname of the profile.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code.
+ */
+ public void setNickname(String cardId, String iccid, String nickname, Executor executor,
+ ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().setNickname(mContext.getOpPackageName(), cardId, iccid,
+ nickname, new ISetNicknameCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ executor.execute(() -> callback.onComplete(resultCode, null));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setNickname", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Deletes the profile of the given iccid from eUICC.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code.
+ */
+ public void deleteProfile(String cardId, String iccid, Executor executor,
+ ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), cardId, iccid,
+ new IDeleteProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ executor.execute(() -> callback.onComplete(resultCode, null));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling deleteProfile", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Resets the eUICC memory.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param options Bits of the options of resetting which parts of the eUICC memory. See
+ * EuiccCard for details.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code.
+ */
+ public void resetMemory(String cardId, @ResetOption int options, Executor executor,
+ ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().resetMemory(mContext.getOpPackageName(), cardId, options,
+ new IResetMemoryCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ executor.execute(() -> callback.onComplete(resultCode, null));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling resetMemory", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the default SM-DP+ address from eUICC.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code and the default SM-DP+ address.
+ */
+ public void requestDefaultSmdpAddress(String cardId, Executor executor,
+ ResultCallback<String> callback) {
+ try {
+ getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
+ new IGetDefaultSmdpAddressCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, String address) {
+ executor.execute(() -> callback.onComplete(resultCode, address));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getDefaultSmdpAddress", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the SM-DS address from eUICC.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code and the SM-DS address.
+ */
+ public void requestSmdsAddress(String cardId, Executor executor,
+ ResultCallback<String> callback) {
+ try {
+ getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(), cardId,
+ new IGetSmdsAddressCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, String address) {
+ executor.execute(() -> callback.onComplete(resultCode, address));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getSmdsAddress", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the default SM-DP+ address of eUICC.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param defaultSmdpAddress The default SM-DP+ address to set.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback The callback to get the result code.
+ */
+ public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, Executor executor,
+ ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
+ defaultSmdpAddress,
+ new ISetDefaultSmdpAddressCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ executor.execute(() -> callback.onComplete(resultCode, null));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setDefaultSmdpAddress", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests Rules Authorisation Table.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and the rule authorisation table.
+ */
+ public void requestRulesAuthTable(String cardId, Executor executor,
+ ResultCallback<EuiccRulesAuthTable> callback) {
+ try {
+ getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(), cardId,
+ new IGetRulesAuthTableCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
+ executor.execute(() -> callback.onComplete(resultCode, rat));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getRulesAuthTable", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the eUICC challenge for new profile downloading.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and the challenge.
+ */
+ public void requestEuiccChallenge(String cardId, Executor executor,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(), cardId,
+ new IGetEuiccChallengeCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] challenge) {
+ executor.execute(() -> callback.onComplete(resultCode, challenge));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccChallenge", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and the info1.
+ */
+ public void requestEuiccInfo1(String cardId, Executor executor,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(), cardId,
+ new IGetEuiccInfo1Callback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] info) {
+ executor.execute(() -> callback.onComplete(resultCode, info));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccInfo1", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and the info2.
+ */
+ public void requestEuiccInfo2(String cardId, Executor executor,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(), cardId,
+ new IGetEuiccInfo2Callback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] info) {
+ executor.execute(() -> callback.onComplete(resultCode, info));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getEuiccInfo2", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Authenticates the SM-DP+ server by the eUICC.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param matchingId the activation code token defined in GSMA RSP v2.0+ or empty when it is not
+ * required.
+ * @param serverSigned1 ASN.1 data in byte array signed and returned by the SM-DP+ server.
+ * @param serverSignature1 ASN.1 data in byte array indicating a SM-DP+ signature which is
+ * returned by SM-DP+ server.
+ * @param euiccCiPkIdToBeUsed ASN.1 data in byte array indicating CI Public Key Identifier to be
+ * used by the eUICC for signature which is returned by SM-DP+ server. This is defined in
+ * GSMA RSP v2.0+.
+ * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
+ * SM-DP+ server.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
+ */
+ public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1,
+ byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
+ Executor executor, ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().authenticateServer(
+ mContext.getOpPackageName(),
+ cardId,
+ matchingId,
+ serverSigned1,
+ serverSignature1,
+ euiccCiPkIdToBeUsed,
+ serverCertificate,
+ new IAuthenticateServerCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ executor.execute(() -> callback.onComplete(resultCode, response));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling authenticateServer", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Prepares the profile download request sent to SM-DP+.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param hashCc the hash of confirmation code. It can be null if there is no confirmation code
+ * required.
+ * @param smdpSigned2 ASN.1 data in byte array indicating the data to be signed by the SM-DP+
+ * returned by SM-DP+ server.
+ * @param smdpSignature2 ASN.1 data in byte array indicating the SM-DP+ signature returned by
+ * SM-DP+ server.
+ * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
+ * by SM-DP+ server.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
+ */
+ public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2,
+ byte[] smdpSignature2, byte[] smdpCertificate, Executor executor,
+ ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().prepareDownload(
+ mContext.getOpPackageName(),
+ cardId,
+ hashCc,
+ smdpSigned2,
+ smdpSignature2,
+ smdpCertificate,
+ new IPrepareDownloadCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ executor.execute(() -> callback.onComplete(resultCode, response));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling prepareDownload", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Loads a downloaded bound profile package onto the eUICC.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and a byte array which represents a
+ * {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
+ */
+ public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage,
+ Executor executor, ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().loadBoundProfilePackage(
+ mContext.getOpPackageName(),
+ cardId,
+ boundProfilePackage,
+ new ILoadBoundProfilePackageCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ executor.execute(() -> callback.onComplete(resultCode, response));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling loadBoundProfilePackage", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Cancels the current profile download session.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param transactionId the transaction ID returned by SM-DP+ server.
+ * @param reason the cancel reason.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and an byte[] which represents a
+ * {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
+ */
+ public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason,
+ Executor executor, ResultCallback<byte[]> callback) {
+ try {
+ getIEuiccCardController().cancelSession(
+ mContext.getOpPackageName(),
+ cardId,
+ transactionId,
+ reason,
+ new ICancelSessionCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, byte[] response) {
+ executor.execute(() -> callback.onComplete(resultCode, response));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling cancelSession", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Lists all notifications of the given {@code notificationEvents}.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and the list of notifications.
+ */
+ public void listNotifications(String cardId, @EuiccNotification.Event int events,
+ Executor executor, ResultCallback<EuiccNotification[]> callback) {
+ try {
+ getIEuiccCardController().listNotifications(mContext.getOpPackageName(), cardId, events,
+ new IListNotificationsCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification[] notifications) {
+ executor.execute(() -> callback.onComplete(resultCode, notifications));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling listNotifications", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves contents of all notification of the given {@code events}.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and the list of notifications.
+ */
+ public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
+ Executor executor, ResultCallback<EuiccNotification[]> callback) {
+ try {
+ getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), cardId,
+ events, new IRetrieveNotificationListCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification[] notifications) {
+ executor.execute(() -> callback.onComplete(resultCode, notifications));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling retrieveNotificationList", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves the content of a notification of the given {@code seqNumber}.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param seqNumber the sequence number of the notification.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code and the notification.
+ */
+ public void retrieveNotification(String cardId, int seqNumber, Executor executor,
+ ResultCallback<EuiccNotification> callback) {
+ try {
+ getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), cardId,
+ seqNumber, new IRetrieveNotificationCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccNotification notification) {
+ executor.execute(() -> callback.onComplete(resultCode, notification));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling retrieveNotification", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a notification from eUICC.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param seqNumber the sequence number of the notification.
+ * @param executor The executor through which the callback should be invode.
+ * @param callback the callback to get the result code.
+ */
+ public void removeNotificationFromList(String cardId, int seqNumber, Executor executor,
+ ResultCallback<Void> callback) {
+ try {
+ getIEuiccCardController().removeNotificationFromList(
+ mContext.getOpPackageName(),
+ cardId,
+ seqNumber,
+ new IRemoveNotificationFromListCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode) {
+ executor.execute(() -> callback.onComplete(resultCode, null));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling removeNotificationFromList", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/euicc/EuiccInfo.java b/telephony/java/android/telephony/euicc/EuiccInfo.java
index 5bfff08..a4adf05 100644
--- a/telephony/java/android/telephony/euicc/EuiccInfo.java
+++ b/telephony/java/android/telephony/euicc/EuiccInfo.java
@@ -23,9 +23,6 @@
* Information about an eUICC chip/device.
*
* @see EuiccManager#getEuiccInfo
- * @hide
- *
- * TODO(b/35851809): Make this public.
*/
// WARNING: Do not add any privacy-sensitive fields to this class (such as an eUICC identifier)!
// This API is accessible to all applications. Privacy-sensitive fields should be returned in their
@@ -45,12 +42,17 @@
}
};
+ @Nullable
+ private final String osVersion;
+
/**
- * Version of the operating system running on the eUICC. This field is hardware-specific and is
- * not guaranteed to match any particular format.
+ * Gets the version of the operating system running on the eUICC. This field is
+ * hardware-specific and is not guaranteed to match any particular format.
*/
@Nullable
- public final String osVersion;
+ public String getOsVersion() {
+ return osVersion;
+ }
public EuiccInfo(@Nullable String osVersion) {
this.osVersion = osVersion;
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 84d0087..71ef5de 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -15,8 +15,11 @@
*/
package android.telephony.euicc;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
@@ -29,6 +32,9 @@
import com.android.internal.telephony.euicc.IEuiccController;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs.
*
@@ -36,9 +42,6 @@
* {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}.
*
* <p>See {@link #isEnabled} before attempting to use these APIs.
- *
- * TODO(b/35851809): Make this public.
- * @hide
*/
public class EuiccManager {
@@ -50,11 +53,37 @@
*
* <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
* {@link #isEnabled} is false.
+ *
+ * This is ued by non-LPA app to bring up LUI.
*/
@SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
"android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+
+ /**
+ * Broadcast Action: The eUICC OTA status is changed.
+ * <p class="note">
+ * Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_OTA_STATUS_CHANGED =
+ "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+
+ /**
+ * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not
+ * completed.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE =
+ "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
+
/**
* Intent action to provision an embedded subscription.
*
@@ -65,8 +94,9 @@
* <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
* {@link #isEnabled} is false or if the device is already provisioned.
*
- * TODO(b/35851809): Make this a SystemApi.
+ * @hide
*/
+ @SystemApi
@SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
"android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
@@ -113,9 +143,8 @@
* Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result
* callbacks providing the downloadable subscription metadata.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
@@ -123,9 +152,8 @@
* Key for an extra set on {@link #getDefaultDownloadableSubscriptionList} PendingIntent result
* callbacks providing the list of available downloadable subscriptions.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
@@ -167,6 +195,58 @@
*/
public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
+ /**
+ * Euicc OTA update status which can be got by {@link #getOtaStatus}
+ * @hide
+ */
+ @SystemApi
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"EUICC_OTA_"}, value = {
+ EUICC_OTA_IN_PROGRESS,
+ EUICC_OTA_FAILED,
+ EUICC_OTA_SUCCEEDED,
+ EUICC_OTA_NOT_NEEDED,
+ EUICC_OTA_STATUS_UNAVAILABLE
+
+ })
+ public @interface OtaStatus{}
+
+ /**
+ * An OTA is in progress. During this time, the eUICC is not available and the user may lose
+ * network access.
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_OTA_IN_PROGRESS = 1;
+
+ /**
+ * The OTA update failed.
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_OTA_FAILED = 2;
+
+ /**
+ * The OTA update finished successfully.
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_OTA_SUCCEEDED = 3;
+
+ /**
+ * The OTA update not needed since current eUICC OS is latest.
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_OTA_NOT_NEEDED = 4;
+
+ /**
+ * The OTA status is unavailable since eUICC service is unavailable.
+ * @hide
+ */
+ @SystemApi
+ public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
+
private final Context mContext;
/** @hide */
@@ -211,9 +291,31 @@
}
/**
+ * Returns the current status of eUICC OTA.
+ *
+ * <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.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getOtaStatus() {
+ if (!isEnabled()) {
+ return EUICC_OTA_STATUS_UNAVAILABLE;
+ }
+ try {
+ return getIEuiccController().getOtaStatus();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Attempt to download the given {@link DownloadableSubscription}.
*
- * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+ * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
* or the calling app must be authorized to manage both the currently-active subscription and
* the subscription to be downloaded according to the subscription metadata. Without the former,
* an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
@@ -275,14 +377,16 @@
*
* <p>To be called by the LUI upon completion of a resolvable error flow.
*
+ * <p>Requires that the calling app has the
+ * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
* @param resolutionIntent The original intent used to start the LUI.
* @param resolutionExtras Resolution-specific extras depending on the result of the resolution.
* For example, this may indicate whether the user has consented or may include the input
* they provided.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
if (!isEnabled()) {
PendingIntent callbackIntent =
@@ -316,9 +420,8 @@
* @param subscription the subscription which needs metadata filled in
* @param callbackIntent a PendingIntent to launch when the operation completes.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public void getDownloadableSubscriptionMetadata(
DownloadableSubscription subscription, PendingIntent callbackIntent) {
if (!isEnabled()) {
@@ -347,9 +450,8 @@
*
* @param callbackIntent a PendingIntent to launch when the operation completes.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
if (!isEnabled()) {
sendUnavailableError(callbackIntent);
@@ -389,7 +491,7 @@
*
* <p>Requires that the calling app has carrier privileges according to the metadata of the
* profile to be deleted, or the
- * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
*
* @param subscriptionId the ID of the subscription to delete.
* @param callbackIntent a PendingIntent to launch when the operation completes.
@@ -410,7 +512,7 @@
/**
* Switch to (enable) the given subscription.
*
- * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+ * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
* or the calling app must be authorized to manage both the currently-active subscription and
* the subscription to be enabled according to the subscription metadata. Without the former,
* an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
similarity index 82%
copy from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
copy to telephony/java/android/telephony/euicc/EuiccNotification.aidl
index d648a35..dad770d 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.euicc;
-parcelable ImsStreamMediaProfile;
+parcelable EuiccNotification;
diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.java b/telephony/java/android/telephony/euicc/EuiccNotification.java
new file mode 100644
index 0000000..43a7707
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.java
@@ -0,0 +1,180 @@
+/*
+ * 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.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * This represents a signed notification which is defined in SGP.22. It can be either a profile
+ * installation result or a notification generated for profile operations (e.g., enabling,
+ * disabling, or deleting).
+ *
+ * @hide
+ */
+@SystemApi
+public final class EuiccNotification implements Parcelable {
+ /** Event */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+ EVENT_INSTALL,
+ EVENT_ENABLE,
+ EVENT_DISABLE,
+ EVENT_DELETE
+ })
+ /** @hide */
+ public @interface Event {}
+
+ /** A profile is downloaded and installed. */
+ public static final int EVENT_INSTALL = 1;
+
+ /** A profile is enabled. */
+ public static final int EVENT_ENABLE = 1 << 1;
+
+ /** A profile is disabled. */
+ public static final int EVENT_DISABLE = 1 << 2;
+
+ /** A profile is deleted. */
+ public static final int EVENT_DELETE = 1 << 3;
+
+ /** Value of the bits of all the events including install, enable, disable and delete. */
+ @Event
+ public static final int ALL_EVENTS =
+ EVENT_INSTALL | EVENT_ENABLE | EVENT_DISABLE | EVENT_DELETE;
+
+ private final int mSeq;
+ private final String mTargetAddr;
+ @Event private final int mEvent;
+ @Nullable private final byte[] mData;
+
+ /**
+ * Creates an instance.
+ *
+ * @param seq The sequence number of this notification.
+ * @param targetAddr The target server where to send this notification.
+ * @param event The event which causes this notification.
+ * @param data The data which needs to be sent to the target server. This can be null for
+ * building a list of notification metadata without data.
+ */
+ public EuiccNotification(int seq, String targetAddr, @Event int event, @Nullable byte[] data) {
+ mSeq = seq;
+ mTargetAddr = targetAddr;
+ mEvent = event;
+ mData = data;
+ }
+
+ /** @return The sequence number of this notification. */
+ public int getSeq() {
+ return mSeq;
+ }
+
+ /** @return The target server address where this notification should be sent to. */
+ public String getTargetAddr() {
+ return mTargetAddr;
+ }
+
+ /** @return The event of this notification. */
+ @Event
+ public int getEvent() {
+ return mEvent;
+ }
+
+ /** @return The notification data which needs to be sent to the target server. */
+ @Nullable
+ public byte[] getData() {
+ return mData;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ EuiccNotification that = (EuiccNotification) obj;
+ return mSeq == that.mSeq
+ && Objects.equals(mTargetAddr, that.mTargetAddr)
+ && mEvent == that.mEvent
+ && Arrays.equals(mData, that.mData);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + mSeq;
+ result = 31 * result + Objects.hashCode(mTargetAddr);
+ result = 31 * result + mEvent;
+ result = 31 * result + Arrays.hashCode(mData);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "EuiccNotification (seq="
+ + mSeq
+ + ", targetAddr="
+ + mTargetAddr
+ + ", event="
+ + mEvent
+ + ", data="
+ + (mData == null ? "null" : "byte[" + mData.length + "]")
+ + ")";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSeq);
+ dest.writeString(mTargetAddr);
+ dest.writeInt(mEvent);
+ dest.writeByteArray(mData);
+ }
+
+ private EuiccNotification(Parcel source) {
+ mSeq = source.readInt();
+ mTargetAddr = source.readString();
+ mEvent = source.readInt();
+ mData = source.createByteArray();
+ }
+
+ public static final Creator<EuiccNotification> CREATOR =
+ new Creator<EuiccNotification>() {
+ @Override
+ public EuiccNotification createFromParcel(Parcel source) {
+ return new EuiccNotification(source);
+ }
+
+ @Override
+ public EuiccNotification[] newArray(int size) {
+ return new EuiccNotification[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
similarity index 83%
copy from telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
copy to telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
index e890cf8..9785a45 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
@@ -11,9 +11,9 @@
* 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
+ * limitations under the License.
*/
-package android.telephony.ims.internal.stub;
+package android.telephony.euicc;
-parcelable ImsFeatureConfiguration;
+parcelable EuiccRulesAuthTable;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
new file mode 100644
index 0000000..67ae983
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -0,0 +1,264 @@
+/*
+ * 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.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This represents the RAT (Rules Authorisation Table) stored on eUICC.
+ * @hide
+ */
+@SystemApi
+public final class EuiccRulesAuthTable implements Parcelable {
+ /** Profile policy rule flags */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
+ POLICY_RULE_FLAG_CONSENT_REQUIRED
+ })
+ /** @hide */
+ public @interface PolicyRuleFlag {}
+
+ /** User consent is required to install the profile. */
+ public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1;
+
+ private final int[] mPolicyRules;
+ private final CarrierIdentifier[][] mCarrierIds;
+ private final int[] mPolicyRuleFlags;
+
+ /** This is used to build new {@link EuiccRulesAuthTable} instance. */
+ public static final class Builder {
+ private int[] mPolicyRules;
+ private CarrierIdentifier[][] mCarrierIds;
+ private int[] mPolicyRuleFlags;
+ private int mPosition;
+
+ /**
+ * Creates a new builder.
+ *
+ * @param ruleNum The number of authorisation rules in the table.
+ */
+ public Builder(int ruleNum) {
+ mPolicyRules = new int[ruleNum];
+ mCarrierIds = new CarrierIdentifier[ruleNum][];
+ mPolicyRuleFlags = new int[ruleNum];
+ }
+
+ /**
+ * Builds the RAT instance. This builder should not be used anymore after this method is
+ * called, otherwise {@link NullPointerException} will be thrown.
+ */
+ public EuiccRulesAuthTable build() {
+ if (mPosition != mPolicyRules.length) {
+ throw new IllegalStateException(
+ "Not enough rules are added, expected: "
+ + mPolicyRules.length
+ + ", added: "
+ + mPosition);
+ }
+ return new EuiccRulesAuthTable(mPolicyRules, mCarrierIds, mPolicyRuleFlags);
+ }
+
+ /**
+ * Adds an authorisation rule.
+ *
+ * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size
+ * this table.
+ */
+ public Builder add(int policyRules, List<CarrierIdentifier> carrierId, int policyRuleFlags) {
+ if (mPosition >= mPolicyRules.length) {
+ throw new ArrayIndexOutOfBoundsException(mPosition);
+ }
+ mPolicyRules[mPosition] = policyRules;
+ if (carrierId != null && carrierId.size() > 0) {
+ mCarrierIds[mPosition] = carrierId.toArray(new CarrierIdentifier[carrierId.size()]);
+ }
+ mPolicyRuleFlags[mPosition] = policyRuleFlags;
+ mPosition++;
+ return this;
+ }
+ }
+
+ /**
+ * @param mccRule A 2-character or 3-character string which can be either MCC or MNC. The
+ * character 'E' is used as a wild char to match any digit.
+ * @param mcc A 2-character or 3-character string which can be either MCC or MNC.
+ * @return Whether the {@code mccRule} matches {@code mcc}.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static boolean match(String mccRule, String mcc) {
+ if (mccRule.length() < mcc.length()) {
+ return false;
+ }
+ for (int i = 0; i < mccRule.length(); i++) {
+ // 'E' is the wild char to match any digit.
+ if (mccRule.charAt(i) == 'E'
+ || (i < mcc.length() && mccRule.charAt(i) == mcc.charAt(i))) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds,
+ int[] policyRuleFlags) {
+ mPolicyRules = policyRules;
+ mCarrierIds = carrierIds;
+ mPolicyRuleFlags = policyRuleFlags;
+ }
+
+ /**
+ * Finds the index of the first authorisation rule matching the given policy and carrier id. If
+ * the returned index is not negative, the carrier is allowed to apply this policy to its
+ * profile.
+ *
+ * @param policy The policy rule.
+ * @param carrierId The carrier id.
+ * @return The index of authorization rule. If no rule is found, -1 will be returned.
+ */
+ public int findIndex(@EuiccProfileInfo.PolicyRule int policy, CarrierIdentifier carrierId) {
+ for (int i = 0; i < mPolicyRules.length; i++) {
+ if ((mPolicyRules[i] & policy) == 0) {
+ continue;
+ }
+ CarrierIdentifier[] carrierIds = mCarrierIds[i];
+ if (carrierIds == null || carrierIds.length == 0) {
+ continue;
+ }
+ for (int j = 0; j < carrierIds.length; j++) {
+ CarrierIdentifier ruleCarrierId = carrierIds[j];
+ if (!match(ruleCarrierId.getMcc(), carrierId.getMcc())
+ || !match(ruleCarrierId.getMnc(), carrierId.getMnc())) {
+ continue;
+ }
+ String gid = ruleCarrierId.getGid1();
+ if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid1())) {
+ continue;
+ }
+ gid = ruleCarrierId.getGid2();
+ if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid2())) {
+ continue;
+ }
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Tests if the entry in the table has the given policy rule flag.
+ *
+ * @param index The index of the entry.
+ * @param flag The policy rule flag to be tested.
+ * @throws ArrayIndexOutOfBoundsException If the {@code index} is negative or larger than the
+ * size of this table.
+ */
+ public boolean hasPolicyRuleFlag(int index, @PolicyRuleFlag int flag) {
+ if (index < 0 || index >= mPolicyRules.length) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return (mPolicyRuleFlags[index] & flag) != 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeIntArray(mPolicyRules);
+ for (CarrierIdentifier[] ids : mCarrierIds) {
+ dest.writeTypedArray(ids, flags);
+ }
+ dest.writeIntArray(mPolicyRuleFlags);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ EuiccRulesAuthTable that = (EuiccRulesAuthTable) obj;
+ if (mCarrierIds.length != that.mCarrierIds.length) {
+ return false;
+ }
+ for (int i = 0; i < mCarrierIds.length; i++) {
+ CarrierIdentifier[] carrierIds = mCarrierIds[i];
+ CarrierIdentifier[] thatCarrierIds = that.mCarrierIds[i];
+ if (carrierIds != null && thatCarrierIds != null) {
+ if (carrierIds.length != thatCarrierIds.length) {
+ return false;
+ }
+ for (int j = 0; j < carrierIds.length; j++) {
+ if (!carrierIds[j].equals(thatCarrierIds[j])) {
+ return false;
+ }
+ }
+ continue;
+ } else if (carrierIds == null && thatCarrierIds == null) {
+ continue;
+ }
+ return false;
+ }
+
+ return Arrays.equals(mPolicyRules, that.mPolicyRules)
+ && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
+ }
+
+ private EuiccRulesAuthTable(Parcel source) {
+ mPolicyRules = source.createIntArray();
+ int len = mPolicyRules.length;
+ mCarrierIds = new CarrierIdentifier[len][];
+ for (int i = 0; i < len; i++) {
+ mCarrierIds[i] = source.createTypedArray(CarrierIdentifier.CREATOR);
+ }
+ mPolicyRuleFlags = source.createIntArray();
+ }
+
+ public static final Creator<EuiccRulesAuthTable> CREATOR =
+ new Creator<EuiccRulesAuthTable>() {
+ @Override
+ public EuiccRulesAuthTable createFromParcel(Parcel source) {
+ return new EuiccRulesAuthTable(source);
+ }
+
+ @Override
+ public EuiccRulesAuthTable[] newArray(int size) {
+ return new EuiccRulesAuthTable[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/ims/ImsCallForwardInfo.aidl b/telephony/java/android/telephony/ims/ImsCallForwardInfo.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsCallForwardInfo.aidl
rename to telephony/java/android/telephony/ims/ImsCallForwardInfo.aidl
index a7c3f9a..b322b39 100644
--- a/telephony/java/com/android/ims/ImsCallForwardInfo.aidl
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsCallForwardInfo;
diff --git a/telephony/java/com/android/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
similarity index 77%
rename from telephony/java/com/android/ims/ImsCallForwardInfo.java
rename to telephony/java/android/telephony/ims/ImsCallForwardInfo.java
index eeee0fc..6d72181 100644
--- a/telephony/java/com/android/ims/ImsCallForwardInfo.java
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -11,11 +11,12 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,23 +25,32 @@
*
* @hide
*/
-public class ImsCallForwardInfo implements Parcelable {
+@SystemApi
+public final class ImsCallForwardInfo implements Parcelable {
// Refer to ImsUtInterface#CDIV_CF_XXX
+ /** @hide */
public int mCondition;
// 0: disabled, 1: enabled
+ /** @hide */
public int mStatus;
// 0x91: International, 0x81: Unknown
+ /** @hide */
public int mToA;
// Service class
+ /** @hide */
public int mServiceClass;
// Number (it will not include the "sip" or "tel" URI scheme)
+ /** @hide */
public String mNumber;
// No reply timer for CF
+ /** @hide */
public int mTimeSeconds;
+ /** @hide */
public ImsCallForwardInfo() {
}
+ /** @hide */
public ImsCallForwardInfo(Parcel in) {
readFromParcel(in);
}
@@ -91,4 +101,28 @@
return new ImsCallForwardInfo[size];
}
};
+
+ public int getCondition() {
+ return mCondition;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public int getToA() {
+ return mToA;
+ }
+
+ public int getServiceClass() {
+ return mServiceClass;
+ }
+
+ public String getNumber() {
+ return mNumber;
+ }
+
+ public int getTimeSeconds() {
+ return mTimeSeconds;
+ }
}
diff --git a/telephony/java/com/android/ims/ImsCallProfile.aidl b/telephony/java/android/telephony/ims/ImsCallProfile.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsCallProfile.aidl
rename to telephony/java/android/telephony/ims/ImsCallProfile.aidl
index a356d13..e24e145 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.aidl
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsCallProfile;
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
similarity index 89%
rename from telephony/java/com/android/ims/ImsCallProfile.java
rename to telephony/java/android/telephony/ims/ImsCallProfile.java
index 489c208..27e5f94 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -11,11 +11,12 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,7 +33,8 @@
*
* @hide
*/
-public class ImsCallProfile implements Parcelable {
+@SystemApi
+public final class ImsCallProfile implements Parcelable {
private static final String TAG = "ImsCallProfile";
/**
@@ -110,52 +112,92 @@
* the video during voice call.
* conference_avail : Indicates if the session can be extended to the conference.
*/
+ /**
+ * @hide
+ */
public static final String EXTRA_CONFERENCE = "conference";
+ /**
+ * @hide
+ */
public static final String EXTRA_E_CALL = "e_call";
+ /**
+ * @hide
+ */
public static final String EXTRA_VMS = "vms";
+ /**
+ * @hide
+ */
public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
+ /**
+ * @hide
+ */
public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";
// Extra string for internal use only. OEMs should not use
// this for packing extras.
+ /**
+ * @hide
+ */
public static final String EXTRA_OEM_EXTRAS = "OemCallExtras";
/**
- * Integer extra properties
- * oir : Rule for originating identity (number) presentation, MO/MT.
+ * Rule for originating identity (number) presentation, MO/MT.
* {@link ImsCallProfile#OIR_DEFAULT}
* {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
* {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
- * cnap : Rule for calling name presentation
+ */
+ public static final String EXTRA_OIR = "oir";
+ /**
+ * Rule for calling name presentation
* {@link ImsCallProfile#OIR_DEFAULT}
* {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
* {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
- * dialstring : To identify the Ims call type, MO
- * {@link ImsCallProfile#DIALSTRING_NORMAL_CALL}
+ */
+ public static final String EXTRA_CNAP = "cnap";
+ /**
+ * To identify the Ims call type, MO
+ * {@link ImsCallProfile#DIALSTRING_NORMAL}
* {@link ImsCallProfile#DIALSTRING_SS_CONF}
* {@link ImsCallProfile#DIALSTRING_USSD}
*/
- public static final String EXTRA_OIR = "oir";
- public static final String EXTRA_CNAP = "cnap";
public static final String EXTRA_DIALSTRING = "dialstring";
/**
* Values for EXTRA_OIR / EXTRA_CNAP
*/
+ /**
+ * Default presentation for Originating Identity.
+ */
public static final int OIR_DEFAULT = 0; // "user subscription default value"
+ /**
+ * Restricted presentation for Originating Identity.
+ */
public static final int OIR_PRESENTATION_RESTRICTED = 1;
+ /**
+ * Not restricted presentation for Originating Identity.
+ */
public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+ /**
+ * Presentation unknown for Originating Identity.
+ */
public static final int OIR_PRESENTATION_UNKNOWN = 3;
+ /**
+ * Payphone presentation for Originating Identity.
+ */
public static final int OIR_PRESENTATION_PAYPHONE = 4;
+ //Values for EXTRA_DIALSTRING
/**
- * Values for EXTRA_DIALSTRING
+ * A default or normal normal call.
*/
- // default (normal call)
public static final int DIALSTRING_NORMAL = 0;
- // Call for SIP-based user configuration
+ /**
+ * Call for SIP-based user configuration
+ */
public static final int DIALSTRING_SS_CONF = 1;
- // Call for USSD message
+ /**
+ * Call for USSD message
+ */
public static final int DIALSTRING_USSD = 2;
/**
@@ -215,8 +257,11 @@
*/
public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
+ /** @hide */
public int mServiceType;
+ /** @hide */
public int mCallType;
+ /** @hide */
public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
/**
@@ -241,13 +286,17 @@
* Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across
* a {@link android.os.Binder}.
*/
+ /** @hide */
public Bundle mCallExtras;
+ /** @hide */
public ImsStreamMediaProfile mMediaProfile;
+ /** @hide */
public ImsCallProfile(Parcel in) {
readFromParcel(in);
}
+ /** @hide */
public ImsCallProfile() {
mServiceType = SERVICE_TYPE_NORMAL;
mCallType = CALL_TYPE_VOICE_N_VIDEO;
@@ -255,6 +304,7 @@
mMediaProfile = new ImsStreamMediaProfile();
}
+ /** @hide */
public ImsCallProfile(int serviceType, int callType) {
mServiceType = serviceType;
mCallType = callType;
@@ -351,7 +401,7 @@
mServiceType = in.readInt();
mCallType = in.readInt();
mCallExtras = in.readBundle();
- mMediaProfile = in.readParcelable(null);
+ mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
}
public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -366,8 +416,28 @@
}
};
+ public int getServiceType() {
+ return mServiceType;
+ }
+
+ public int getCallType() {
+ return mCallType;
+ }
+
+ public int getRestrictCause() {
+ return mRestrictCause;
+ }
+
+ public Bundle getCallExtras() {
+ return mCallExtras;
+ }
+
+ public ImsStreamMediaProfile getMediaProfile() {
+ return mMediaProfile;
+ }
+
/**
- * Converts from the call types defined in {@link com.android.ims.ImsCallProfile} to the
+ * Converts from the call types defined in {@link ImsCallProfile} to the
* video state values defined in {@link VideoProfile}.
*
* @param callProfile The call profile.
@@ -434,9 +504,9 @@
}
/**
- * Translate presentation value to OIR value
- * @param presentation
- * @return OIR valuse
+ * Badly named old method, kept for compatibility.
+ * See {@link #presentationToOir(int)}.
+ * @hide
*/
public static int presentationToOIR(int presentation) {
switch (presentation) {
@@ -454,9 +524,19 @@
}
/**
+ * Translate presentation value to OIR value
+ * @param presentation
+ * @return OIR values
+ */
+ public static int presentationToOir(int presentation) {
+ return presentationToOIR(presentation);
+ }
+
+ /**
* Translate OIR value to presentation value
* @param oir value
* @return presentation value
+ * @hide
*/
public static int OIRToPresentation(int oir) {
switch(oir) {
diff --git a/telephony/java/com/android/ims/internal/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
similarity index 90%
rename from telephony/java/com/android/ims/internal/ImsCallSession.java
rename to telephony/java/android/telephony/ims/ImsCallSession.java
index 1736b80..da32211 100644
--- a/telephony/java/com/android/ims/internal/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -11,23 +11,25 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims.internal;
+package android.telephony.ims;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Message;
import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
import java.util.Objects;
+import java.util.concurrent.Executor;
-import android.telephony.ims.stub.ImsCallSessionListenerImplBase;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.util.Log;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
/**
* Provides the call initiation/termination, and media exchange between two IMS endpoints.
@@ -39,7 +41,8 @@
private static final String TAG = "ImsCallSession";
/**
- * Defines IMS call session state.
+ * Defines IMS call session state. Please use {@link ImsCallSessionImplBase.State} definition.
+ * This is kept around for capability reasons.
*/
public static class State {
public static final int IDLE = 0;
@@ -92,6 +95,7 @@
* Listener for events relating to an IMS session, such as when a session is being
* recieved ("on ringing") or a call is outgoing ("on calling").
* <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+ * @hide
*/
public static class Listener {
/**
@@ -449,6 +453,7 @@
private boolean mClosed = false;
private Listener mListener;
+ /** @hide */
public ImsCallSession(IImsCallSession iSession) {
miSession = iSession;
@@ -462,6 +467,7 @@
}
}
+ /** @hide */
public ImsCallSession(IImsCallSession iSession, Listener listener) {
this(iSession);
setListener(listener);
@@ -470,15 +476,17 @@
/**
* Closes this object. This object is not usable after being closed.
*/
- public synchronized void close() {
- if (mClosed) {
- return;
- }
+ public void close() {
+ synchronized (this) {
+ if (mClosed) {
+ return;
+ }
- try {
- miSession.close();
- mClosed = true;
- } catch (RemoteException e) {
+ try {
+ miSession.close();
+ mClosed = true;
+ } catch (RemoteException e) {
+ }
}
}
@@ -554,6 +562,7 @@
* Gets the video call provider for the session.
*
* @return The video call provider.
+ * @hide
*/
public IImsVideoCallProvider getVideoCallProvider() {
if (mClosed) {
@@ -659,6 +668,7 @@
* override the previous listener.
*
* @param listener to listen to the session events of this object
+ * @hide
*/
public void setListener(Listener listener) {
mListener = listener;
@@ -743,6 +753,22 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param number number to be deflected to
+ */
+ public void deflect(String number) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.deflect(number);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
@@ -987,7 +1013,6 @@
* Sends Rtt Message
*
* @param rttMessage rtt text to be sent
- * @throws ImsException if call is absent
*/
public void sendRttMessage(String rttMessage) {
if (mClosed) {
@@ -1004,7 +1029,6 @@
* Sends RTT Upgrade request
*
* @param to : expected profile
- * @throws CallStateException
*/
public void sendRttModifyRequest(ImsCallProfile to) {
if (mClosed) {
@@ -1021,7 +1045,6 @@
* Sends RTT Upgrade response
*
* @param response : response for upgrade
- * @throws CallStateException
*/
public void sendRttModifyResponse(boolean response) {
if (mClosed) {
@@ -1040,37 +1063,33 @@
* the application is notified by having one of the methods called on
* the {@link IImsCallSessionListener}.
*/
- private class IImsCallSessionListenerProxy extends ImsCallSessionListenerImplBase {
+ private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub {
/**
* Notifies the result of the basic session operation (setup / terminate).
*/
@Override
- public void callSessionProgressing(IImsCallSession session,
- ImsStreamMediaProfile profile) {
+ public void callSessionProgressing(ImsStreamMediaProfile profile) {
if (mListener != null) {
mListener.callSessionProgressing(ImsCallSession.this, profile);
}
}
@Override
- public void callSessionStarted(IImsCallSession session,
- ImsCallProfile profile) {
+ public void callSessionInitiated(ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionStarted(ImsCallSession.this, profile);
}
}
@Override
- public void callSessionStartFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
}
}
@Override
- public void callSessionTerminated(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionTerminated(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
}
@@ -1080,48 +1099,42 @@
* Notifies the result of the call hold/resume operation.
*/
@Override
- public void callSessionHeld(IImsCallSession session,
- ImsCallProfile profile) {
+ public void callSessionHeld(ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionHeld(ImsCallSession.this, profile);
}
}
@Override
- public void callSessionHoldFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
}
}
@Override
- public void callSessionHoldReceived(IImsCallSession session,
- ImsCallProfile profile) {
+ public void callSessionHoldReceived(ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionHoldReceived(ImsCallSession.this, profile);
}
}
@Override
- public void callSessionResumed(IImsCallSession session,
- ImsCallProfile profile) {
+ public void callSessionResumed(ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionResumed(ImsCallSession.this, profile);
}
}
@Override
- public void callSessionResumeFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
}
}
@Override
- public void callSessionResumeReceived(IImsCallSession session,
- ImsCallProfile profile) {
+ public void callSessionResumeReceived(ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionResumeReceived(ImsCallSession.this, profile);
}
@@ -1130,13 +1143,11 @@
/**
* Notifies the start of a call merge operation.
*
- * @param session The call session.
* @param newSession The merged call session.
* @param profile The call profile.
*/
@Override
- public void callSessionMergeStarted(IImsCallSession session,
- IImsCallSession newSession, ImsCallProfile profile) {
+ public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile) {
// This callback can be used for future use to add additional
// functionality that may be needed between conference start and complete
Log.d(TAG, "callSessionMergeStarted");
@@ -1173,12 +1184,10 @@
/**
* Notifies of a failure to perform a call merge operation.
*
- * @param session The call session.
* @param reasonInfo The merge failure reason.
*/
@Override
- public void callSessionMergeFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
}
@@ -1188,24 +1197,21 @@
* Notifies the result of call upgrade / downgrade or any other call updates.
*/
@Override
- public void callSessionUpdated(IImsCallSession session,
- ImsCallProfile profile) {
+ public void callSessionUpdated(ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionUpdated(ImsCallSession.this, profile);
}
}
@Override
- public void callSessionUpdateFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
}
}
@Override
- public void callSessionUpdateReceived(IImsCallSession session,
- ImsCallProfile profile) {
+ public void callSessionUpdateReceived(ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
}
@@ -1215,8 +1221,8 @@
* Notifies the result of conference extension.
*/
@Override
- public void callSessionConferenceExtended(IImsCallSession session,
- IImsCallSession newSession, ImsCallProfile profile) {
+ public void callSessionConferenceExtended(IImsCallSession newSession,
+ ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionConferenceExtended(ImsCallSession.this,
new ImsCallSession(newSession), profile);
@@ -1224,16 +1230,15 @@
}
@Override
- public void callSessionConferenceExtendFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
}
}
@Override
- public void callSessionConferenceExtendReceived(IImsCallSession session,
- IImsCallSession newSession, ImsCallProfile profile) {
+ public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+ ImsCallProfile profile) {
if (mListener != null) {
mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
new ImsCallSession(newSession), profile);
@@ -1245,15 +1250,14 @@
* the conference session.
*/
@Override
- public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+ public void callSessionInviteParticipantsRequestDelivered() {
if (mListener != null) {
mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
}
}
@Override
- public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
reasonInfo);
@@ -1261,15 +1265,14 @@
}
@Override
- public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+ public void callSessionRemoveParticipantsRequestDelivered() {
if (mListener != null) {
mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
}
}
@Override
- public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
+ public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
reasonInfo);
@@ -1280,8 +1283,7 @@
* Notifies the changes of the conference info. in the conference session.
*/
@Override
- public void callSessionConferenceStateUpdated(IImsCallSession session,
- ImsConferenceState state) {
+ public void callSessionConferenceStateUpdated(ImsConferenceState state) {
if (mListener != null) {
mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
}
@@ -1291,17 +1293,15 @@
* Notifies the incoming USSD message.
*/
@Override
- public void callSessionUssdMessageReceived(IImsCallSession session,
- int mode, String ussdMessage) {
+ public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
if (mListener != null) {
mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
}
}
/**
- * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may
+ * Notifies of a case where a {@link ImsCallSession} may
* potentially handover from one radio technology to another.
- * @param session
* @param srcAccessTech The source radio access technology; one of the access technology
* constants defined in {@link android.telephony.ServiceState}. For
* example
@@ -1312,8 +1312,7 @@
* {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
*/
@Override
- public void callSessionMayHandover(IImsCallSession session,
- int srcAccessTech, int targetAccessTech) {
+ public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
if (mListener != null) {
mListener.callSessionMayHandover(ImsCallSession.this, srcAccessTech,
targetAccessTech);
@@ -1324,9 +1323,8 @@
* Notifies of handover information for this call
*/
@Override
- public void callSessionHandover(IImsCallSession session,
- int srcAccessTech, int targetAccessTech,
- ImsReasonInfo reasonInfo) {
+ public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
targetAccessTech, reasonInfo);
@@ -1337,9 +1335,8 @@
* Notifies of handover failure info for this call
*/
@Override
- public void callSessionHandoverFailed(IImsCallSession session,
- int srcAccessTech, int targetAccessTech,
- ImsReasonInfo reasonInfo) {
+ public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
if (mListener != null) {
mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
targetAccessTech, reasonInfo);
@@ -1350,8 +1347,7 @@
* Notifies the TTY mode received from remote party.
*/
@Override
- public void callSessionTtyModeReceived(IImsCallSession session,
- int mode) {
+ public void callSessionTtyModeReceived(int mode) {
if (mListener != null) {
mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
}
@@ -1360,21 +1356,17 @@
/**
* Notifies of a change to the multiparty state for this {@code ImsCallSession}.
*
- * @param session The call session.
* @param isMultiParty {@code true} if the session became multiparty, {@code false}
* otherwise.
*/
- public void callSessionMultipartyStateChanged(IImsCallSession session,
- boolean isMultiParty) {
-
+ public void callSessionMultipartyStateChanged(boolean isMultiParty) {
if (mListener != null) {
mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
}
}
@Override
- public void callSessionSuppServiceReceived(IImsCallSession session,
- ImsSuppServiceNotification suppServiceInfo ) {
+ public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
if (mListener != null) {
mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
}
@@ -1384,8 +1376,7 @@
* Received RTT modify request from remote party
*/
@Override
- public void callSessionRttModifyRequestReceived(IImsCallSession session,
- ImsCallProfile callProfile) {
+ public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
if (mListener != null) {
mListener.callSessionRttModifyRequestReceived(ImsCallSession.this, callProfile);
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
new file mode 100644
index 0000000..a7f124a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -0,0 +1,603 @@
+/*
+ * 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.SystemApi;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * Listener interface for notifying the Framework's {@link ImsCallSession} for updates to an ongoing
+ * IMS call.
+ *
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this implementation or you
+// will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+// TODO: APIs in here do not conform to API guidelines yet. This can be changed if
+// ImsCallSessionListenerConverter is also changed.
+@SystemApi
+public class ImsCallSessionListener {
+
+ private final IImsCallSessionListener mListener;
+
+ /** @hide */
+ public ImsCallSessionListener(IImsCallSessionListener l) {
+ mListener = l;
+ }
+
+ /**
+ * A request has been sent out to initiate a new IMS call session and a 1xx response has been
+ * received from the network.
+ */
+ public void callSessionProgressing(ImsStreamMediaProfile profile) {
+ try {
+ mListener.callSessionProgressing(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session has been initiated.
+ *
+ * @param profile the associated {@link ImsCallProfile}.
+ */
+ public void callSessionInitiated(ImsCallProfile profile) {
+ try {
+ mListener.callSessionInitiated(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session establishment has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+ * establishment failure.
+ */
+ public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionInitiatedFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session has been terminated.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session termination.
+ */
+ public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionTerminated(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session has started the process of holding the call. If it fails,
+ * {@link #callSessionHoldFailed(ImsReasonInfo)} should be called.
+ *
+ * If the IMS call session is resumed, call {@link #callSessionResumed(ImsCallProfile)}.
+ *
+ * @param profile The associated {@link ImsCallProfile} of the call session that has been put
+ * on hold.
+ */
+ public void callSessionHeld(ImsCallProfile profile) {
+ try {
+ mListener.callSessionHeld(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session has failed to be held.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session hold failure.
+ */
+ public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionHoldFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * This IMS Call session has been put on hold by the remote party.
+ *
+ * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+ */
+ public void callSessionHoldReceived(ImsCallProfile profile) {
+ try {
+ mListener.callSessionHoldReceived(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session has started the process of resuming the call. If the process of resuming
+ * the call fails, call {@link #callSessionResumeFailed(ImsReasonInfo)}.
+ *
+ * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+ */
+ public void callSessionResumed(ImsCallProfile profile) {
+ try {
+ mListener.callSessionResumed(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session resume has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the session resume
+ * failure.
+ */
+ public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionResumeFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The remote party has resumed this IMS call session.
+ *
+ * @param profile {@link ImsCallProfile} associated with the IMS call session.
+ */
+ public void callSessionResumeReceived(ImsCallProfile profile) {
+ try {
+ mListener.callSessionResumeReceived(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session merge has been started. At this point, the {@code newSession}
+ * represents the IMS call session which represents the new merged conference and has been
+ * initiated to the IMS conference server.
+ *
+ * @param newSession the {@link ImsCallSessionImplBase} that represents the merged active & held
+ * sessions.
+ * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+ */
+ public void callSessionMergeStarted(ImsCallSessionImplBase newSession, ImsCallProfile profile)
+ {
+ try {
+ mListener.callSessionMergeStarted(newSession != null ?
+ newSession.getServiceImpl() : null, profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Compatibility method for older implementations.
+ * See {@link #callSessionMergeStarted(ImsCallSessionImplBase, ImsCallProfile)}.
+ *
+ * @hide
+ */
+ public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile)
+ {
+ try {
+ mListener.callSessionMergeStarted(newSession, profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The session merge is successful and the merged {@link ImsCallSession} is active.
+ *
+ * @param newSession the new {@link ImsCallSessionImplBase}
+ * that represents the conference IMS call
+ * session.
+ */
+ public void callSessionMergeComplete(ImsCallSessionImplBase newSession) {
+ try {
+ mListener.callSessionMergeComplete(newSession != null ?
+ newSession.getServiceImpl() : null);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Compatibility method for older implementations of ImsService.
+ *
+ * See {@link #callSessionMergeComplete(ImsCallSessionImplBase)}}.
+ *
+ * @hide
+ */
+ public void callSessionMergeComplete(IImsCallSession newSession) {
+ try {
+ mListener.callSessionMergeComplete(newSession);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session merge has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} contining the reason for the call merge failure.
+ */
+ public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionMergeFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session profile has been updated. Does not include holding or resuming a call.
+ *
+ * @param profile The {@link ImsCallProfile} associated with the updated IMS call session.
+ */
+ public void callSessionUpdated(ImsCallProfile profile) {
+ try {
+ mListener.callSessionUpdated(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session profile update has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} containing a reason for the session update failure.
+ */
+ public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionUpdateFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session profile has received an update from the remote user.
+ *
+ * @param profile The new {@link ImsCallProfile} associated with the update.
+ */
+ public void callSessionUpdateReceived(ImsCallProfile profile) {
+ try {
+ mListener.callSessionUpdateReceived(profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Called when the session has been extended to a conference session.
+ *
+ * If the conference extension fails, call
+ * {@link #callSessionConferenceExtendFailed(ImsReasonInfo)}.
+ *
+ * @param newSession the session object that is extended to the conference from the active
+ * IMS Call session.
+ * @param profile The {@link ImsCallProfile} associated with the IMS call session.
+ */
+ public void callSessionConferenceExtended(ImsCallSessionImplBase newSession,
+ ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtended(
+ newSession != null ? newSession.getServiceImpl() : null, profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Compatibility method to interface with older versions of ImsService.
+ * See {@link #callSessionConferenceExtended(ImsCallSessionImplBase, ImsCallProfile)}.
+ *
+ * @hide
+ */
+ public void callSessionConferenceExtended(IImsCallSession newSession, ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtended(newSession, profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The previous conference extension has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the conference
+ * extension failure.
+ */
+ public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionConferenceExtendFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * A conference extension has been received received from the remote party.
+ *
+ * @param newSession An {@link ImsCallSessionImplBase}
+ * representing the extended IMS call session.
+ * @param profile The {@link ImsCallProfile} associated with the new IMS call session.
+ */
+ public void callSessionConferenceExtendReceived(ImsCallSessionImplBase newSession,
+ ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtendReceived(newSession != null
+ ? newSession.getServiceImpl() : null, profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Compatibility method to interface with older versions of ImsService.
+ * See {@link #callSessionConferenceExtendReceived(ImsCallSessionImplBase, ImsCallProfile)}.
+ *
+ * @hide
+ */
+ public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+ ImsCallProfile profile) {
+ try {
+ mListener.callSessionConferenceExtendReceived(newSession, profile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The request to invite participants to the conference has been delivered to the conference
+ * server.
+ */
+ public void callSessionInviteParticipantsRequestDelivered() {
+ try {
+ mListener.callSessionInviteParticipantsRequestDelivered();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The previous request to invite participants to the conference (see
+ * {@link #callSessionInviteParticipantsRequestDelivered()}) has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason forthe conference invitation
+ * failure.
+ */
+ public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+ {
+ try {
+ mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The request to remove participants from the conference has been delivered to the conference
+ * server.
+ */
+ public void callSessionRemoveParticipantsRequestDelivered() {
+ try {
+ mListener.callSessionRemoveParticipantsRequestDelivered();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The previous request to remove participants from the conference (see
+ * {@link #callSessionRemoveParticipantsRequestDelivered()}) has failed.
+ *
+ * @param reasonInfo {@link ImsReasonInfo} detailing the reason for the conference removal
+ * failure.
+ */
+ public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+ {
+ try {
+ mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session's conference state has changed.
+ *
+ * @param state The new {@link ImsConferenceState} associated with the conference.
+ */
+ public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+ try {
+ mListener.callSessionConferenceStateUpdated(state);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session has received a Ussd message.
+ *
+ * @param mode The mode of the USSD message, either
+ * {@link ImsCallSessionImplBase#USSD_MODE_NOTIFY} or
+ * {@link ImsCallSessionImplBase#USSD_MODE_REQUEST}.
+ * @param ussdMessage The USSD message.
+ */
+ public void callSessionUssdMessageReceived(int mode, String ussdMessage)
+ {
+ try {
+ mListener.callSessionUssdMessageReceived(mode, ussdMessage);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * An {@link ImsCallSession} may potentially handover from one radio
+ * technology to another.
+ *
+ * @param srcAccessTech The source radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For example
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ * @param targetAccessTech The target radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For example
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ */
+ public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
+ {
+ try {
+ mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session's access technology has changed.
+ *
+ * @param srcAccessTech original access technology, defined in
+ * {@link android.telephony.ServiceState}.
+ * @param targetAccessTech new access technology, defined in
+ * {@link android.telephony.ServiceState}.
+ * @param reasonInfo The {@link ImsReasonInfo} associated with this handover.
+ */
+ public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The IMS call session's access technology change has failed..
+ *
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+ */
+ public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ try {
+ mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The TTY mode has been changed by the remote party.
+ *
+ * @param mode one of the following: -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ */
+ public void callSessionTtyModeReceived(int mode) {
+ try {
+ mListener.callSessionTtyModeReceived(mode);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * The multiparty state has been changed for this {@code ImsCallSession}.
+ *
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+ */
+ public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+ try {
+ mListener.callSessionMultipartyStateChanged(isMultiParty);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Supplementary service information has been received for the current IMS call session.
+ *
+ * @param suppSrvNotification The {@link ImsSuppServiceNotification} containing the change.
+ */
+ public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
+ {
+ try {
+ mListener.callSessionSuppServiceReceived(suppSrvNotification);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * An RTT modify request has been received from the remote party.
+ *
+ * @param callProfile An {@link ImsCallProfile} with the updated attributes
+ */
+ public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
+ {
+ try {
+ mListener.callSessionRttModifyRequestReceived(callProfile);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * An RTT modify response has been received.
+ *
+ * @param status the received response for RTT modify request.
+ */
+ public void callSessionRttModifyResponseReceived(int status) {
+ try {
+ mListener.callSessionRttModifyResponseReceived(status);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * An RTT message has been received from the remote party.
+ *
+ * @param rttMessage The RTT message that has been received.
+ */
+ public void callSessionRttMessageReceived(String rttMessage) {
+ try {
+ mListener.callSessionRttMessageReceived(rttMessage);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
+
diff --git a/telephony/java/com/android/ims/ImsConferenceState.aidl b/telephony/java/android/telephony/ims/ImsConferenceState.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsConferenceState.aidl
rename to telephony/java/android/telephony/ims/ImsConferenceState.aidl
index 2fc029f..e2b371c 100644
--- a/telephony/java/com/android/ims/ImsConferenceState.aidl
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsConferenceState;
diff --git a/telephony/java/com/android/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java
similarity index 95%
rename from telephony/java/com/android/ims/ImsConferenceState.java
rename to telephony/java/android/telephony/ims/ImsConferenceState.java
index 0afde88..66d2f8d 100644
--- a/telephony/java/com/android/ims/ImsConferenceState.java
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -11,16 +11,17 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,7 +33,8 @@
*
* @hide
*/
-public class ImsConferenceState implements Parcelable {
+@SystemApi
+public final class ImsConferenceState implements Parcelable {
/**
* conference-info : user
*/
@@ -87,12 +89,13 @@
*/
public static final String SIP_STATUS_CODE = "sipstatuscode";
- public HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
+ public final HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
+ /** @hide */
public ImsConferenceState() {
}
- public ImsConferenceState(Parcel in) {
+ private ImsConferenceState(Parcel in) {
readFromParcel(in);
}
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.aidl b/telephony/java/android/telephony/ims/ImsExternalCallState.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsExternalCallState.aidl
rename to telephony/java/android/telephony/ims/ImsExternalCallState.aidl
index c208702..99d29356 100644
--- a/telephony/java/com/android/ims/ImsExternalCallState.aidl
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsExternalCallState;
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
similarity index 92%
rename from telephony/java/com/android/ims/ImsExternalCallState.java
rename to telephony/java/android/telephony/ims/ImsExternalCallState.java
index da26073..e82c115 100644
--- a/telephony/java/com/android/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016 The Android Open Source Project
+ * 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.
@@ -11,11 +11,12 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,7 +33,8 @@
* Parcelable object to handle MultiEndpoint Dialog Information
* @hide
*/
-public class ImsExternalCallState implements Parcelable {
+@SystemApi
+public final class ImsExternalCallState implements Parcelable {
private static final String TAG = "ImsExternalCallState";
@@ -50,9 +52,11 @@
private int mCallType;
private boolean mIsHeld;
+ /** @hide */
public ImsExternalCallState() {
}
+ /** @hide */
public ImsExternalCallState(int callId, Uri address, boolean isPullable, int callState,
int callType, boolean isCallheld) {
mCallId = callId;
@@ -64,6 +68,7 @@
Rlog.d(TAG, "ImsExternalCallState = " + this);
}
+ /** @hide */
public ImsExternalCallState(Parcel in) {
mCallId = in.readInt();
ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.aidl b/telephony/java/android/telephony/ims/ImsReasonInfo.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsReasonInfo.aidl
rename to telephony/java/android/telephony/ims/ImsReasonInfo.aidl
index 17e6d3a..604b323 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.aidl
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsReasonInfo;
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
similarity index 95%
rename from telephony/java/com/android/ims/ImsReasonInfo.java
rename to telephony/java/android/telephony/ims/ImsReasonInfo.java
index 4f6f68c..7d65430 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -11,11 +11,12 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,7 +25,8 @@
*
* @hide
*/
-public class ImsReasonInfo implements Parcelable {
+@SystemApi
+public final class ImsReasonInfo implements Parcelable {
/**
* Specific code of each types
@@ -384,6 +386,19 @@
/** Call/IMS registration is failed/dropped because of a network detach */
public static final int CODE_NETWORK_DETACH = 1513;
+ /**
+ * Call failed due to SIP code 380 (Alternative Service response) while dialing an "undetected
+ * emergency number". This scenario is important in some regions where the carrier network will
+ * identify other non-emergency help numbers (e.g. mountain rescue) when attempting to dial.
+ */
+ public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514;
+
+ /**
+ * Call failed because of unobtainable number
+ * @hide
+ */
+ public static final int CODE_UNOBTAINABLE_NUMBER = 1515;
+
/* OEM specific error codes. To be used by OEMs when they don't want to
reveal error code which would be replaced by ERROR_UNSPECIFIED */
public static final int CODE_OEM_CAUSE_1 = 0xf001;
@@ -411,27 +426,36 @@
// For main reason code
+ /** @hide */
public int mCode;
// For the extra code value; it depends on the code value.
+ /** @hide */
public int mExtraCode;
// For the additional message of the reason info.
+ /** @hide */
public String mExtraMessage;
+
+ /** @hide */
public ImsReasonInfo() {
mCode = CODE_UNSPECIFIED;
mExtraCode = CODE_UNSPECIFIED;
mExtraMessage = null;
}
- public ImsReasonInfo(Parcel in) {
- readFromParcel(in);
+ private ImsReasonInfo(Parcel in) {
+ mCode = in.readInt();
+ mExtraCode = in.readInt();
+ mExtraMessage = in.readString();
}
+ /** @hide */
public ImsReasonInfo(int code, int extraCode) {
mCode = code;
mExtraCode = extraCode;
mExtraMessage = null;
}
+ /** @hide */
public ImsReasonInfo(int code, int extraCode, String extraMessage) {
mCode = code;
mExtraCode = extraCode;
@@ -480,12 +504,6 @@
out.writeString(mExtraMessage);
}
- private void readFromParcel(Parcel in) {
- mCode = in.readInt();
- mExtraCode = in.readInt();
- mExtraMessage = in.readString();
- }
-
public static final Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
@Override
public ImsReasonInfo createFromParcel(Parcel in) {
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8230eaf..2748cb5 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,23 +16,28 @@
package android.telephony.ims;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
import android.telephony.ims.feature.ImsFeature;
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsServiceController;
import com.android.internal.annotations.VisibleForTesting;
import static android.Manifest.permission.MODIFY_PHONE_STATE;
@@ -46,9 +51,7 @@
* ...
* <service android:name=".EgImsService"
* android:permission="android.permission.BIND_IMS_SERVICE" >
- * <!-- Apps must declare which features they support as metadata. The different categories are
- * defined below. In this example, the RCS_FEATURE feature is supported. -->
- * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ * ...
* <intent-filter>
* <action android:name="android.telephony.ims.ImsService" />
* </intent-filter>
@@ -62,13 +65,31 @@
* 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
* {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
*
+ * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
+ * supports, dynamic or static definitions.
+ *
+ * In the static definition, the {@link ImsFeature}s that are supported are defined in the service
+ * definition of the AndroidManifest.xml file as metadata:
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ * defined below. In this example, the MMTEL_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" />
+ *
* The features that are currently supported in an ImsService are:
* - RCS_FEATURE: This ImsService implements the RcsFeature class.
- * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
- * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
- * available to place emergency calls at all times. This MUST be implemented by the default
- * ImsService provided in the device overlay.
- * @hide
+ * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be
+ * declared along with the MMTEL_FEATURE. If this is not specified, the framework will use
+ * circuit switch for emergency calling.
+ *
+ * In the dynamic definition, the supported features are not specified in the service definition
+ * of the AndroidManifest. Instead, the framework binds to this service and calls
+ * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an
+ * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported
+ * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes,
+ * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the
+ * framework of the changes.
+ *
+ * @hide
*/
@SystemApi
public class ImsService extends Service {
@@ -87,20 +108,36 @@
// call ImsFeature#onFeatureRemoved.
private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+ private IImsServiceControllerListener mListener;
+
+
+ /**
+ * Listener that notifies the framework of ImsService changes.
+ * @hide
+ */
+ public static class Listener extends IImsServiceControllerListener.Stub {
+ /**
+ * The IMS features that this ImsService supports has changed.
+ * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
+ * that this ImsService supports. This may trigger the addition/removal of feature
+ * in this service.
+ */
+ public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+ }
+ }
+
/**
* @hide
*/
protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
-
@Override
- public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
- IImsFeatureStatusCallback c) {
- return createEmergencyMMTelFeatureInternal(slotId, c);
+ public void setListener(IImsServiceControllerListener l) {
+ mListener = l;
}
@Override
- public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
- return createMMTelFeatureInternal(slotId, c);
+ public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createMmTelFeatureInternal(slotId, c);
}
@Override
@@ -109,10 +146,46 @@
}
@Override
- public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
- throws RemoteException {
+ public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) {
ImsService.this.removeImsFeature(slotId, featureType, c);
}
+
+ @Override
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
+ return ImsService.this.querySupportedImsFeatures();
+ }
+
+ @Override
+ public void notifyImsServiceReadyForFeatureCreation() {
+ ImsService.this.readyForFeatureCreation();
+ }
+
+ @Override
+ public void notifyImsFeatureReady(int slotId, int featureType) {
+ ImsService.this.notifyImsFeatureReady(slotId, featureType);
+ }
+
+ @Override
+ public IImsConfig getConfig(int slotId) {
+ ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+ return c != null ? c.getIImsConfig() : null;
+ }
+
+ @Override
+ public IImsRegistration getRegistration(int slotId) {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ return r != null ? r.getBinder() : null;
+ }
+
+ @Override
+ public void enableIms(int slotId) {
+ ImsService.this.enableIms(slotId);
+ }
+
+ @Override
+ public void disableIms(int slotId) {
+ ImsService.this.disableIms(slotId);
+ }
};
/**
@@ -135,44 +208,34 @@
return mFeaturesBySlot.get(slotId);
}
- private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+ private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
IImsFeatureStatusCallback c) {
- MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+ MmTelFeature f = createMmTelFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+ setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
return f.getBinder();
} else {
- return null;
- }
- }
-
- private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
- MMTelFeature f = onCreateMMTelImsFeature(slotId);
- if (f != null) {
- setupFeature(f, slotId, ImsFeature.MMTEL, c);
- return f.getBinder();
- } else {
+ Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
return null;
}
}
private IImsRcsFeature createRcsFeatureInternal(int slotId,
IImsFeatureStatusCallback c) {
- RcsFeature f = onCreateRcsFeature(slotId);
+ RcsFeature f = createRcsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.RCS, c);
+ setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
return f.getBinder();
} else {
+ Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
return null;
}
}
private void setupFeature(ImsFeature f, int slotId, int featureType,
IImsFeatureStatusCallback c) {
- f.setContext(this);
- f.setSlotId(slotId);
f.addImsFeatureStatusCallback(c);
+ f.initialize(this, slotId);
addImsFeature(slotId, featureType, f);
}
@@ -211,29 +274,122 @@
}
}
+ private void notifyImsFeatureReady(int slotId, int featureType) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
+ "slot " + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f == null) {
+ Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
+ + featureType + " exists on slot " + slotId);
+ return;
+ }
+ f.onFeatureReady();
+ }
+ }
+
/**
- * @return An implementation of MMTelFeature that will be used by the system for MMTel
- * functionality. Must be able to handle emergency calls at any time as well.
- * @hide
+ * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
+ * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
+ * correspond to the {@link ImsFeature}s configured here.
+ *
+ * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
+ * {@link ImsFeature}s.
+ *
+ * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
*/
- public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
+ // Return empty for base implementation
+ return new ImsFeatureConfiguration();
+ }
+
+ /**
+ * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
+ * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
+ * new ImsFeatures, depending on the configuration.
+ */
+ public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
+ throws RemoteException {
+ if (mListener == null) {
+ throw new IllegalStateException("Framework is not ready");
+ }
+ mListener.onUpdateSupportedImsFeatures(c);
+ }
+
+ /**
+ * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
+ * the ImsService has registered for with the framework, either in the manifest or via
+ * {@link #querySupportedImsFeatures()}.
+ *
+ * The ImsService should use this signal instead of onCreate/onBind or similar to perform
+ * feature initialization because the framework may bind to this service multiple times to
+ * query the ImsService's {@link ImsFeatureConfiguration} via
+ * {@link #querySupportedImsFeatures()}before creating features.
+ */
+ public void readyForFeatureCreation() {
+ }
+
+ /**
+ * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
+ * and perform all appropriate initialization to bring up all ImsFeatures.
+ */
+ public void enableIms(int slotId) {
+ }
+
+ /**
+ * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
+ * and set capability status to false for all ImsFeatures.
+ */
+ public void disableIms(int slotId) {
+ }
+
+ /**
+ * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+ * specified slot.
+ *
+ * @param slotId The slot ID that the MMTEL Feature is being created for.
+ * @return The newly created {@link MmTelFeature} associated with the slot or null if the
+ * feature is not supported.
+ */
+ public MmTelFeature createMmTelFeature(int slotId) {
return null;
}
/**
- * @return An implementation of MMTelFeature that will be used by the system for MMTel
- * functionality.
- * @hide
+ * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+ * specified slot.
+ *
+ * @param slotId The slot ID that the RCS Feature is being created for.
+ * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
+ * is not supported.
*/
- public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+ public RcsFeature createRcsFeature(int slotId) {
return null;
}
/**
- * @return An implementation of RcsFeature that will be used by the system for RCS.
- * @hide
+ * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
+ * will be used by the platform to get/set specific IMS related configurations.
+ *
+ * @param slotId The slot that the IMS configuration is associated with.
+ * @return ImsConfig implementation that is associated with the specified slot.
*/
- public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
- return null;
+ public ImsConfigImplBase getConfig(int slotId) {
+ return new ImsConfigImplBase();
}
-}
+
+ /**
+ * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+ *
+ * @param slotId The slot that is associated with the IMS Registration.
+ * @return the ImsRegistration implementation associated with the slot.
+ */
+ public ImsRegistrationImplBase getRegistration(int slotId) {
+ return new ImsRegistrationImplBase();
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/ImsSsData.aidl b/telephony/java/android/telephony/ims/ImsSsData.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsSsData.aidl
rename to telephony/java/android/telephony/ims/ImsSsData.aidl
index 33f8306..eff3a6b 100644
--- a/telephony/java/com/android/ims/ImsSsData.aidl
+++ b/telephony/java/android/telephony/ims/ImsSsData.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsSsData;
diff --git a/telephony/java/com/android/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
similarity index 89%
rename from telephony/java/com/android/ims/ImsSsData.java
rename to telephony/java/android/telephony/ims/ImsSsData.java
index 7336c13..1ddf199 100644
--- a/telephony/java/com/android/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 The Android Open Source Project
+ * 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.
@@ -11,21 +11,21 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.ArrayList;
-
/**
* Provided STK Call Control Suplementary Service information
*
* {@hide}
*/
-public class ImsSsData implements Parcelable {
+@SystemApi
+public final class ImsSsData implements Parcelable {
//ServiceType
public static final int SS_CFU = 0;
@@ -68,30 +68,38 @@
public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
// Refer to ServiceType
+ /** @hide */
public int serviceType;
// Refere to SSRequestType
+ /** @hide */
public int requestType;
// Refer to TeleserviceType
+ /** @hide */
public int teleserviceType;
// Service Class
+ /** @hide */
public int serviceClass;
// Error information
+ /** @hide */
public int result;
+ /** @hide */
public int[] ssInfo; /* Valid for all supplementary services.
This field will be empty for RequestType SS_INTERROGATION
and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
SS_INCOMING_BARRING_ANONYMOUS.*/
+ /** @hide */
public ImsCallForwardInfo[] cfInfo; /* Valid only for supplementary services
ServiceType SS_CF_* and RequestType SS_INTERROGATION */
+ /** @hide */
public ImsSsInfo[] imsSsInfo; /* Valid only for ServiceType SS_INCOMING_BARRING_DN and
ServiceType SS_INCOMING_BARRING_ANONYMOUS */
public ImsSsData() {}
- public ImsSsData(Parcel in) {
+ private ImsSsData(Parcel in) {
readFromParcel(in);
}
@@ -133,20 +141,36 @@
return 0;
}
+ /**
+ * Old method, kept for compatibility. See {@link #isTypeCf()}
+ * @hide
+ */
public boolean isTypeCF() {
return (serviceType == SS_CFU || serviceType == SS_CF_BUSY ||
serviceType == SS_CF_NO_REPLY || serviceType == SS_CF_NOT_REACHABLE ||
serviceType == SS_CF_ALL || serviceType == SS_CF_ALL_CONDITIONAL);
}
+ public boolean isTypeCf() {
+ return isTypeCF();
+ }
+
public boolean isTypeUnConditional() {
return (serviceType == SS_CFU || serviceType == SS_CF_ALL);
}
+ /**
+ * Old method, kept for compatibility. See {@link #isTypeCf()}
+ * @hide
+ */
public boolean isTypeCW() {
return (serviceType == SS_WAIT);
}
+ public boolean isTypeCw() {
+ return isTypeCW();
+ }
+
public boolean isTypeClip() {
return (serviceType == SS_CLIP);
}
diff --git a/telephony/java/com/android/ims/ImsSsInfo.aidl b/telephony/java/android/telephony/ims/ImsSsInfo.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsSsInfo.aidl
rename to telephony/java/android/telephony/ims/ImsSsInfo.aidl
index 0ac598b..66d4950 100644
--- a/telephony/java/com/android/ims/ImsSsInfo.aidl
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsSsInfo;
diff --git a/telephony/java/com/android/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
similarity index 81%
rename from telephony/java/com/android/ims/ImsSsInfo.java
rename to telephony/java/android/telephony/ims/ImsSsInfo.java
index 7acc3bf..1d1292f 100644
--- a/telephony/java/com/android/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -11,11 +11,12 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,7 +25,8 @@
*
* @hide
*/
-public class ImsSsInfo implements Parcelable {
+@SystemApi
+public final class ImsSsInfo implements Parcelable {
/**
* For the status of service registration or activation/deactivation.
*/
@@ -33,13 +35,15 @@
public static final int ENABLED = 1;
// 0: disabled, 1: enabled
+ /** @hide */
public int mStatus;
+ /** @hide */
public String mIcbNum;
public ImsSsInfo() {
}
- public ImsSsInfo(Parcel in) {
+ private ImsSsInfo(Parcel in) {
readFromParcel(in);
}
@@ -76,4 +80,12 @@
return new ImsSsInfo[size];
}
};
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public String getIcbNum() {
+ return mIcbNum;
+ }
}
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
rename to telephony/java/android/telephony/ims/ImsStreamMediaProfile.aidl
index d648a35..ee321ae 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.aidl
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsStreamMediaProfile;
diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
similarity index 88%
rename from telephony/java/com/android/ims/ImsStreamMediaProfile.java
rename to telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index cfe37b5..243352b 100644
--- a/telephony/java/com/android/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Android Open Source Project
+ * 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.
@@ -11,11 +11,12 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,7 +26,8 @@
*
* @hide
*/
-public class ImsStreamMediaProfile implements Parcelable {
+@SystemApi
+public final class ImsStreamMediaProfile implements Parcelable {
private static final String TAG = "ImsStreamMediaProfile";
/**
@@ -79,18 +81,25 @@
public static final int RTT_MODE_FULL = 1;
// Audio related information
+ /** @hide */
public int mAudioQuality;
+ /** @hide */
public int mAudioDirection;
// Video related information
+ /** @hide */
public int mVideoQuality;
+ /** @hide */
public int mVideoDirection;
// Rtt related information
+ /** @hide */
public int mRttMode;
+ /** @hide */
public ImsStreamMediaProfile(Parcel in) {
readFromParcel(in);
}
+ /** @hide */
public ImsStreamMediaProfile() {
mAudioQuality = AUDIO_QUALITY_NONE;
mAudioDirection = DIRECTION_SEND_RECEIVE;
@@ -99,6 +108,7 @@
mRttMode = RTT_MODE_DISABLED;
}
+ /** @hide */
public ImsStreamMediaProfile(int audioQuality, int audioDirection,
int videoQuality, int videoDirection) {
mAudioQuality = audioQuality;
@@ -107,6 +117,7 @@
mVideoDirection = videoDirection;
}
+ /** @hide */
public ImsStreamMediaProfile(int rttMode) {
mRttMode = rttMode;
}
@@ -178,4 +189,23 @@
mRttMode = rttMode;
}
+ public int getAudioQuality() {
+ return mAudioQuality;
+ }
+
+ public int getAudioDirection() {
+ return mAudioDirection;
+ }
+
+ public int getVideoQuality() {
+ return mVideoQuality;
+ }
+
+ public int getVideoDirection() {
+ return mVideoDirection;
+ }
+
+ public int getRttMode() {
+ return mRttMode;
+ }
}
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.aidl
similarity index 95%
rename from telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
rename to telephony/java/android/telephony/ims/ImsSuppServiceNotification.aidl
index 6b4479f..0552780 100644
--- a/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
+++ b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.aidl
@@ -15,6 +15,6 @@
*/
-package com.android.ims;
+package android.telephony.ims;
parcelable ImsSuppServiceNotification;
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.java b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
similarity index 76%
rename from telephony/java/com/android/ims/ImsSuppServiceNotification.java
rename to telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
index faf7499..efaade8 100644
--- a/telephony/java/com/android/ims/ImsSuppServiceNotification.java
+++ b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015 The Android Open Source Project
+ * 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.
@@ -11,12 +11,13 @@
* 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.
+ * limitations under the License
*/
-package com.android.ims;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,27 +29,42 @@
*
* @hide
*/
-public class ImsSuppServiceNotification implements Parcelable {
+@SystemApi
+public final class ImsSuppServiceNotification implements Parcelable {
private static final String TAG = "ImsSuppServiceNotification";
/** Type of notification: 0 = MO; 1 = MT */
- public int notificationType;
+ public final int notificationType;
/** TS 27.007 7.17 "code1" or "code2" */
- public int code;
+ public final int code;
/** TS 27.007 7.17 "index" - Not used currently*/
- public int index;
+ public final int index;
/** TS 27.007 7.17 "type" (MT only) - Not used currently */
- public int type;
+ public final int type;
/** TS 27.007 7.17 "number" (MT only) */
- public String number;
+ public final String number;
/** List of forwarded numbers, if any */
- public String[] history;
+ public final String[] history;
- public ImsSuppServiceNotification() {
+
+ public ImsSuppServiceNotification(int notificationType, int code, int index, int type,
+ String number, String[] history) {
+ this.notificationType = notificationType;
+ this.code = code;
+ this.index = index;
+ this.type = type;
+ this.number = number;
+ this.history = history;
}
+ /** @hide */
public ImsSuppServiceNotification(Parcel in) {
- readFromParcel(in);
+ notificationType = in.readInt();
+ code = in.readInt();
+ index = in.readInt();
+ type = in.readInt();
+ number = in.readString();
+ history = in.createStringArray();
}
@Override
@@ -77,15 +93,6 @@
out.writeStringArray(history);
}
- private void readFromParcel(Parcel in) {
- notificationType = in.readInt();
- code = in.readInt();
- index = in.readInt();
- type = in.readInt();
- number = in.readString();
- history = in.createStringArray();
- }
-
public static final Creator<ImsSuppServiceNotification> CREATOR =
new Creator<ImsSuppServiceNotification>() {
@Override
diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java
new file mode 100644
index 0000000..d50a0f7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsUtListener.java
@@ -0,0 +1,108 @@
+/*
+ * 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.SystemApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of the IMS UT listener interface, which implements stubs.
+ * Override these methods to implement functionality.
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtListener {
+ private IImsUtListener mServiceInterface;
+ private static final String LOG_TAG = "ImsUtListener";
+
+ public void onUtConfigurationUpdated(int id) {
+ try {
+ mServiceInterface.utConfigurationUpdated(null, id);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationUpdated: remote exception");
+ }
+ }
+
+ public void onUtConfigurationUpdateFailed(int id, ImsReasonInfo error) {
+ try {
+ mServiceInterface.utConfigurationUpdateFailed(null, id, error);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationUpdateFailed: remote exception");
+ }
+ }
+
+ public void onUtConfigurationQueried(int id, Bundle ssInfo) {
+ try {
+ mServiceInterface.utConfigurationQueried(null, id, ssInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationQueried: remote exception");
+ }
+ }
+
+ public void onUtConfigurationQueryFailed(int id, ImsReasonInfo error) {
+ try {
+ mServiceInterface.utConfigurationQueryFailed(null, id, error);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationQueryFailed: remote exception");
+ }
+ }
+
+ public void onUtConfigurationCallBarringQueried(int id, ImsSsInfo[] cbInfo) {
+ try {
+ mServiceInterface.utConfigurationCallBarringQueried(null, id, cbInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationCallBarringQueried: remote exception");
+ }
+ }
+
+ public void onUtConfigurationCallForwardQueried(int id, ImsCallForwardInfo[] cfInfo) {
+ try {
+ mServiceInterface.utConfigurationCallForwardQueried(null, id, cfInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationCallForwardQueried: remote exception");
+ }
+ }
+
+ public void onUtConfigurationCallWaitingQueried(int id, ImsSsInfo[] cwInfo) {
+ try {
+ mServiceInterface.utConfigurationCallWaitingQueried(null, id, cwInfo);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "utConfigurationCallWaitingQueried: remote exception");
+ }
+ }
+
+ public void onSupplementaryServiceIndication(ImsSsData ssData) {
+ try {
+ mServiceInterface.onSupplementaryServiceIndication(ssData);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "onSupplementaryServiceIndication: remote exception");
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public ImsUtListener(IImsUtListener serviceInterface) {
+ mServiceInterface = serviceInterface;
+ }
+}
diff --git a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
similarity index 97%
rename from telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
rename to telephony/java/android/telephony/ims/ImsVideoCallProvider.java
index 432dc39..b4f60b9 100644
--- a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
+++ b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,8 +14,9 @@
* limitations under the License
*/
-package com.android.ims.internal;
+package android.telephony.ims;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -26,11 +27,14 @@
import android.telecom.VideoProfile.CameraCapabilities;
import android.view.Surface;
+import com.android.ims.internal.IImsVideoCallCallback;
+import com.android.ims.internal.IImsVideoCallProvider;
import com.android.internal.os.SomeArgs;
/**
* @hide
*/
+@SystemApi
public abstract class ImsVideoCallProvider {
private static final int MSG_SET_CALLBACK = 1;
private static final int MSG_SET_CAMERA = 2;
@@ -173,6 +177,7 @@
/**
* Returns binder object which can be used across IPC methods.
+ * @hide
*/
public final IImsVideoCallProvider getInterface() {
return mBinder;
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index 2fb6744..f25b4b1 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsConferenceState;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsConferenceState;
import com.android.ims.internal.IImsCallSession;
-import com.android.ims.ImsSuppServiceNotification;
+import android.telephony.ims.ImsSuppServiceNotification;
/**
* A listener type for receiving notification on IMS call session events.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl
similarity index 95%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl
index fd2eb24..c755703 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
/**
* See ImsFeature#CapabilityCallback for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
similarity index 91%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 3d424a3..4433c1c 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -15,9 +15,9 @@
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
-import android.telephony.ims.internal.aidl.IImsConfigCallback;
+import android.telephony.ims.aidl.IImsConfigCallback;
import com.android.ims.ImsConfigListener;
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl
index 52efd23..2b3f1ca 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl
@@ -15,7 +15,7 @@
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
/**
* Provides callback interface for ImsConfig when a value has changed.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
similarity index 69%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index d976686..b9a6b3c 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -14,16 +14,15 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
import android.os.Message;
-import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.internal.aidl.IImsSmsListener;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-import android.telephony.ims.internal.feature.CapabilityChangeRequest;
+import android.telephony.ims.aidl.IImsMmTelListener;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.feature.CapabilityChangeRequest;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
@@ -31,14 +30,15 @@
import com.android.ims.internal.IImsUt;
/**
- * See SmsImplBase for more information.
+ * See MmTelFeature for more information.
* {@hide}
*/
interface IImsMmTelFeature {
void setListener(IImsMmTelListener l);
int getFeatureState();
ImsCallProfile createCallProfile(int callSessionType, int callType);
- IImsCallSession createCallSession(in ImsCallProfile profile, IImsCallSessionListener listener);
+ IImsCallSession createCallSession(in ImsCallProfile profile);
+ int shouldProcessCall(in String[] uris);
IImsUt getUtInterface();
IImsEcbm getEcbmInterface();
void setUiTtyMode(int uiTtyMode, in Message onCompleteMessage);
@@ -52,8 +52,10 @@
IImsCapabilityCallback c);
// SMS APIs
void setSmsListener(IImsSmsListener l);
- oneway void sendSms(int messageRef, String format, String smsc, boolean retry, in byte[] pdu);
- oneway void acknowledgeSms(int messageRef, int result);
- oneway void acknowledgeSmsReport(int messageRef, int result);
+ oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
+ in byte[] pdu);
+ oneway void acknowledgeSms(int token, int messageRef, int result);
+ oneway void acknowledgeSmsReport(int token, int messageRef, int result);
String getSmsFormat();
+ oneway void onSmsReady();
}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
similarity index 81%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
index 8332bc0..904e7ca 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
+
+import android.os.Bundle;
import com.android.ims.internal.IImsCallSession;
@@ -23,5 +25,6 @@
* {@hide}
*/
oneway interface IImsMmTelListener {
- void onIncomingCall(IImsCallSession c);
+ void onIncomingCall(IImsCallSession c, in Bundle extras);
+ void onVoiceMessageCountUpdate(int count);
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
similarity index 94%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
index f6005b6..691cfba 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
/**
* See RcsFeature for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
similarity index 82%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
index 687b7ca..4ae0a75a 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl
@@ -15,10 +15,9 @@
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
/**
* See ImsRegistration for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
similarity index 81%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index a50575b..4f37caa 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -15,11 +15,12 @@
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.net.Uri;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
/**
* See ImsRegistrationImplBase.Callback for more information.
@@ -31,4 +32,5 @@
void onRegistering(int imsRadioTech);
void onDeregistered(in ImsReasonInfo info);
void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
+ void onSubscriberAssociatedUriChanged(in Uri[] uris);
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
similarity index 77%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index 8afb955..86f8606 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import com.android.ims.internal.IImsFeatureStatusCallback;
@@ -41,4 +41,6 @@
void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
IImsConfig getConfig(int slotId);
IImsRegistration getRegistration(int slotId);
+ oneway void enableIms(int slotId);
+ oneway void disableIms(int slotId);
}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl
similarity index 87%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
rename to telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl
index 01cca2db..54f6120 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
+package android.telephony.ims.aidl;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
/**
* See ImsService#Listener for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
similarity index 63%
copy from telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
copy to telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
index 01cca2db..606df15 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package android.telephony.ims.internal.aidl;
-
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+package android.telephony.ims.aidl;
/**
- * See ImsService#Listener for more information.
+ * See SmsImplBase for more information.
* {@hide}
*/
-oneway interface IImsServiceControllerListener {
- void onUpdateSupportedImsFeatures(in ImsFeatureConfiguration c);
-}
+oneway interface IImsSmsListener {
+ void onSendSmsResult(int token, int messageRef, int status, int reason);
+ void onSmsStatusReportReceived(int token, int messageRef, in String format,
+ in byte[] pdu);
+ void onSmsReceived(int token, in String format, in byte[] pdu);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/compat/ImsService.java b/telephony/java/android/telephony/ims/compat/ImsService.java
new file mode 100644
index 0000000..cf1efb3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/ImsService.java
@@ -0,0 +1,237 @@
+/*
+ * 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.compat;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.compat.feature.ImsFeature;
+import android.telephony.ims.compat.feature.MMTelFeature;
+import android.telephony.ims.compat.feature.RcsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsServiceController;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ * android:permission="android.permission.BIND_IMS_SERVICE" >
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ * defined below. In this example, the RCS_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ * <intent-filter>
+ * <action android:name="android.telephony.ims.compat.ImsService" />
+ * </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ * "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
+ * available to place emergency calls at all times. This MUST be implemented by the default
+ * ImsService provided in the device overlay.
+ * @hide
+ */
+public class ImsService extends Service {
+
+ private static final String LOG_TAG = "ImsService(Compat)";
+
+ /**
+ * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE = "android.telephony.ims.compat.ImsService";
+
+ // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+ // slot.
+ // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+ // call ImsFeature#onFeatureRemoved.
+ private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+ /**
+ * @hide
+ */
+ protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+ @Override
+ public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
+ IImsFeatureStatusCallback c) {
+ return createEmergencyMMTelFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createMMTelFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createRcsFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ ImsService.this.removeImsFeature(slotId, featureType, c);
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if(SERVICE_INTERFACE.equals(intent.getAction())) {
+ Log.i(LOG_TAG, "ImsService(Compat) Bound.");
+ return mImsServiceController;
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public SparseArray<ImsFeature> getFeatures(int slotId) {
+ return mFeaturesBySlot.get(slotId);
+ }
+
+ private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+ return f.getBinder();
+ } else {
+ return null;
+ }
+ }
+
+ private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ MMTelFeature f = onCreateMMTelImsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.MMTEL, c);
+ return f.getBinder();
+ } else {
+ return null;
+ }
+ }
+
+ private IImsRcsFeature createRcsFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ RcsFeature f = onCreateRcsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.RCS, c);
+ return f.getBinder();
+ } else {
+ return null;
+ }
+ }
+
+ private void setupFeature(ImsFeature f, int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ f.setContext(this);
+ f.setSlotId(slotId);
+ f.addImsFeatureStatusCallback(c);
+ addImsFeature(slotId, featureType, f);
+ // TODO: Remove once new onFeatureReady AIDL is merged in.
+ f.onFeatureReady();
+ }
+
+ private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+ synchronized (mFeaturesBySlot) {
+ // Get SparseArray for Features, by querying slot Id
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ // Populate new SparseArray of features if it doesn't exist for this slot yet.
+ features = new SparseArray<>();
+ mFeaturesBySlot.put(slotId, features);
+ }
+ features.put(featureType, f);
+ }
+ }
+
+ private void removeImsFeature(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+ + featureType + " exists on slot " + slotId);
+ return;
+ }
+ f.removeImsFeatureStatusCallback(c);
+ f.onFeatureRemoved();
+ features.remove(featureType);
+ }
+ }
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality. Must be able to handle emergency calls at any time as well.
+ * @hide
+ */
+ public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality.
+ * @hide
+ */
+ public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * @return An implementation of RcsFeature that will be used by the system for RCS.
+ * @hide
+ */
+ public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
+ return null;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
new file mode 100644
index 0000000..0a12cae
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
@@ -0,0 +1,202 @@
+/*
+ * 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.compat.feature;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public abstract class ImsFeature {
+
+ private static final String LOG_TAG = "ImsFeature";
+
+ /**
+ * Action to broadcast when ImsService is up.
+ * Internal use only.
+ * Only defined here separately compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_UP =
+ "com.android.ims.IMS_SERVICE_UP";
+
+ /**
+ * Action to broadcast when ImsService is down.
+ * Internal use only.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_DOWN =
+ "com.android.ims.IMS_SERVICE_DOWN";
+
+ /**
+ * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+ * A long value; the phone ID corresponding to the IMS service coming up or down.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String EXTRA_PHONE_ID = "android:phone_id";
+
+ // Invalid feature value
+ public static final int INVALID = -1;
+ // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+ // defined values in ImsServiceClass for compatibility purposes.
+ public static final int EMERGENCY_MMTEL = 0;
+ public static final int MMTEL = 1;
+ public static final int RCS = 2;
+ // Total number of features defined
+ public static final int MAX = 3;
+
+ // Integer values defining the state of the ImsFeature at any time.
+ @IntDef(flag = true,
+ value = {
+ STATE_NOT_AVAILABLE,
+ STATE_INITIALIZING,
+ STATE_READY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsState {}
+ public static final int STATE_NOT_AVAILABLE = 0;
+ public static final int STATE_INITIALIZING = 1;
+ public static final int STATE_READY = 2;
+
+ private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+ new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+ private @ImsState int mState = STATE_NOT_AVAILABLE;
+ private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ protected Context mContext;
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ public void setSlotId(int slotId) {
+ mSlotId = slotId;
+ }
+
+ public int getFeatureState() {
+ return mState;
+ }
+
+ protected final void setFeatureState(@ImsState int state) {
+ if (mState != state) {
+ mState = state;
+ notifyFeatureState(state);
+ }
+ }
+
+ public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+ if (c == null) {
+ return;
+ }
+ try {
+ // If we have just connected, send queued status.
+ c.notifyImsFeatureStatus(mState);
+ // Add the callback if the callback completes successfully without a RemoteException.
+ synchronized (mStatusCallbacks) {
+ mStatusCallbacks.add(c);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+
+ public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+ if (c == null) {
+ return;
+ }
+ synchronized (mStatusCallbacks) {
+ mStatusCallbacks.remove(c);
+ }
+ }
+
+ /**
+ * Internal method called by ImsFeature when setFeatureState has changed.
+ * @param state
+ */
+ private void notifyFeatureState(@ImsState int state) {
+ synchronized (mStatusCallbacks) {
+ for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+ iter.hasNext(); ) {
+ IImsFeatureStatusCallback callback = iter.next();
+ try {
+ Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+ callback.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ // remove if the callback is no longer alive.
+ iter.remove();
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+ }
+ sendImsServiceIntent(state);
+ }
+
+ /**
+ * Provide backwards compatibility using deprecated service UP/DOWN intents.
+ */
+ private void sendImsServiceIntent(@ImsState int state) {
+ if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ return;
+ }
+ Intent intent;
+ switch (state) {
+ case ImsFeature.STATE_NOT_AVAILABLE:
+ case ImsFeature.STATE_INITIALIZING:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ break;
+ case ImsFeature.STATE_READY:
+ intent = new Intent(ACTION_IMS_SERVICE_UP);
+ break;
+ default:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ }
+ intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Called when the feature is ready to use.
+ */
+ public abstract void onFeatureReady();
+
+ /**
+ * Called when the feature is being removed and must be cleaned up.
+ */
+ public abstract void onFeatureRemoved();
+
+ /**
+ * @return Binder instance
+ */
+ public abstract IInterface getBinder();
+}
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
similarity index 90%
rename from telephony/java/android/telephony/ims/feature/MMTelFeature.java
rename to telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
index 4e095e3a..d3d17f4 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,13 +14,13 @@
* limitations under the License
*/
-package android.telephony.ims.feature;
+package android.telephony.ims.compat.feature;
import android.app.PendingIntent;
import android.os.Message;
import android.os.RemoteException;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
@@ -29,7 +29,11 @@
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.compat.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
/**
* Base implementation for MMTel.
@@ -108,10 +112,10 @@
}
@Override
- public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+ throws RemoteException {
synchronized (mLock) {
- return MMTelFeature.this.createCallSession(sessionId, profile, listener);
+ return MMTelFeature.this.createCallSession(sessionId, profile, null);
}
}
@@ -126,7 +130,8 @@
@Override
public IImsUt getUtInterface() throws RemoteException {
synchronized (mLock) {
- return MMTelFeature.this.getUtInterface();
+ ImsUtImplBase implBase = MMTelFeature.this.getUtInterface();
+ return implBase != null ? implBase.getInterface() : null;
}
}
@@ -154,7 +159,8 @@
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
synchronized (mLock) {
- return MMTelFeature.this.getEcbmInterface();
+ ImsEcbmImplBase implBase = MMTelFeature.this.getEcbmInterface();
+ return implBase != null ? implBase.getImsEcbm() : null;
}
}
@@ -168,7 +174,8 @@
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
synchronized (mLock) {
- return MMTelFeature.this.getMultiEndpointInterface();
+ ImsMultiEndpointImplBase implBase = MMTelFeature.this.getMultiEndpointInterface();
+ return implBase != null ? implBase.getIImsMultiEndpoint() : null;
}
}
};
@@ -281,7 +288,6 @@
*
* @param sessionId a session id which is obtained from {@link #startSession}
* @param profile a call profile to make the call
- * @param listener An implementation of IImsCallSessionListener.
*/
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) {
@@ -301,7 +307,7 @@
/**
* @return The Ut interface for the supplementary service configuration.
*/
- public IImsUt getUtInterface() {
+ public ImsUtImplBase getUtInterface() {
return null;
}
@@ -327,7 +333,7 @@
/**
* @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
*/
- public IImsEcbm getEcbmInterface() {
+ public ImsEcbmImplBase getEcbmInterface() {
return null;
}
@@ -342,7 +348,7 @@
/**
* @return MultiEndpoint interface for DEP notifications
*/
- public IImsMultiEndpoint getMultiEndpointInterface() {
+ public ImsMultiEndpointImplBase getMultiEndpointInterface() {
return null;
}
diff --git a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java b/telephony/java/android/telephony/ims/compat/feature/RcsFeature.java
similarity index 76%
rename from telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
rename to telephony/java/android/telephony/ims/compat/feature/RcsFeature.java
index 8d1bd9d..228b330 100644
--- a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/RcsFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,9 +14,10 @@
* limitations under the License
*/
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.compat.feature;
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
+
+import com.android.ims.internal.IImsRcsFeature;
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -36,9 +37,8 @@
}
@Override
- public void changeEnabledCapabilities(CapabilityChangeRequest request,
- CapabilityCallbackProxy c) {
- // Do nothing for base implementation.
+ public void onFeatureReady() {
+
}
@Override
@@ -46,12 +46,6 @@
}
- /**{@inheritDoc}*/
- @Override
- public void onFeatureReady() {
-
- }
-
@Override
public final IImsRcsFeature getBinder() {
return mImsRcsBinder;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..e5ed825
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,594 @@
+/*
+ * 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.compat.stub;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+import android.telephony.ims.ImsCallSession;
+
+/**
+ * Compat implementation of ImsCallSessionImplBase for older implementations.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+ @Override
+ // convert to old implementation of listener
+ public final void setListener(IImsCallSessionListener listener)
+ throws RemoteException {
+ setListener(new ImsCallSessionListenerConverter(listener));
+ }
+
+ /**
+ * Sets the listener to listen to the session events. An {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ */
+ public void setListener(com.android.ims.internal.IImsCallSessionListener listener) {
+
+ }
+
+ /**
+ * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
+ */
+ @Override
+ public void close() {
+
+ }
+
+ /**
+ * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+ */
+ @Override
+ public String getCallId() {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+ * with.
+ */
+ @Override
+ public ImsCallProfile getCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ @Override
+ public ImsCallProfile getLocalCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ @Override
+ public ImsCallProfile getRemoteCallProfile() {
+ return null;
+ }
+
+ /**
+ * @param name The String extra key.
+ * @return The string extra value associated with the specified property.
+ */
+ @Override
+ public String getProperty(String name) {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallSessionImplBase} state.
+ */
+ @Override
+ public int getState() {
+ return -1;
+ }
+
+ /**
+ * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+ */
+ @Override
+ public boolean isInCall() {
+ return false;
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call should be muted, false otherwise.
+ */
+ @Override
+ public void setMute(boolean muted) {
+ }
+
+ /**
+ * Initiates an IMS call with the specified number and call profile.
+ * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+ * defined session events.
+ * Only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void start(String callee, ImsCallProfile profile) {
+ }
+
+ /**
+ * Initiates an IMS call with the specified participants and call profile.
+ * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+ * defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void startConference(String[] participants, ImsCallProfile profile) {
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see {@link ImsCallSession.Listener#callSessionStarted}
+ */
+ @Override
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ @Override
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void reject(int reason) {
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionTerminated}
+ */
+ @Override
+ public void terminate(int reason) {
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+ * called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see {@link ImsCallSession.Listener#callSessionHeld},
+ * {@link ImsCallSession.Listener#callSessionHoldFailed}
+ */
+ @Override
+ public void hold(ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link ImsCallSession.Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+ * @see {@link ImsCallSession.Listener#callSessionResumed},
+ * {@link ImsCallSession.Listener#callSessionResumeFailed}
+ */
+ @Override
+ public void resume(ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Merges the active and held call. When the merge starts,
+ * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+ * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+ * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+ * fails.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+ * {@link ImsCallSession.Listener#callSessionMergeComplete},
+ * {@link ImsCallSession.Listener#callSessionMergeFailed}
+ */
+ @Override
+ public void merge() {
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see {@link ImsCallSession.Listener#callSessionUpdated},
+ * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+ */
+ @Override
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants participant list to be invited to the conference call after extending the
+ * call
+ * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+ * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+ */
+ @Override
+ public void extendToConference(String[] participants) {
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants participant list to be invited to the conference call
+ * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+ */
+ @Override
+ public void inviteParticipants(String[] participants) {
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+ */
+ @Override
+ public void removeParticipants(String[] participants) {
+ }
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void sendDtmf(char c, Message result) {
+ }
+
+ /**
+ * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void startDtmf(char c) {
+ }
+
+ /**
+ * Stop a DTMF code.
+ */
+ @Override
+ public void stopDtmf() {
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ @Override
+ public void sendUssd(String ussdMessage) {
+ }
+
+ @Override
+ public IImsVideoCallProvider getVideoCallProvider() {
+ return null;
+ }
+
+ /**
+ * Determines if the current session is multiparty.
+ * @return {@code True} if the session is multiparty.
+ */
+ @Override
+ public boolean isMultiparty() {
+ return false;
+ }
+
+ /**
+ * Device issues RTT modify request
+ * @param toProfile The profile with requested changes made
+ */
+ @Override
+ public void sendRttModifyRequest(ImsCallProfile toProfile) {
+ }
+
+ /**
+ * Device responds to Remote RTT modify request
+ * @param status true if the the request was accepted or false of the request is defined.
+ */
+ @Override
+ public void sendRttModifyResponse(boolean status) {
+ }
+
+ /**
+ * Device sends RTT message
+ * @param rttMessage RTT message to be sent
+ */
+ @Override
+ public void sendRttMessage(String rttMessage) {
+ }
+
+ /**
+ * There are two different ImsCallSessionListeners that need to reconciled here, we need to
+ * convert the "old" version of the com.android.ims.internal.IImsCallSessionListener to the
+ * "new" version of the Listener android.telephony.ims.ImsCallSessionListener when calling
+ * back to the framework.
+ */
+ private class ImsCallSessionListenerConverter
+ extends com.android.ims.internal.IImsCallSessionListener.Stub {
+
+ private final IImsCallSessionListener mNewListener;
+
+ public ImsCallSessionListenerConverter(IImsCallSessionListener listener) {
+ mNewListener = listener;
+ }
+
+ @Override
+ public void callSessionProgressing(IImsCallSession i,
+ ImsStreamMediaProfile imsStreamMediaProfile) throws RemoteException {
+ mNewListener.callSessionProgressing(imsStreamMediaProfile);
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionInitiated(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionInitiatedFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionTerminated(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionHeld(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionHeld(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionHoldFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionHoldReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionResumed(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionResumeFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionResumeReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionMergeStarted(IImsCallSession i, IImsCallSession newSession,
+ ImsCallProfile profile)
+ throws RemoteException {
+ mNewListener.callSessionMergeStarted(newSession, profile);
+ }
+
+ @Override
+ public void callSessionMergeComplete(IImsCallSession iImsCallSession)
+ throws RemoteException {
+ mNewListener.callSessionMergeComplete(iImsCallSession);
+ }
+
+ @Override
+ public void callSessionMergeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionMergeFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionUpdated(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionUpdated(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionUpdateFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionUpdateReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession i, IImsCallSession newSession,
+ ImsCallProfile imsCallProfile) throws RemoteException {
+ mNewListener.callSessionConferenceExtended(newSession, imsCallProfile);
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession i,
+ ImsReasonInfo imsReasonInfo) throws RemoteException {
+ mNewListener.callSessionConferenceExtendFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession i,
+ IImsCallSession newSession, ImsCallProfile imsCallProfile)
+ throws RemoteException {
+ mNewListener.callSessionConferenceExtendReceived(newSession, imsCallProfile);
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(IImsCallSession i)
+ throws RemoteException {
+ mNewListener.callSessionInviteParticipantsRequestDelivered();
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession i,
+ ImsReasonInfo imsReasonInfo) throws RemoteException {
+ mNewListener.callSessionInviteParticipantsRequestFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession i)
+ throws RemoteException {
+ mNewListener.callSessionRemoveParticipantsRequestDelivered();
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession i,
+ ImsReasonInfo imsReasonInfo) throws RemoteException {
+ mNewListener.callSessionRemoveParticipantsRequestFailed(imsReasonInfo);
+ }
+
+ @Override
+ public void callSessionConferenceStateUpdated(IImsCallSession i,
+ ImsConferenceState imsConferenceState) throws RemoteException {
+ mNewListener.callSessionConferenceStateUpdated(imsConferenceState);
+ }
+
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession i, int mode, String message)
+ throws RemoteException {
+ mNewListener.callSessionUssdMessageReceived(mode, message);
+ }
+
+ @Override
+ public void callSessionHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) throws RemoteException {
+ mNewListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+ }
+
+ @Override
+ public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
+ int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
+ mNewListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+ }
+
+ @Override
+ public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
+ throws RemoteException {
+ mNewListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+ }
+
+ @Override
+ public void callSessionTtyModeReceived(IImsCallSession iImsCallSession, int mode)
+ throws RemoteException {
+ mNewListener.callSessionTtyModeReceived(mode);
+ }
+
+ @Override
+ public void callSessionMultipartyStateChanged(IImsCallSession i, boolean isMultiparty)
+ throws RemoteException {
+ mNewListener.callSessionMultipartyStateChanged(isMultiparty);
+ }
+
+ @Override
+ public void callSessionSuppServiceReceived(IImsCallSession i,
+ ImsSuppServiceNotification imsSuppServiceNotification) throws RemoteException {
+ mNewListener.callSessionSuppServiceReceived(imsSuppServiceNotification);
+ }
+
+ @Override
+ public void callSessionRttModifyRequestReceived(IImsCallSession i,
+ ImsCallProfile imsCallProfile) throws RemoteException {
+ mNewListener.callSessionRttModifyRequestReceived(imsCallProfile);
+ }
+
+ @Override
+ public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
+ mNewListener.callSessionRttModifyResponseReceived(status);
+ }
+
+ @Override
+ public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
+ mNewListener.callSessionRttMessageReceived(rttMessage);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..2c325ba8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.stub;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+
+
+/**
+ * Base implementation of ImsConfig.
+ * Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
+ * @hide
+ */
+
+public class ImsConfigImplBase {
+
+ static final private String TAG = "ImsConfigImplBase";
+
+ ImsConfigStub mImsConfigStub;
+
+ public ImsConfigImplBase(Context context) {
+ mImsConfigStub = new ImsConfigStub(this, context);
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ public int getProvisionedValue(int item) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ public String getProvisionedStringValue(int item) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ public int setProvisionedValue(int item, int value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ public int setProvisionedStringValue(int item, String value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Gets the value of the specified IMS feature item for specified network type.
+ * This operation gets the feature config value from the master storage (i.e. final
+ * value). Asynchronous non-blocking call.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param listener feature value returned asynchronously through listener.
+ */
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item for specified network type.
+ * This operation stores the user setting in setting db from which master db
+ * is derived.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Gets the value for IMS VoLTE provisioned.
+ * This should be the same as the operator provisioned value if applies.
+ */
+ public boolean getVolteProvisioned() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Gets the value for IMS feature item video quality.
+ *
+ * @param listener Video quality value returned asynchronously through listener.
+ */
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item video quality.
+ *
+ * @param quality, defines the value of video quality.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+ }
+
+ public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call #setProvisionedValue and assumes the result succeeded.
+ * This should only be used by modem when they implicitly changed provisioned values.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ */
+ public final void notifyProvisionedValueChanged(int item, int value) {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call #setProvisionedValue and assumes the result succeeded.
+ * This should only be used by modem when they implicitly changed provisioned values.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ */
+ public final void notifyProvisionedValueChanged(int item, String value) {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ }
+
+ /**
+ * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+ * in order to get/set configuration parameters.
+ *
+ * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+ * with actual implementations from vendors. This class caches provisioned values from
+ * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+ * it first checks cache layer. If missed, it will call the vendor implementation of
+ * ImsConfigImplBase API.
+ * and cache the return value if the set succeeds.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ static public class ImsConfigStub extends IImsConfig.Stub {
+ Context mContext;
+ WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+ private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+ private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+ @VisibleForTesting
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
+ mContext = context;
+ mImsConfigImplBaseWeakReference =
+ new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ @Override
+ public synchronized int getProvisionedValue(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ int retVal = getImsConfigImpl().getProvisionedValue(item);
+ if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ @Override
+ public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedStringValue.get(item);
+ } else {
+ String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+ if (retVal != null) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived, and write it into local cache.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+ mProvisionedIntValue.remove(item);
+ int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ } else {
+ Log.d(TAG, "Set provision value of " + item +
+ " to " + value + " failed with error code " + retVal);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived, and write it into local cache.
+ * Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public synchronized int setProvisionedStringValue(int item, String value)
+ throws RemoteException {
+ mProvisionedStringValue.remove(item);
+ int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getFeatureValue.
+ */
+ @Override
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().getFeatureValue(feature, network, listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.setFeatureValue.
+ */
+ @Override
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().setFeatureValue(feature, network, value, listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
+ */
+ @Override
+ public boolean getVolteProvisioned() throws RemoteException {
+ return getImsConfigImpl().getVolteProvisioned();
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getVideoQuality.
+ */
+ @Override
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ getImsConfigImpl().getVideoQuality(listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.setVideoQuality.
+ */
+ @Override
+ public void setVideoQuality(int quality, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().setVideoQuality(quality, listener);
+ }
+
+ private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+ ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+ if (ref == null) {
+ throw new RemoteException("Fail to get ImsConfigImpl");
+ } else {
+ return ref;
+ }
+ }
+
+ private void sendImsConfigChangedIntent(int item, int value) {
+ sendImsConfigChangedIntent(item, Integer.toString(value));
+ }
+
+ private void sendImsConfigChangedIntent(int item, String value) {
+ Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+ if (mContext != null) {
+ mContext.sendBroadcast(configChangedIntent);
+ }
+ }
+
+ protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+ mProvisionedIntValue.put(item, value);
+ if (notifyChange) {
+ sendImsConfigChangedIntent(item, value);
+ }
+ }
+
+ protected synchronized void updateCachedValue(
+ int item, String value, boolean notifyChange) {
+ mProvisionedStringValue.put(item, value);
+ if (notifyChange) {
+ sendImsConfigChangedIntent(item, value);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
similarity index 90%
rename from telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
rename to telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
index daa74c8..b2aa080 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,15 +14,15 @@
* limitations under the License
*/
-package android.telephony.ims.stub;
+package android.telephony.ims.compat.stub;
import android.os.Bundle;
import android.os.RemoteException;
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsData;
-import com.android.ims.ImsSsInfo;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.aidl
similarity index 92%
rename from telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
rename to telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.aidl
index f4ec0eb..e789bd5 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.aidl
@@ -14,6 +14,6 @@
* limitations under the License
*/
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.feature;
parcelable CapabilityChangeRequest;
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
similarity index 78%
rename from telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
rename to telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index 4d18873..7c793a5 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,11 +14,12 @@
* limitations under the License
*/
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.feature;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.ArraySet;
import java.util.ArrayList;
@@ -30,17 +31,32 @@
* the request.
* {@hide}
*/
-public class CapabilityChangeRequest implements Parcelable {
+@SystemApi
+public final class CapabilityChangeRequest implements Parcelable {
+ /**
+ * Contains a feature capability, defined as
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS},
+ * along with an associated technology, defined as
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ */
public static class CapabilityPair {
private final int mCapability;
private final int radioTech;
- public CapabilityPair(int capability, int radioTech) {
+ public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
this.mCapability = capability;
this.radioTech = radioTech;
}
+ /**
+ * @hide
+ */
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -52,6 +68,9 @@
return getRadioTech() == that.getRadioTech();
}
+ /**
+ * @hide
+ */
@Override
public int hashCode() {
int result = getCapability();
@@ -59,10 +78,22 @@
return result;
}
+ /**
+ * @return The stored capability, defined as
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+ */
public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
return mCapability;
}
+ /**
+ * @return the stored radio technology, defined as
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ */
public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
return radioTech;
}
@@ -73,6 +104,7 @@
// Pair contains <radio tech, mCapability>
private final Set<CapabilityPair> mCapabilitiesToDisable;
+ /** @hide */
public CapabilityChangeRequest() {
mCapabilitiesToEnable = new ArraySet<>();
mCapabilitiesToDisable = new ArraySet<>();
@@ -130,6 +162,9 @@
}
}
+ /**
+ * @hide
+ */
protected CapabilityChangeRequest(Parcel in) {
int enableSize = in.readInt();
mCapabilitiesToEnable = new ArraySet<>(enableSize);
@@ -177,17 +212,24 @@
}
}
+ /**
+ * @hide
+ */
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CapabilityChangeRequest)) return false;
- CapabilityChangeRequest that = (CapabilityChangeRequest) o;
+ CapabilityChangeRequest
+ that = (CapabilityChangeRequest) o;
if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
}
+ /**
+ * @hide
+ */
@Override
public int hashCode() {
int result = mCapabilitiesToEnable.hashCode();
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index ca4a210..1fdbae9 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -17,28 +17,35 @@
package android.telephony.ims.feature;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.os.IInterface;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
/**
- * Base class for all IMS features that are supported by the framework.
+ * Base class for all IMS features that are supported by the framework. Use a concrete subclass
+ * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}.
+ *
* @hide
*/
+@SystemApi
public abstract class ImsFeature {
private static final String LOG_TAG = "ImsFeature";
@@ -46,7 +53,8 @@
/**
* Action to broadcast when ImsService is up.
* Internal use only.
- * Only defined here separately compatibility purposes with the old ImsService.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ *
* @hide
*/
public static final String ACTION_IMS_SERVICE_UP =
@@ -56,6 +64,7 @@
* Action to broadcast when ImsService is down.
* Internal use only.
* Only defined here separately for compatibility purposes with the old ImsService.
+ *
* @hide
*/
public static final String ACTION_IMS_SERVICE_DOWN =
@@ -65,67 +74,329 @@
* Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
* A long value; the phone ID corresponding to the IMS service coming up or down.
* Only defined here separately for compatibility purposes with the old ImsService.
+ *
* @hide
*/
public static final String EXTRA_PHONE_ID = "android:phone_id";
- // Invalid feature value
- public static final int INVALID = -1;
+ /**
+ * Invalid feature value
+ * @hide
+ */
+ public static final int FEATURE_INVALID = -1;
// ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
// defined values in ImsServiceClass for compatibility purposes.
- public static final int EMERGENCY_MMTEL = 0;
- public static final int MMTEL = 1;
- public static final int RCS = 2;
- // Total number of features defined
- public static final int MAX = 3;
+ /**
+ * This feature supports emergency calling over MMTEL.
+ */
+ public static final int FEATURE_EMERGENCY_MMTEL = 0;
+ /**
+ * This feature supports the MMTEL feature.
+ */
+ public static final int FEATURE_MMTEL = 1;
+ /**
+ * This feature supports the RCS feature.
+ */
+ public static final int FEATURE_RCS = 2;
+ /**
+ * Total number of features defined
+ * @hide
+ */
+ public static final int FEATURE_MAX = 3;
- // Integer values defining the state of the ImsFeature at any time.
+ /**
+ * Integer values defining IMS features that are supported in ImsFeature.
+ * @hide
+ */
@IntDef(flag = true,
value = {
- STATE_NOT_AVAILABLE,
+ FEATURE_EMERGENCY_MMTEL,
+ FEATURE_MMTEL,
+ FEATURE_RCS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeatureType {}
+
+ /**
+ * Integer values defining the state of the ImsFeature at any time.
+ * @hide
+ */
+ @IntDef(flag = true,
+ value = {
+ STATE_UNAVAILABLE,
STATE_INITIALIZING,
STATE_READY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ImsState {}
- public static final int STATE_NOT_AVAILABLE = 0;
+
+ /**
+ * This {@link ImsFeature}'s state is unavailable and should not be communicated with.
+ */
+ public static final int STATE_UNAVAILABLE = 0;
+ /**
+ * This {@link ImsFeature} state is initializing and should not be communicated with.
+ */
public static final int STATE_INITIALIZING = 1;
+ /**
+ * This {@link ImsFeature} is ready for communication.
+ */
public static final int STATE_READY = 2;
+ /**
+ * Integer values defining the result codes that should be returned from
+ * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
+ * @hide
+ */
+ @IntDef(flag = true,
+ value = {
+ CAPABILITY_ERROR_GENERIC,
+ CAPABILITY_SUCCESS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsCapabilityError {}
+
+ /**
+ * The capability was unable to be changed.
+ */
+ public static final int CAPABILITY_ERROR_GENERIC = -1;
+ /**
+ * The capability was able to be changed.
+ */
+ public static final int CAPABILITY_SUCCESS = 0;
+
+
+ /**
+ * The framework implements this callback in order to register for Feature Capability status
+ * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
+ * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
+ * callbacks when the ImsService can not change the capability as requested, via
+ * {@link #onChangeCapabilityConfigurationError}.
+ *
+ * @hide
+ */
+ public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+ @Override
+ public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
+ onCapabilitiesStatusChanged(new Capabilities(config));
+ }
+
+ /**
+ * Returns the result of a query for the capability configuration of a requested capability.
+ *
+ * @param capability The capability that was requested.
+ * @param radioTech The IMS radio technology associated with the capability.
+ * @param isEnabled true if the capability is enabled, false otherwise.
+ */
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+
+ }
+
+ /**
+ * Called when a change to the capability configuration has returned an error.
+ *
+ * @param capability The capability that was requested to be changed.
+ * @param radioTech The IMS radio technology associated with the capability.
+ * @param reason error associated with the failure to change configuration.
+ */
+ @Override
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsCapabilityError int reason) {
+ }
+
+ /**
+ * The status of the feature's capabilities has changed to either available or unavailable.
+ * If unavailable, the feature is not able to support the unavailable capability at this
+ * time.
+ *
+ * @param config The new availability of the capabilities.
+ */
+ public void onCapabilitiesStatusChanged(Capabilities config) {
+ }
+ }
+
+ /**
+ * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
+ * provided.
+ */
+ protected static class CapabilityCallbackProxy {
+ private final IImsCapabilityCallback mCallback;
+
+ /** @hide */
+ public CapabilityCallbackProxy(IImsCapabilityCallback c) {
+ mCallback = c;
+ }
+
+ /**
+ * This method notifies the provided framework callback that the request to change the
+ * indicated capability has failed and has not changed.
+ *
+ * @param capability The Capability that will be notified to the framework, defined as
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+ * @param radioTech The radio tech that this capability failed for, defined as
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+ * @param reason The reason this capability was unable to be changed, defined as
+ * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
+ */
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsCapabilityError int reason) {
+ if (mCallback == null) {
+ return;
+ }
+ try {
+ mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
+ }
+ }
+ }
+
+ /**
+ * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
+ * @hide
+ */
+ public static class Capabilities {
+ protected int mCapabilities = 0;
+
+ public Capabilities() {
+ }
+
+ protected Capabilities(int capabilities) {
+ mCapabilities = capabilities;
+ }
+
+ /**
+ * @param capabilities Capabilities to be added to the configuration in the form of a
+ * bit mask.
+ */
+ public void addCapabilities(int capabilities) {
+ mCapabilities |= capabilities;
+ }
+
+ /**
+ * @param capabilities Capabilities to be removed to the configuration in the form of a
+ * bit mask.
+ */
+ public void removeCapabilities(int capabilities) {
+ mCapabilities &= ~capabilities;
+ }
+
+ /**
+ * @return true if all of the capabilities specified are capable.
+ */
+ public boolean isCapable(int capabilities) {
+ return (mCapabilities & capabilities) == capabilities;
+ }
+
+ /**
+ * @return a deep copy of the Capabilites.
+ */
+ public Capabilities copy() {
+ return new Capabilities(mCapabilities);
+ }
+
+ /**
+ * @return a bitmask containing the capability flags directly.
+ */
+ public int getMask() {
+ return mCapabilities;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Capabilities)) return false;
+
+ Capabilities that = (Capabilities) o;
+
+ return mCapabilities == that.mCapabilities;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return mCapabilities;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public String toString() {
+ return "Capabilities: " + Integer.toBinaryString(mCapabilities);
+ }
+ }
+
private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
- private @ImsState int mState = STATE_NOT_AVAILABLE;
+ private @ImsState int mState = STATE_UNAVAILABLE;
private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- private Context mContext;
+ /**
+ * @hide
+ */
+ protected Context mContext;
+ private final Object mLock = new Object();
+ private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
+ = new RemoteCallbackList<>();
+ private Capabilities mCapabilityStatus = new Capabilities();
- public void setContext(Context context) {
+ /**
+ * @hide
+ */
+ public final void initialize(Context context, int slotId) {
mContext = context;
- }
-
- public void setSlotId(int slotId) {
mSlotId = slotId;
}
+ /**
+ * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE},
+ * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+ * @hide
+ */
public int getFeatureState() {
- return mState;
- }
-
- protected final void setFeatureState(@ImsState int state) {
- if (mState != state) {
- mState = state;
- notifyFeatureState(state);
+ synchronized (mLock) {
+ return mState;
}
}
- public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
- if (c == null) {
- return;
+ /**
+ * Set the state of the ImsFeature. The state is used as a signal to the framework to start or
+ * stop communication, depending on the state sent.
+ * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
+ * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+ */
+ public final void setFeatureState(@ImsState int state) {
+ synchronized (mLock) {
+ if (mState != state) {
+ mState = state;
+ notifyFeatureState(state);
+ }
}
+ }
+
+ /**
+ * Not final for testing, but shouldn't be extended!
+ * @hide
+ */
+ @VisibleForTesting
+ public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
try {
// If we have just connected, send queued status.
- c.notifyImsFeatureStatus(mState);
+ c.notifyImsFeatureStatus(getFeatureState());
// Add the callback if the callback completes successfully without a RemoteException.
- synchronized (mStatusCallbacks) {
+ synchronized (mLock) {
mStatusCallbacks.add(c);
}
} catch (RemoteException e) {
@@ -133,23 +404,24 @@
}
}
- public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
- if (c == null) {
- return;
- }
- synchronized (mStatusCallbacks) {
+ /**
+ * Not final for testing, but shouldn't be extended!
+ * @hide
+ */
+ @VisibleForTesting
+ public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+ synchronized (mLock) {
mStatusCallbacks.remove(c);
}
}
/**
* Internal method called by ImsFeature when setFeatureState has changed.
- * @param state
*/
private void notifyFeatureState(@ImsState int state) {
- synchronized (mStatusCallbacks) {
+ synchronized (mLock) {
for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
- iter.hasNext(); ) {
+ iter.hasNext(); ) {
IImsFeatureStatusCallback callback = iter.next();
try {
Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
@@ -168,12 +440,12 @@
* Provide backwards compatibility using deprecated service UP/DOWN intents.
*/
private void sendImsServiceIntent(@ImsState int state) {
- if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
return;
}
Intent intent;
switch (state) {
- case ImsFeature.STATE_NOT_AVAILABLE:
+ case ImsFeature.STATE_UNAVAILABLE:
case ImsFeature.STATE_INITIALIZING:
intent = new Intent(ACTION_IMS_SERVICE_DOWN);
break;
@@ -188,17 +460,104 @@
}
/**
- * Called when the feature is ready to use.
+ * @hide
*/
- public abstract void onFeatureReady();
+ public final void addCapabilityCallback(IImsCapabilityCallback c) {
+ mCapabilityCallbacks.register(c);
+ }
/**
- * Called when the feature is being removed and must be cleaned up.
+ * @hide
+ */
+ public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+ mCapabilityCallbacks.unregister(c);
+ }
+
+ /**
+ * @return the cached capabilities status for this feature.
+ * @hide
+ */
+ @VisibleForTesting
+ public Capabilities queryCapabilityStatus() {
+ synchronized (mLock) {
+ return mCapabilityStatus.copy();
+ }
+ }
+
+ /**
+ * Called internally to request the change of enabled capabilities.
+ * @hide
+ */
+ @VisibleForTesting
+ public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
+ IImsCapabilityCallback c) {
+ if (request == null) {
+ throw new IllegalArgumentException(
+ "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
+ }
+ changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
+ }
+
+ /**
+ * Called by the ImsFeature when the capabilities status has changed.
+ *
+ * @param c A {@link Capabilities} containing the new Capabilities status.
+ *
+ * @hide
+ */
+ protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+ synchronized (mLock) {
+ mCapabilityStatus = c.copy();
+ }
+ int count = mCapabilityCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
+ c.mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
+ "callback.");
+ }
+ }
+ } finally {
+ mCapabilityCallbacks.finishBroadcast();
+ }
+ }
+
+ /**
+ * Features should override this method to receive Capability preference change requests from
+ * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
+ * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
+ * each failed capability.
+ *
+ * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
+ * enable/disable.
+ * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
+ * setting a subset of these capabilities fail, using
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
+ */
+ public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c);
+
+ /**
+ * Called when the framework is removing this feature and it needs to be cleaned up.
*/
public abstract void onFeatureRemoved();
/**
- * @return Binder instance
+ * Called when the feature has been initialized and communication with the framework is set up.
+ * Any attempt by this feature to access the framework before this method is called will return
+ * with an {@link IllegalStateException}.
+ * The IMS provider should use this method to trigger registration for this feature on the IMS
+ * network, if needed.
*/
- public abstract IInterface getBinder();
+ public abstract void onFeatureReady();
+
+ /**
+ * @return Binder instance that the framework will use to communicate with this feature.
+ * @hide
+ */
+ protected abstract IInterface getBinder();
}
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
similarity index 60%
rename from telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
rename to telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 2f350c8..09267fc 100644
--- a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,33 +14,33 @@
* limitations under the License
*/
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.feature;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.telecom.TelecomManager;
-import android.telephony.ims.internal.ImsCallSessionListener;
-import android.telephony.ims.internal.SmsImplBase;
-import android.telephony.ims.internal.SmsImplBase.DeliverStatusResult;
-import android.telephony.ims.internal.SmsImplBase.StatusReportResult;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
-import android.telephony.ims.internal.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsMmTelListener;
+import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import android.util.Log;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallSession;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
@@ -53,7 +53,7 @@
* service supports.
* @hide
*/
-
+@SystemApi
public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
@@ -68,14 +68,13 @@
}
@Override
- public void setSmsListener(IImsSmsListener l) throws RemoteException {
- MmTelFeature.this.setSmsListener(l);
- }
-
- @Override
public int getFeatureState() throws RemoteException {
synchronized (mLock) {
- return MmTelFeature.this.getFeatureState();
+ try {
+ return MmTelFeature.this.getFeatureState();
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
}
}
@@ -84,45 +83,57 @@
public ImsCallProfile createCallProfile(int callSessionType, int callType)
throws RemoteException {
synchronized (mLock) {
- return MmTelFeature.this.createCallProfile(callSessionType, callType);
+ try {
+ return MmTelFeature.this.createCallProfile(callSessionType, callType);
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
}
}
@Override
- public IImsCallSession createCallSession(ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
+ public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
synchronized (mLock) {
- ImsCallSession s = MmTelFeature.this.createCallSession(profile,
- new ImsCallSessionListener(listener));
- return s != null ? s.getSession() : null;
+ return createCallSessionInterface(profile);
+ }
+ }
+
+ @Override
+ public int shouldProcessCall(String[] numbers) {
+ synchronized (mLock) {
+ return MmTelFeature.this.shouldProcessCall(numbers);
}
}
@Override
public IImsUt getUtInterface() throws RemoteException {
synchronized (mLock) {
- return MmTelFeature.this.getUt();
+ return MmTelFeature.this.getUtInterface();
}
}
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
synchronized (mLock) {
- return MmTelFeature.this.getEcbm();
+ return MmTelFeature.this.getEcbmInterface();
}
}
@Override
public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
synchronized (mLock) {
- MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+ try {
+ MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
+ }
}
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
synchronized (mLock) {
- return MmTelFeature.this.getMultiEndpoint();
+ return MmTelFeature.this.getMultiEndpointInterface();
}
}
@@ -154,23 +165,29 @@
}
@Override
- public void sendSms(int messageRef, String format, String smsc, boolean retry, byte[] pdu) {
+ public void setSmsListener(IImsSmsListener l) throws RemoteException {
+ MmTelFeature.this.setSmsListener(l);
+ }
+
+ @Override
+ public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
+ byte[] pdu) {
synchronized (mLock) {
- MmTelFeature.this.sendSms(messageRef, format, smsc, retry, pdu);
+ MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
}
}
@Override
- public void acknowledgeSms(int messageRef, int result) {
+ public void acknowledgeSms(int token, int messageRef, int result) {
synchronized (mLock) {
- MmTelFeature.this.acknowledgeSms(messageRef, result);
+ MmTelFeature.this.acknowledgeSms(token, messageRef, result);
}
}
@Override
- public void acknowledgeSmsReport(int messageRef, int result) {
+ public void acknowledgeSmsReport(int token, int messageRef, int result) {
synchronized (mLock) {
- MmTelFeature.this.acknowledgeSmsReport(messageRef, result);
+ MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
}
}
@@ -180,11 +197,22 @@
return MmTelFeature.this.getSmsFormat();
}
}
+
+ @Override
+ public void onSmsReady() {
+ synchronized (mLock) {
+ MmTelFeature.this.onSmsReady();
+ }
+ }
};
/**
* Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
- * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}.
+ * The capabilities that are used in MmTelFeature are defined as
+ * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+ * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
+ * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
*
* The capabilities of this MmTelFeature will be set by the framework and can be queried with
* {@link #queryCapabilityStatus()}.
@@ -195,6 +223,9 @@
*/
public static class MmTelCapabilities extends Capabilities {
+ /**
+ * @hide
+ */
@VisibleForTesting
public MmTelCapabilities() {
super();
@@ -204,6 +235,10 @@
mCapabilities = c.mCapabilities;
}
+ public MmTelCapabilities(int capabilities) {
+ mCapabilities = capabilities;
+ }
+
@IntDef(flag = true,
value = {
CAPABILITY_TYPE_VOICE,
@@ -248,26 +283,74 @@
public final boolean isCapable(@MmTelCapability int capabilities) {
return super.isCapable(capabilities);
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
+ builder.append("Voice: ");
+ builder.append(isCapable(CAPABILITY_TYPE_VOICE));
+ builder.append(" Video: ");
+ builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
+ builder.append(" UT: ");
+ builder.append(isCapable(CAPABILITY_TYPE_UT));
+ builder.append(" SMS: ");
+ builder.append(isCapable(CAPABILITY_TYPE_SMS));
+ builder.append("]");
+ return builder.toString();
+ }
}
/**
* Listener that the framework implements for communication from the MmTelFeature.
+ * @hide
*/
public static class Listener extends IImsMmTelListener.Stub {
- @Override
- public final void onIncomingCall(IImsCallSession c) {
- onIncomingCall(new ImsCallSession(c));
- }
-
/**
* Called when the IMS provider receives an incoming call.
* @param c The {@link ImsCallSession} associated with the new call.
*/
- public void onIncomingCall(ImsCallSession c) {
+ @Override
+ public void onIncomingCall(IImsCallSession c, Bundle extras) {
+
+ }
+
+ /**
+ * Updates the Listener when the voice message count for IMS has changed.
+ * @param count an integer representing the new message count.
+ */
+ @Override
+ public void onVoiceMessageCountUpdate(int count) {
+
}
}
+ /**
+ * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
+ * outgoing call as IMS.
+ */
+ public static final int PROCESS_CALL_IMS = 0;
+ /**
+ * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
+ * not process the outgoing NON_EMERGENCY call as IMS and should instead use circuit switch.
+ */
+ public static final int PROCESS_CALL_CSFB = 1;
+ /**
+ * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
+ * not process the outgoing EMERGENCY call as IMS and should instead use circuit switch.
+ */
+ public static final int PROCESS_CALL_EMERGENCY_CSFB = 2;
+
+ @IntDef(flag = true,
+ value = {
+ PROCESS_CALL_IMS,
+ PROCESS_CALL_CSFB,
+ PROCESS_CALL_EMERGENCY_CSFB
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProcessCallResult {}
+
+
// Lock for feature synchronization
private final Object mLock = new Object();
private IImsMmTelListener mListener;
@@ -280,10 +363,9 @@
synchronized (mLock) {
mListener = listener;
}
- }
-
- private void setSmsListener(IImsSmsListener listener) {
- getSmsImplementation().registerSmsListener(listener);
+ if (mListener != null) {
+ onFeatureReady();
+ }
}
private void queryCapabilityConfigurationInternal(int capability, int radioTech,
@@ -321,23 +403,58 @@
* support the capability that is enabled. A capability that is disabled by the framework (via
* {@link #changeEnabledCapabilities}) should also show the status as disabled.
*/
- protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+ public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
super.notifyCapabilitiesStatusChanged(c);
}
/**
* Notify the framework of an incoming call.
- * @param c The {@link ImsCallSession} of the new incoming call.
- *
- * @throws RemoteException if the connection to the framework is not available. If this happens,
- * the call should be no longer considered active and should be cleaned up.
- * */
- protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException {
+ * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+ */
+ public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
}
- mListener.onIncomingCall(c.getSession());
+ try {
+ mListener.onIncomingCall(c.getServiceImpl(), extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ *
+ * @hide
+ */
+ public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ mListener.onIncomingCall(c, extras);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Notify the framework of a change in the Voice Message count.
+ * @link count the new Voice Message count.
+ */
+ public final void notifyVoiceMessageCountUpdate(int count) {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ try {
+ mListener.onVoiceMessageCountUpdate(count);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
}
}
@@ -397,21 +514,67 @@
}
/**
+ * @hide
+ */
+ public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+ throws RemoteException {
+ ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
+ return s != null ? s.getServiceImpl() : null;
+ }
+
+ /**
* Creates an {@link ImsCallSession} with the specified call profile.
* Use other methods, if applicable, instead of interacting with
* {@link ImsCallSession} directly.
*
* @param profile a call profile to make the call
- * @param listener An implementation of IImsCallSessionListener.
*/
- public ImsCallSession createCallSession(ImsCallProfile profile,
- ImsCallSessionListener listener) {
+ public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
// Base Implementation - Should be overridden
return null;
}
/**
- * @return The Ut interface for the supplementary service configuration.
+ * Called by the framework to determine if the outgoing call, designated by the outgoing
+ * {@link Uri}s, should be processed as an IMS call or CSFB call.
+ * @param numbers An array of {@link String}s that will be used for placing the call. There can
+ * be multiple {@link String}s listed in the case when we want to place an outgoing
+ * call as a conference.
+ * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
+ * call wil lbe placed over IMS or via CSFB.
+ */
+ public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+ return PROCESS_CALL_IMS;
+ }
+
+ /**
+ *
+ * @hide
+ */
+ protected IImsUt getUtInterface() throws RemoteException {
+ ImsUtImplBase utImpl = getUt();
+ return utImpl != null ? utImpl.getInterface() : null;
+ }
+
+ /**
+ * @hide
+ */
+ protected IImsEcbm getEcbmInterface() throws RemoteException {
+ ImsEcbmImplBase ecbmImpl = getEcbm();
+ return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+ }
+
+ /**
+ * @hide
+ */
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
+ return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+ }
+
+ /**
+ * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
+ * configuration.
*/
public ImsUtImplBase getUt() {
// Base Implementation - Should be overridden
@@ -419,7 +582,8 @@
}
/**
- * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
+ * calls that support it.
*/
public ImsEcbmImplBase getEcbm() {
// Base Implementation - Should be overridden
@@ -427,7 +591,8 @@
}
/**
- * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
+ * package processing for multi-endpoint.
*/
public ImsMultiEndpointImplBase getMultiEndpoint() {
// Base Implementation - Should be overridden
@@ -443,34 +608,46 @@
* {@link TelecomManager#TTY_MODE_VCO}
* @param onCompleteMessage A {@link Message} to be used when the mode has been set.
*/
- void setUiTtyMode(int mode, Message onCompleteMessage) {
+ public void setUiTtyMode(int mode, Message onCompleteMessage) {
// Base Implementation - Should be overridden
}
- private void sendSms(int messageRef, String format, String smsc, boolean isRetry, byte[] pdu) {
- getSmsImplementation().sendSms(messageRef, format, smsc, isRetry, pdu);
+ private void setSmsListener(IImsSmsListener listener) {
+ getSmsImplementation().registerSmsListener(listener);
}
- private void acknowledgeSms(int messageRef, @DeliverStatusResult int result) {
- getSmsImplementation().acknowledgeSms(messageRef, result);
+ private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+ byte[] pdu) {
+ getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
}
- private void acknowledgeSmsReport(int messageRef, @StatusReportResult int result) {
- getSmsImplementation().acknowledgeSmsReport(messageRef, result);
+ private void acknowledgeSms(int token, int messageRef,
+ @ImsSmsImplBase.DeliverStatusResult int result) {
+ getSmsImplementation().acknowledgeSms(token, messageRef, result);
}
- private String getSmsFormat() {
- return getSmsImplementation().getSmsFormat();
+ private void acknowledgeSmsReport(int token, int messageRef,
+ @ImsSmsImplBase.StatusReportResult int result) {
+ getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
+ }
+
+ private void onSmsReady() {
+ getSmsImplementation().onReady();
}
/**
* Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
* non-functional implementation is returned.
*
- * @return an instance of {@link SmsImplBase} which should be implemented by the IMS Provider.
+ * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
+ * Provider.
*/
- protected SmsImplBase getSmsImplementation() {
- return new SmsImplBase();
+ public ImsSmsImplBase getSmsImplementation() {
+ return new ImsSmsImplBase();
+ }
+
+ private String getSmsFormat() {
+ return getSmsImplementation().getSmsFormat();
}
/**{@inheritDoc}*/
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 40c5181..a637e16 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,16 +16,18 @@
package android.telephony.ims.feature;
-import com.android.ims.internal.IImsRcsFeature;
+import android.annotation.SystemApi;
+import android.telephony.ims.aidl.IImsRcsFeature;
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
* this class and provide implementations of the RcsFeature methods that they support.
* @hide
*/
-
+@SystemApi
public class RcsFeature extends ImsFeature {
+ /**{@inheritDoc}*/
private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
// Empty Default Implementation.
};
@@ -35,16 +37,30 @@
super();
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void onFeatureReady() {
-
+ public void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c) {
+ // Do nothing for base implementation.
}
+ /**{@inheritDoc}*/
@Override
public void onFeatureRemoved() {
}
+ /**{@inheritDoc}*/
+ @Override
+ public void onFeatureReady() {
+
+ }
+
+ /**
+ * @hide
+ */
@Override
public final IImsRcsFeature getBinder() {
return mImsRcsBinder;
diff --git a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
deleted file mode 100644
index 5d16dd5..0000000
--- a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.internal;
-
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.ImsCallSession;
-
-/**
- * Proxy class for interfacing with the framework's Call session for an ongoing IMS call.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListener {
-
- private final IImsCallSessionListener mListener;
-
- public ImsCallSessionListener(IImsCallSessionListener l) {
- mListener = l;
- }
-
- /**
- * Called when a request is sent out to initiate a new session
- * and 1xx response is received from the network.
- */
- public void callSessionProgressing(ImsStreamMediaProfile profile)
- throws RemoteException {
- mListener.callSessionProgressing(profile);
- }
-
- /**
- * Called when the session is initiated.
- *
- * @param profile the associated {@link ImsCallSession}.
- */
- public void callSessionInitiated(ImsCallProfile profile) throws RemoteException {
- mListener.callSessionInitiated(profile);
- }
-
- /**
- * Called when the session establishment has failed.
- *
- * @param reasonInfo detailed reason of the session establishment failure
- */
- public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionInitiatedFailed(reasonInfo);
- }
-
- /**
- * Called when the session is terminated.
- *
- * @param reasonInfo detailed reason of the session termination
- */
- public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionTerminated(reasonInfo);
- }
-
- /**
- * Called when the session is on hold.
- */
- public void callSessionHeld(ImsCallProfile profile) throws RemoteException {
- mListener.callSessionHeld(profile);
- }
-
- /**
- * Called when the session hold has failed.
- *
- * @param reasonInfo detailed reason of the session hold failure
- */
- public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionHoldFailed(reasonInfo);
- }
-
- /**
- * Called when the session hold is received from the remote user.
- */
- public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException {
- mListener.callSessionHoldReceived(profile);
- }
-
- /**
- * Called when the session resume is done.
- */
- public void callSessionResumed(ImsCallProfile profile) throws RemoteException {
- mListener.callSessionResumed(profile);
- }
-
- /**
- * Called when the session resume has failed.
- *
- * @param reasonInfo detailed reason of the session resume failure
- */
- public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionResumeFailed(reasonInfo);
- }
-
- /**
- * Called when the session resume is received from the remote user.
- */
- public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException {
- mListener.callSessionResumeReceived(profile);
- }
-
- /**
- * Called when the session merge has been started. At this point, the {@code newSession}
- * represents the session which has been initiated to the IMS conference server for the
- * new merged conference.
- *
- * @param newSession the session object that is merged with an active & hold session
- */
- public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile)
- throws RemoteException {
- mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null,
- profile);
- }
-
- /**
- * Called when the session merge is successful and the merged session is active.
- *
- * @param newSession the new session object that is used for the conference
- */
- public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException {
- mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null);
- }
-
- /**
- * Called when the session merge has failed.
- *
- * @param reasonInfo detailed reason of the call merge failure
- */
- public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionMergeFailed(reasonInfo);
- }
-
- /**
- * Called when the session is updated (except for hold/unhold).
- */
- public void callSessionUpdated(ImsCallProfile profile) throws RemoteException {
- mListener.callSessionUpdated(profile);
- }
-
- /**
- * Called when the session update has failed.
- *
- * @param reasonInfo detailed reason of the session update failure
- */
- public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionUpdateFailed(reasonInfo);
- }
-
- /**
- * Called when the session update is received from the remote user.
- */
- public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException {
- mListener.callSessionUpdateReceived(profile);
- }
-
- /**
- * Called when the session has been extended to a conference session.
- *
- * @param newSession the session object that is extended to the conference
- * from the active session
- */
- public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile)
- throws RemoteException {
- mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null,
- profile);
- }
-
- /**
- * Called when the conference extension has failed.
- *
- * @param reasonInfo detailed reason of the conference extension failure
- */
- public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionConferenceExtendFailed(reasonInfo);
- }
-
- /**
- * Called when the conference extension is received from the remote user.
- */
- public void callSessionConferenceExtendReceived(ImsCallSession newSession,
- ImsCallProfile profile) throws RemoteException {
- mListener.callSessionConferenceExtendReceived(newSession != null
- ? newSession.getSession() : null, profile);
- }
-
- /**
- * Called when the invitation request of the participants is delivered to the conference
- * server.
- */
- public void callSessionInviteParticipantsRequestDelivered() throws RemoteException {
- mListener.callSessionInviteParticipantsRequestDelivered();
- }
-
- /**
- * Called when the invitation request of the participants has failed.
- *
- * @param reasonInfo detailed reason of the conference invitation failure
- */
- public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
- throws RemoteException {
- mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
- }
-
- /**
- * Called when the removal request of the participants is delivered to the conference
- * server.
- */
- public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException {
- mListener.callSessionRemoveParticipantsRequestDelivered();
- }
-
- /**
- * Called when the removal request of the participants has failed.
- *
- * @param reasonInfo detailed reason of the conference removal failure
- */
- public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
- throws RemoteException {
- mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
- }
-
- /**
- * Notifies the framework of the updated Call session conference state.
- *
- * @param state the new {@link ImsConferenceState} associated with the conference.
- */
- public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException {
- mListener.callSessionConferenceStateUpdated(state);
- }
-
- /**
- * Notifies the incoming USSD message.
- */
- public void callSessionUssdMessageReceived(int mode, String ussdMessage)
- throws RemoteException {
- mListener.callSessionUssdMessageReceived(mode, ussdMessage);
- }
-
- /**
- * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
- * handover from one radio technology to another.
- *
- * @param srcAccessTech The source radio access technology; one of the access technology
- * constants defined in {@link android.telephony.ServiceState}. For
- * example
- * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
- * @param targetAccessTech The target radio access technology; one of the access technology
- * constants defined in {@link android.telephony.ServiceState}. For
- * example
- * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
- */
- public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
- throws RemoteException {
- mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
- }
-
- /**
- * Called when session access technology changes.
- *
- * @param srcAccessTech original access technology
- * @param targetAccessTech new access technology
- * @param reasonInfo
- */
- public void callSessionHandover(int srcAccessTech, int targetAccessTech,
- ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
- }
-
- /**
- * Called when session access technology change fails.
- *
- * @param srcAccessTech original access technology
- * @param targetAccessTech new access technology
- * @param reasonInfo handover failure reason
- */
- public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
- ImsReasonInfo reasonInfo) throws RemoteException {
- mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
- }
-
- /**
- * Called when the TTY mode is changed by the remote party.
- *
- * @param mode one of the following: -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
- */
- public void callSessionTtyModeReceived(int mode) throws RemoteException {
- mListener.callSessionTtyModeReceived(mode);
- }
-
- /**
- * Called when the multiparty state is changed for this {@code ImsCallSession}.
- *
- * @param isMultiParty {@code true} if the session became multiparty,
- * {@code false} otherwise.
- */
-
- public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException {
- mListener.callSessionMultipartyStateChanged(isMultiParty);
- }
-
- /**
- * Called when the supplementary service information is received for the current session.
- */
- public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
- throws RemoteException {
- mListener.callSessionSuppServiceReceived(suppSrvNotification);
- }
-
- /**
- * Received RTT modify request from the remote party.
- *
- * @param callProfile ImsCallProfile with updated attributes
- */
- public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
- throws RemoteException {
- mListener.callSessionRttModifyRequestReceived(callProfile);
- }
-
- /**
- * @param status the received response for RTT modify request.
- */
- public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
- mListener.callSessionRttModifyResponseReceived(status);
- }
-
- /**
- * Device received RTT message from Remote UE.
- *
- * @param rttMessage RTT message received
- */
- public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
- mListener.callSessionRttMessageReceived(rttMessage);
- }
-}
-
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
deleted file mode 100644
index b7c8ca0..0000000
--- a/telephony/java/android/telephony/ims/internal/ImsService.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.internal;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsRegistration;
-import android.telephony.ims.internal.aidl.IImsServiceController;
-import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.internal.feature.RcsFeature;
-import android.telephony.ims.internal.stub.ImsConfigImplBase;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
- * ImsService must register the service in their AndroidManifest to be detected by the framework.
- * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
- * permission. Then, the ImsService definition in the manifest must follow the following format:
- *
- * ...
- * <service android:name=".EgImsService"
- * android:permission="android.permission.BIND_IMS_SERVICE" >
- * <!-- Apps must declare which features they support as metadata. The different categories are
- * defined below. In this example, the RCS_FEATURE feature is supported. -->
- * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
- * <intent-filter>
- * <action android:name="android.telephony.ims.ImsService" />
- * </intent-filter>
- * </service>
- * ...
- *
- * The telephony framework will then bind to the ImsService you have defined in your manifest
- * if you are either:
- * 1) Defined as the default ImsService for the device in the device overlay using
- * "config_ims_package".
- * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
- * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
- *
- * The features that are currently supported in an ImsService are:
- * - RCS_FEATURE: This ImsService implements the RcsFeature class.
- * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
- * @hide
- */
-public class ImsService extends Service {
-
- private static final String LOG_TAG = "ImsService";
-
- /**
- * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
- * @hide
- */
- public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
-
- // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
- // slot.
- // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
- // call ImsFeature#onFeatureRemoved.
- private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
-
- private IImsServiceControllerListener mListener;
-
-
- /**
- * Listener that notifies the framework of ImsService changes.
- */
- public static class Listener extends IImsServiceControllerListener.Stub {
- /**
- * The IMS features that this ImsService supports has changed.
- * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
- * that this ImsService supports. This may trigger the addition/removal of feature
- * in this service.
- */
- public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
- }
- }
-
- /**
- * @hide
- */
- protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
- @Override
- public void setListener(IImsServiceControllerListener l) {
- mListener = l;
- }
-
- @Override
- public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
- return createMmTelFeatureInternal(slotId, c);
- }
-
- @Override
- public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
- return createRcsFeatureInternal(slotId, c);
- }
-
- @Override
- public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
- throws RemoteException {
- ImsService.this.removeImsFeature(slotId, featureType, c);
- }
-
- @Override
- public ImsFeatureConfiguration querySupportedImsFeatures() {
- return ImsService.this.querySupportedImsFeatures();
- }
-
- @Override
- public void notifyImsServiceReadyForFeatureCreation() {
- ImsService.this.readyForFeatureCreation();
- }
-
- @Override
- public void notifyImsFeatureReady(int slotId, int featureType)
- throws RemoteException {
- ImsService.this.notifyImsFeatureReady(slotId, featureType);
- }
-
- @Override
- public IImsConfig getConfig(int slotId) throws RemoteException {
- ImsConfigImplBase c = ImsService.this.getConfig(slotId);
- return c != null ? c.getBinder() : null;
- }
-
- @Override
- public IImsRegistration getRegistration(int slotId) throws RemoteException {
- ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
- return r != null ? r.getBinder() : null;
- }
- };
-
- /**
- * @hide
- */
- @Override
- public IBinder onBind(Intent intent) {
- if(SERVICE_INTERFACE.equals(intent.getAction())) {
- Log.i(LOG_TAG, "ImsService Bound.");
- return mImsServiceController;
- }
- return null;
- }
-
- /**
- * @hide
- */
- @VisibleForTesting
- public SparseArray<ImsFeature> getFeatures(int slotId) {
- return mFeaturesBySlot.get(slotId);
- }
-
- private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
- MmTelFeature f = createMmTelFeature(slotId);
- if (f != null) {
- setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
- return f.getBinder();
- } else {
- Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
- return null;
- }
- }
-
- private IImsRcsFeature createRcsFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
- RcsFeature f = createRcsFeature(slotId);
- if (f != null) {
- setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
- return f.getBinder();
- } else {
- Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
- return null;
- }
- }
-
- private void setupFeature(ImsFeature f, int slotId, int featureType,
- IImsFeatureStatusCallback c) {
- f.addImsFeatureStatusCallback(c);
- f.initialize(this, slotId);
- addImsFeature(slotId, featureType, f);
- }
-
- private void addImsFeature(int slotId, int featureType, ImsFeature f) {
- synchronized (mFeaturesBySlot) {
- // Get SparseArray for Features, by querying slot Id
- SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
- if (features == null) {
- // Populate new SparseArray of features if it doesn't exist for this slot yet.
- features = new SparseArray<>();
- mFeaturesBySlot.put(slotId, features);
- }
- features.put(featureType, f);
- }
- }
-
- private void removeImsFeature(int slotId, int featureType,
- IImsFeatureStatusCallback c) {
- synchronized (mFeaturesBySlot) {
- // get ImsFeature associated with the slot/feature
- SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
- if (features == null) {
- Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
- + slotId);
- return;
- }
- ImsFeature f = features.get(featureType);
- if (f == null) {
- Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
- + featureType + " exists on slot " + slotId);
- return;
- }
- f.removeImsFeatureStatusCallback(c);
- f.onFeatureRemoved();
- features.remove(featureType);
- }
- }
-
- private void notifyImsFeatureReady(int slotId, int featureType) {
- synchronized (mFeaturesBySlot) {
- // get ImsFeature associated with the slot/feature
- SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
- if (features == null) {
- Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
- "slot " + slotId);
- return;
- }
- ImsFeature f = features.get(featureType);
- if (f == null) {
- Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
- + featureType + " exists on slot " + slotId);
- return;
- }
- f.onFeatureReady();
- }
- }
-
- /**
- * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently
- * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond
- * to the {@link ImsFeature.FeatureType}s configured here.
- * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports,
- * defined in {@link ImsFeature.FeatureType}.
- */
- public ImsFeatureConfiguration querySupportedImsFeatures() {
- // Return empty for base implementation
- return new ImsFeatureConfiguration();
- }
-
- /**
- * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
- * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may
- * trigger the framework to add/remove new ImsFeatures, depending on the configuration.
- */
- public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
- throws RemoteException {
- if (mListener == null) {
- throw new IllegalStateException("Framework is not ready");
- }
- mListener.onUpdateSupportedImsFeatures(c);
- }
-
- /**
- * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
- * the ImsService has registered for with the framework, either in the manifest or via
- * The ImsService should use this signal instead of onCreate/onBind or similar to perform
- * feature initialization because the framework may bind to this service multiple times to
- * query the ImsService's {@link ImsFeatureConfiguration} via
- * {@link #querySupportedImsFeatures()}before creating features.
- */
- public void readyForFeatureCreation() {
- }
-
- /**
- * When called, the framework is requesting that a new MmTelFeature is created for the specified
- * slot.
- *
- * @param slotId The slot ID that the MMTel Feature is being created for.
- * @return The newly created MmTelFeature associated with the slot or null if the feature is not
- * supported.
- */
- public MmTelFeature createMmTelFeature(int slotId) {
- return null;
- }
-
- /**
- * When called, the framework is requesting that a new RcsFeature is created for the specified
- * slot
- *
- * @param slotId The slot ID that the RCS Feature is being created for.
- * @return The newly created RcsFeature associated with the slot or null if the feature is not
- * supported.
- */
- public RcsFeature createRcsFeature(int slotId) {
- return null;
- }
-
- /**
- * @param slotId The slot that the IMS configuration is associated with.
- * @return ImsConfig implementation that is associated with the specified slot.
- */
- public ImsConfigImplBase getConfig(int slotId) {
- return new ImsConfigImplBase();
- }
-
- /**
- * @param slotId The slot that is associated with the IMS Registration.
- * @return the ImsRegistration implementation associated with the slot.
- */
- public ImsRegistrationImplBase getRegistration(int slotId) {
- return new ImsRegistrationImplBase();
- }
-}
diff --git a/telephony/java/android/telephony/ims/internal/SmsImplBase.java b/telephony/java/android/telephony/ims/internal/SmsImplBase.java
deleted file mode 100644
index 47414cf..0000000
--- a/telephony/java/android/telephony/ims/internal/SmsImplBase.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.internal;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.os.RemoteException;
-import android.telephony.SmsManager;
-import android.telephony.SmsMessage;
-import android.telephony.ims.internal.aidl.IImsSmsListener;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.util.Log;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Base implementation for SMS over IMS.
- *
- * Any service wishing to provide SMS over IMS should extend this class and implement all methods
- * that the service supports.
- * @hide
- */
-public class SmsImplBase {
- private static final String LOG_TAG = "SmsImplBase";
-
- @IntDef({
- SEND_STATUS_OK,
- SEND_STATUS_ERROR,
- SEND_STATUS_ERROR_RETRY,
- SEND_STATUS_ERROR_FALLBACK
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SendStatusResult {}
- /**
- * Message was sent successfully.
- */
- public static final int SEND_STATUS_OK = 1;
-
- /**
- * IMS provider failed to send the message and platform should not retry falling back to sending
- * the message using the radio.
- */
- public static final int SEND_STATUS_ERROR = 2;
-
- /**
- * IMS provider failed to send the message and platform should retry again after setting TP-RD bit
- * to high.
- */
- public static final int SEND_STATUS_ERROR_RETRY = 3;
-
- /**
- * IMS provider failed to send the message and platform should retry falling back to sending
- * the message using the radio.
- */
- public static final int SEND_STATUS_ERROR_FALLBACK = 4;
-
- @IntDef({
- DELIVER_STATUS_OK,
- DELIVER_STATUS_ERROR
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface DeliverStatusResult {}
- /**
- * Message was delivered successfully.
- */
- public static final int DELIVER_STATUS_OK = 1;
-
- /**
- * Message was not delivered.
- */
- public static final int DELIVER_STATUS_ERROR = 2;
-
- @IntDef({
- STATUS_REPORT_STATUS_OK,
- STATUS_REPORT_STATUS_ERROR
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface StatusReportResult {}
-
- /**
- * Status Report was set successfully.
- */
- public static final int STATUS_REPORT_STATUS_OK = 1;
-
- /**
- * Error while setting status report.
- */
- public static final int STATUS_REPORT_STATUS_ERROR = 2;
-
-
- // Lock for feature synchronization
- private final Object mLock = new Object();
- private IImsSmsListener mListener;
-
- /**
- * Registers a listener responsible for handling tasks like delivering messages.
- *
- * @param listener listener to register.
- *
- * @hide
- */
- public final void registerSmsListener(IImsSmsListener listener) {
- synchronized (mLock) {
- mListener = listener;
- }
- }
-
- /**
- * This method will be triggered by the platform when the user attempts to send an SMS. This
- * method should be implemented by the IMS providers to provide implementation of sending an SMS
- * over IMS.
- *
- * @param smsc the Short Message Service Center address.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
- * @param messageRef the message reference.
- * @param isRetry whether it is a retry of an already attempted message or not.
- * @param pdu PDUs representing the contents of the message.
- */
- public void sendSms(int messageRef, String format, String smsc, boolean isRetry, byte[] pdu) {
- // Base implementation returns error. Should be overridden.
- try {
- onSendSmsResult(messageRef, SEND_STATUS_ERROR, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
- }
- }
-
- /**
- * This method will be triggered by the platform after {@link #onSmsReceived(String, byte[])} has
- * been called to deliver the result to the IMS provider.
- *
- * @param result result of delivering the message. Valid values are defined in
- * {@link DeliverStatusResult}
- * @param messageRef the message reference or -1 of unavailable.
- */
- public void acknowledgeSms(int messageRef, @DeliverStatusResult int result) {
-
- }
-
- /**
- * This method will be triggered by the platform after
- * {@link #onSmsStatusReportReceived(int, int, byte[])} has been called to provide the result to
- * the IMS provider.
- *
- * @param result result of delivering the message. Valid values are defined in
- * {@link StatusReportResult}
- * @param messageRef the message reference or -1 of unavailable.
- */
- public void acknowledgeSmsReport(int messageRef, @StatusReportResult int result) {
-
- }
-
- /**
- * This method should be triggered by the IMS providers when there is an incoming message. The
- * platform will deliver the message to the messages database and notify the IMS provider of the
- * result by calling {@link #acknowledgeSms(int, int)}.
- *
- * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
- *
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
- * @param pdu PDUs representing the contents of the message.
- * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
- */
- public final void onSmsReceived(String format, byte[] pdu) throws IllegalStateException {
- synchronized (mLock) {
- if (mListener == null) {
- throw new IllegalStateException("Feature not ready.");
- }
- try {
- mListener.onSmsReceived(format, pdu);
- acknowledgeSms(-1, DELIVER_STATUS_OK);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
- acknowledgeSms(-1, DELIVER_STATUS_ERROR);
- }
- }
- }
-
- /**
- * This method should be triggered by the IMS providers to pass the result of the sent message
- * to the platform.
- *
- * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
- *
- * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
- * @param status result of sending the SMS. Valid values are defined in {@link SendStatusResult}
- * @param reason reason in case status is failure. Valid values are:
- * {@link SmsManager#RESULT_ERROR_NONE},
- * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
- * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
- * {@link SmsManager#RESULT_ERROR_NULL_PDU},
- * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
- * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED}
- * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
- * @throws RemoteException if the connection to the framework is not available. If this happens
- * attempting to send the SMS should be aborted.
- */
- public final void onSendSmsResult(int messageRef, @SendStatusResult int status, int reason)
- throws IllegalStateException, RemoteException {
- synchronized (mLock) {
- if (mListener == null) {
- throw new IllegalStateException("Feature not ready.");
- }
- mListener.onSendSmsResult(messageRef, status, reason);
- }
- }
-
- /**
- * Sets the status report of the sent message.
- *
- * @param messageRef the message reference.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
- * @param pdu PDUs representing the content of the status report.
- * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
- */
- public final void onSmsStatusReportReceived(int messageRef, String format, byte[] pdu) {
- synchronized (mLock) {
- if (mListener == null) {
- throw new IllegalStateException("Feature not ready.");
- }
- try {
- mListener.onSmsStatusReportReceived(messageRef, format, pdu);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
- acknowledgeSmsReport(messageRef, STATUS_REPORT_STATUS_ERROR);
- }
- }
- }
-
- /**
- * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
- * Provider.
- *
- * @return the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
- */
- public String getSmsFormat() {
- return SmsMessage.FORMAT_3GPP;
- }
-
-}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
deleted file mode 100644
index 468629a..0000000
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsSmsListener.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims.internal.aidl;
-
-/**
- * See MMTelFeature for more information.
- * {@hide}
- */
-interface IImsSmsListener {
- void onSendSmsResult(in int messageRef, in int status, in int reason);
- void onSmsStatusReportReceived(in int messageRef, in String format, in byte[] pdu);
- void onSmsReceived(in String format, in byte[] pdu);
-}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
deleted file mode 100644
index 9f82ad2..0000000
--- a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.internal.feature;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IInterface;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.util.Log;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.WeakHashMap;
-
-/**
- * Base class for all IMS features that are supported by the framework.
- *
- * @hide
- */
-public abstract class ImsFeature {
-
- private static final String LOG_TAG = "ImsFeature";
-
- /**
- * Action to broadcast when ImsService is up.
- * Internal use only.
- * Only defined here separately for compatibility purposes with the old ImsService.
- *
- * @hide
- */
- public static final String ACTION_IMS_SERVICE_UP =
- "com.android.ims.IMS_SERVICE_UP";
-
- /**
- * Action to broadcast when ImsService is down.
- * Internal use only.
- * Only defined here separately for compatibility purposes with the old ImsService.
- *
- * @hide
- */
- public static final String ACTION_IMS_SERVICE_DOWN =
- "com.android.ims.IMS_SERVICE_DOWN";
-
- /**
- * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
- * A long value; the phone ID corresponding to the IMS service coming up or down.
- * Only defined here separately for compatibility purposes with the old ImsService.
- *
- * @hide
- */
- public static final String EXTRA_PHONE_ID = "android:phone_id";
-
- // Invalid feature value
- public static final int FEATURE_INVALID = -1;
- // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
- // defined values in ImsServiceClass for compatibility purposes.
- public static final int FEATURE_EMERGENCY_MMTEL = 0;
- public static final int FEATURE_MMTEL = 1;
- public static final int FEATURE_RCS = 2;
- // Total number of features defined
- public static final int FEATURE_MAX = 3;
-
- // Integer values defining IMS features that are supported in ImsFeature.
- @IntDef(flag = true,
- value = {
- FEATURE_EMERGENCY_MMTEL,
- FEATURE_MMTEL,
- FEATURE_RCS
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface FeatureType {}
-
- // Integer values defining the state of the ImsFeature at any time.
- @IntDef(flag = true,
- value = {
- STATE_UNAVAILABLE,
- STATE_INITIALIZING,
- STATE_READY,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ImsState {}
-
- public static final int STATE_UNAVAILABLE = 0;
- public static final int STATE_INITIALIZING = 1;
- public static final int STATE_READY = 2;
-
- // Integer values defining the result codes that should be returned from
- // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability.
- @IntDef(flag = true,
- value = {
- CAPABILITY_ERROR_GENERIC,
- CAPABILITY_SUCCESS
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ImsCapabilityError {}
-
- public static final int CAPABILITY_ERROR_GENERIC = -1;
- public static final int CAPABILITY_SUCCESS = 0;
-
-
- /**
- * The framework implements this callback in order to register for Feature Capability status
- * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
- * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
- * callbacks when the ImsService can not change the capability as requested, via
- * {@link #onChangeCapabilityConfigurationError}.
- */
- public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
- @Override
- public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
- onCapabilitiesStatusChanged(new Capabilities(config));
- }
-
- /**
- * Returns the result of a query for the capability configuration of a requested capability.
- *
- * @param capability The capability that was requested.
- * @param radioTech The IMS radio technology associated with the capability.
- * @param isEnabled true if the capability is enabled, false otherwise.
- */
- @Override
- public void onQueryCapabilityConfiguration(int capability, int radioTech,
- boolean isEnabled) {
-
- }
-
- /**
- * Called when a change to the capability configuration has returned an error.
- *
- * @param capability The capability that was requested to be changed.
- * @param radioTech The IMS radio technology associated with the capability.
- * @param reason error associated with the failure to change configuration.
- */
- @Override
- public void onChangeCapabilityConfigurationError(int capability, int radioTech,
- int reason) {
- }
-
- /**
- * The status of the feature's capabilities has changed to either available or unavailable.
- * If unavailable, the feature is not able to support the unavailable capability at this
- * time.
- *
- * @param config The new availability of the capabilities.
- */
- public void onCapabilitiesStatusChanged(Capabilities config) {
- }
- }
-
- /**
- * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
- * provided.
- */
- protected static class CapabilityCallbackProxy {
- private final IImsCapabilityCallback mCallback;
-
- public CapabilityCallbackProxy(IImsCapabilityCallback c) {
- mCallback = c;
- }
-
- /**
- * This method notifies the provided framework callback that the request to change the
- * indicated capability has failed and has not changed.
- *
- * @param capability The Capability that will be notified to the framework.
- * @param radioTech The radio tech that this capability failed for.
- * @param reason The reason this capability was unable to be changed.
- */
- public void onChangeCapabilityConfigurationError(int capability, int radioTech,
- @ImsCapabilityError int reason) {
- try {
- mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
- }
- }
-
- public void onQueryCapabilityConfiguration(int capability, int radioTech,
- boolean isEnabled) {
- try {
- mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder.");
- }
- }
- }
-
- /**
- * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
- */
- public static class Capabilities {
- protected int mCapabilities = 0;
-
- public Capabilities() {
- }
-
- protected Capabilities(int capabilities) {
- mCapabilities = capabilities;
- }
-
- /**
- * @param capabilities Capabilities to be added to the configuration in the form of a
- * bit mask.
- */
- public void addCapabilities(int capabilities) {
- mCapabilities |= capabilities;
- }
-
- /**
- * @param capabilities Capabilities to be removed to the configuration in the form of a
- * bit mask.
- */
- public void removeCapabilities(int capabilities) {
- mCapabilities &= ~capabilities;
- }
-
- /**
- * @return true if all of the capabilities specified are capable.
- */
- public boolean isCapable(int capabilities) {
- return (mCapabilities & capabilities) == capabilities;
- }
-
- public Capabilities copy() {
- return new Capabilities(mCapabilities);
- }
-
- /**
- * @return a bitmask containing the capability flags directly.
- */
- public int getMask() {
- return mCapabilities;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof Capabilities)) return false;
-
- Capabilities that = (Capabilities) o;
-
- return mCapabilities == that.mCapabilities;
- }
-
- @Override
- public int hashCode() {
- return mCapabilities;
- }
- }
-
- private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
- new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
- private @ImsState int mState = STATE_UNAVAILABLE;
- private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- private Context mContext;
- private final Object mLock = new Object();
- private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
- = new RemoteCallbackList<>();
- private Capabilities mCapabilityStatus = new Capabilities();
-
- public final void initialize(Context context, int slotId) {
- mContext = context;
- mSlotId = slotId;
- }
-
- public final int getFeatureState() {
- synchronized (mLock) {
- return mState;
- }
- }
-
- protected final void setFeatureState(@ImsState int state) {
- synchronized (mLock) {
- if (mState != state) {
- mState = state;
- notifyFeatureState(state);
- }
- }
- }
-
- // Not final for testing, but shouldn't be extended!
- @VisibleForTesting
- public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
- try {
- // If we have just connected, send queued status.
- c.notifyImsFeatureStatus(getFeatureState());
- // Add the callback if the callback completes successfully without a RemoteException.
- synchronized (mLock) {
- mStatusCallbacks.add(c);
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
- }
- }
-
- @VisibleForTesting
- // Not final for testing, but should not be extended!
- public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
- synchronized (mLock) {
- mStatusCallbacks.remove(c);
- }
- }
-
- /**
- * Internal method called by ImsFeature when setFeatureState has changed.
- */
- private void notifyFeatureState(@ImsState int state) {
- synchronized (mLock) {
- for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
- iter.hasNext(); ) {
- IImsFeatureStatusCallback callback = iter.next();
- try {
- Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
- callback.notifyImsFeatureStatus(state);
- } catch (RemoteException e) {
- // remove if the callback is no longer alive.
- iter.remove();
- Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
- }
- }
- }
- sendImsServiceIntent(state);
- }
-
- /**
- * Provide backwards compatibility using deprecated service UP/DOWN intents.
- */
- private void sendImsServiceIntent(@ImsState int state) {
- if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- return;
- }
- Intent intent;
- switch (state) {
- case ImsFeature.STATE_UNAVAILABLE:
- case ImsFeature.STATE_INITIALIZING:
- intent = new Intent(ACTION_IMS_SERVICE_DOWN);
- break;
- case ImsFeature.STATE_READY:
- intent = new Intent(ACTION_IMS_SERVICE_UP);
- break;
- default:
- intent = new Intent(ACTION_IMS_SERVICE_DOWN);
- }
- intent.putExtra(EXTRA_PHONE_ID, mSlotId);
- mContext.sendBroadcast(intent);
- }
-
- public final void addCapabilityCallback(IImsCapabilityCallback c) {
- mCapabilityCallbacks.register(c);
- }
-
- public final void removeCapabilityCallback(IImsCapabilityCallback c) {
- mCapabilityCallbacks.unregister(c);
- }
-
- /**
- * @return the cached capabilities status for this feature.
- */
- @VisibleForTesting
- public Capabilities queryCapabilityStatus() {
- synchronized (mLock) {
- return mCapabilityStatus.copy();
- }
- }
-
- // Called internally to request the change of enabled capabilities.
- @VisibleForTesting
- public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
- IImsCapabilityCallback c) throws RemoteException {
- if (request == null) {
- throw new IllegalArgumentException(
- "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
- }
- changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
- }
-
- /**
- * Called by the ImsFeature when the capabilities status has changed.
- *
- * @param c A {@link Capabilities} containing the new Capabilities status.
- */
- protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
- synchronized (mLock) {
- mCapabilityStatus = c.copy();
- }
- int count = mCapabilityCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < count; i++) {
- try {
- mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
- c.mCapabilities);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
- "callback.");
- }
- }
- } finally {
- mCapabilityCallbacks.finishBroadcast();
- }
- }
-
- /**
- * Features should override this method to receive Capability preference change requests from
- * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
- * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
- * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
- * each failed capability.
- *
- * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
- * enable/disable.
- * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
- * setting a subset of these capabilities fail, using
- * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
- */
- public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
- CapabilityCallbackProxy c);
-
- /**
- * Called when the framework is removing this feature and it needs to be cleaned up.
- */
- public abstract void onFeatureRemoved();
-
- /**
- * Called when the feature has been initialized and communication with the framework is set up.
- * Any attempt by this feature to access the framework before this method is called will return
- * with an {@link IllegalStateException}.
- * The IMS provider should use this method to trigger registration for this feature on the IMS
- * network, if needed.
- */
- public abstract void onFeatureReady();
-
- /**
- * @return Binder instance that the framework will use to communicate with this feature.
- */
- protected abstract IInterface getBinder();
-}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
deleted file mode 100644
index 33aec5d..0000000
--- a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.internal.stub;
-
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsConfigCallback;
-
-import com.android.ims.ImsConfig;
-
-/**
- * Controls the modification of IMS specific configurations. For more information on the supported
- * IMS configuration constants, see {@link ImsConfig}.
- *
- * @hide
- */
-
-public class ImsConfigImplBase {
-
- //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config
- // work first.
- private final IImsConfig mBinder = new IImsConfig.Stub() {
-
- @Override
- public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- ImsConfigImplBase.this.addImsConfigCallback(c);
- }
-
- @Override
- public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- ImsConfigImplBase.this.removeImsConfigCallback(c);
- }
-
- @Override
- public int getConfigInt(int item) throws RemoteException {
- return Integer.MIN_VALUE;
- }
-
- @Override
- public String getConfigString(int item) throws RemoteException {
- return null;
- }
-
- @Override
- public int setConfigInt(int item, int value) throws RemoteException {
- return Integer.MIN_VALUE;
- }
-
- @Override
- public int setConfigString(int item, String value) throws RemoteException {
- return Integer.MIN_VALUE;
- }
- };
-
- public class Callback extends IImsConfigCallback.Stub {
-
- @Override
- public final void onIntConfigChanged(int item, int value) throws RemoteException {
- onConfigChanged(item, value);
- }
-
- @Override
- public final void onStringConfigChanged(int item, String value) throws RemoteException {
- onConfigChanged(item, value);
- }
-
- /**
- * Called when the IMS configuration has changed.
- * @param item the IMS configuration key constant, as defined in ImsConfig.
- * @param value the new integer value of the IMS configuration constant.
- */
- public void onConfigChanged(int item, int value) {
- // Base Implementation
- }
-
- /**
- * Called when the IMS configuration has changed.
- * @param item the IMS configuration key constant, as defined in ImsConfig.
- * @param value the new String value of the IMS configuration constant.
- */
- public void onConfigChanged(int item, String value) {
- // Base Implementation
- }
- }
-
- private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
-
- /**
- * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
- * changes.
- * @param c callback to add.
- */
- private void addImsConfigCallback(IImsConfigCallback c) {
- mCallbacks.register(c);
- }
- /**
- * Removes a {@link Callback} to the list of callbacks notified when a value in the
- * configuration changes.
- *
- * @param c callback to remove.
- */
- private void removeImsConfigCallback(IImsConfigCallback c) {
- mCallbacks.unregister(c);
- }
-
- public final IImsConfig getBinder() {
- return mBinder;
- }
-
- /**
- * Sets the value for IMS service/capabilities parameters by the operator device
- * management entity. It sets the config item value in the provisioned storage
- * from which the master value is derived.
- *
- * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @param value in Integer format.
- * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
- */
- public int setConfig(int item, int value) {
- // Base Implementation - To be overridden.
- return ImsConfig.OperationStatusConstants.FAILED;
- }
-
- /**
- * Sets the value for IMS service/capabilities parameters by the operator device
- * management entity. It sets the config item value in the provisioned storage
- * from which the master value is derived.
- *
- * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @param value in String format.
- * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
- */
- public int setConfig(int item, String value) {
- return ImsConfig.OperationStatusConstants.FAILED;
- }
-
- /**
- * Gets the value for ims service/capabilities parameters from the provisioned
- * value storage.
- *
- * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @return value in Integer format.
- */
- public int getConfigInt(int item) {
- return ImsConfig.OperationStatusConstants.FAILED;
- }
-
- /**
- * Gets the value for ims service/capabilities parameters from the provisioned
- * value storage.
- *
- * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @return value in String format.
- */
- public String getConfigString(int item) {
- return null;
- }
-}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index 80b2f78..7b9fe2b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,105 +16,264 @@
package android.telephony.ims.stub;
+import android.annotation.SystemApi;
import android.os.Message;
import android.os.RemoteException;
+import android.telephony.ims.ImsCallSessionListener;
+import android.telephony.ims.aidl.IImsCallSessionListener;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallSession;
import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsVideoCallProvider;
+import android.telephony.ims.ImsVideoCallProvider;
+
+import dalvik.system.CloseGuard;
/**
- * Base implementation of IImsCallSession, which implements stub versions of the methods in the
- * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ * Base implementation of IImsCallSession, which implements stub versions of the methods available.
*
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSession maintained by other ImsServices.
+ * Override the methods that your implementation of ImsCallSession supports.
*
* @hide
*/
-
-public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+@SystemApi
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsCallSession maintained by other ImsServices.
+public class ImsCallSessionImplBase implements AutoCloseable {
+ /**
+ * Notify USSD Mode.
+ */
+ public static final int USSD_MODE_NOTIFY = 0;
+ /**
+ * Request USSD Mode
+ */
+ public static final int USSD_MODE_REQUEST = 1;
/**
- * Closes the object. This object is not usable after being closed.
+ * Defines IMS call session state.
*/
- @Override
- public void close() throws RemoteException {
+ public static class State {
+ public static final int IDLE = 0;
+ public static final int INITIATED = 1;
+ public static final int NEGOTIATING = 2;
+ public static final int ESTABLISHING = 3;
+ public static final int ESTABLISHED = 4;
+ public static final int RENEGOTIATING = 5;
+ public static final int REESTABLISHING = 6;
+
+ public static final int TERMINATING = 7;
+ public static final int TERMINATED = 8;
+
+ public static final int INVALID = (-1);
+
+ /**
+ * Converts the state to string.
+ */
+ public static String toString(int state) {
+ switch (state) {
+ case IDLE:
+ return "IDLE";
+ case INITIATED:
+ return "INITIATED";
+ case NEGOTIATING:
+ return "NEGOTIATING";
+ case ESTABLISHING:
+ return "ESTABLISHING";
+ case ESTABLISHED:
+ return "ESTABLISHED";
+ case RENEGOTIATING:
+ return "RENEGOTIATING";
+ case REESTABLISHING:
+ return "REESTABLISHING";
+ case TERMINATING:
+ return "TERMINATING";
+ case TERMINATED:
+ return "TERMINATED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private State() {
+ }
}
- /**
- * Gets the call ID of the session.
- *
- * @return the call ID
- */
- @Override
- public String getCallId() throws RemoteException {
- return null;
- }
+ // Non-final for injection by tests
+ private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
+ @Override
+ public void close() {
+ ImsCallSessionImplBase.this.close();
+ }
+
+ @Override
+ public String getCallId() {
+ return ImsCallSessionImplBase.this.getCallId();
+ }
+
+ @Override
+ public ImsCallProfile getCallProfile() {
+ return ImsCallSessionImplBase.this.getCallProfile();
+ }
+
+ @Override
+ public ImsCallProfile getLocalCallProfile() {
+ return ImsCallSessionImplBase.this.getLocalCallProfile();
+ }
+
+ @Override
+ public ImsCallProfile getRemoteCallProfile() {
+ return ImsCallSessionImplBase.this.getRemoteCallProfile();
+ }
+
+ @Override
+ public String getProperty(String name) {
+ return ImsCallSessionImplBase.this.getProperty(name);
+ }
+
+ @Override
+ public int getState() {
+ return ImsCallSessionImplBase.this.getState();
+ }
+
+ @Override
+ public boolean isInCall() {
+ return ImsCallSessionImplBase.this.isInCall();
+ }
+
+ @Override
+ public void setListener(IImsCallSessionListener listener) {
+ ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+ }
+
+ @Override
+ public void setMute(boolean muted) {
+ ImsCallSessionImplBase.this.setMute(muted);
+ }
+
+ @Override
+ public void start(String callee, ImsCallProfile profile) {
+ ImsCallSessionImplBase.this.start(callee, profile);
+ }
+
+ @Override
+ public void startConference(String[] participants, ImsCallProfile profile) throws
+ RemoteException {
+ ImsCallSessionImplBase.this.startConference(participants, profile);
+ }
+
+ @Override
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.accept(callType, profile);
+ }
+
+ @Override
+ public void deflect(String deflectNumber) {
+ ImsCallSessionImplBase.this.deflect(deflectNumber);
+ }
+
+ @Override
+ public void reject(int reason) {
+ ImsCallSessionImplBase.this.reject(reason);
+ }
+
+ @Override
+ public void terminate(int reason) {
+ ImsCallSessionImplBase.this.terminate(reason);
+ }
+
+ @Override
+ public void hold(ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.hold(profile);
+ }
+
+ @Override
+ public void resume(ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.resume(profile);
+ }
+
+ @Override
+ public void merge() {
+ ImsCallSessionImplBase.this.merge();
+ }
+
+ @Override
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ ImsCallSessionImplBase.this.update(callType, profile);
+ }
+
+ @Override
+ public void extendToConference(String[] participants) {
+ ImsCallSessionImplBase.this.extendToConference(participants);
+ }
+
+ @Override
+ public void inviteParticipants(String[] participants) {
+ ImsCallSessionImplBase.this.inviteParticipants(participants);
+ }
+
+ @Override
+ public void removeParticipants(String[] participants) {
+ ImsCallSessionImplBase.this.removeParticipants(participants);
+ }
+
+ @Override
+ public void sendDtmf(char c, Message result) {
+ ImsCallSessionImplBase.this.sendDtmf(c, result);
+ }
+
+ @Override
+ public void startDtmf(char c) {
+ ImsCallSessionImplBase.this.startDtmf(c);
+ }
+
+ @Override
+ public void stopDtmf() {
+ ImsCallSessionImplBase.this.stopDtmf();
+ }
+
+ @Override
+ public void sendUssd(String ussdMessage) {
+ ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+ }
+
+ @Override
+ public IImsVideoCallProvider getVideoCallProvider() {
+ return ImsCallSessionImplBase.this.getVideoCallProvider();
+ }
+
+ @Override
+ public boolean isMultiparty() {
+ return ImsCallSessionImplBase.this.isMultiparty();
+ }
+
+ @Override
+ public void sendRttModifyRequest(ImsCallProfile toProfile) {
+ ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+ }
+
+ @Override
+ public void sendRttModifyResponse(boolean status) {
+ ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+ }
+
+ @Override
+ public void sendRttMessage(String rttMessage) {
+ ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+ }
+ };
/**
- * Gets the call profile that this session is associated with
- *
- * @return the {@link ImsCallProfile} that this session is associated with
+ * @hide
*/
- @Override
- public ImsCallProfile getCallProfile() throws RemoteException {
- return null;
- }
-
- /**
- * Gets the local call profile that this session is associated with
- *
- * @return the local {@link ImsCallProfile} that this session is associated with
- */
- @Override
- public ImsCallProfile getLocalCallProfile() throws RemoteException {
- return null;
- }
-
- /**
- * Gets the remote call profile that this session is associated with
- *
- * @return the remote {@link ImsCallProfile} that this session is associated with
- */
- @Override
- public ImsCallProfile getRemoteCallProfile() throws RemoteException {
- return null;
- }
-
- /**
- * Gets the value associated with the specified property of this session.
- *
- * @return the string value associated with the specified property
- */
- @Override
- public String getProperty(String name) throws RemoteException {
- return null;
- }
-
- /**
- * Gets the session state.
- * The value returned must be one of the states in {@link ImsCallSession.State}.
- *
- * @return the session state
- */
- @Override
- public int getState() throws RemoteException {
- return ImsCallSession.State.INVALID;
- }
-
- /**
- * Checks if the session is in call.
- *
- * @return true if the session is in call, false otherwise
- */
- @Override
- public boolean isInCall() throws RemoteException {
- return false;
+ public final void setListener(IImsCallSessionListener listener) throws RemoteException {
+ setListener(new ImsCallSessionListener(listener));
}
/**
@@ -122,25 +281,87 @@
* can only hold one listener at a time. Subsequent calls to this method
* override the previous listener.
*
- * @param listener to listen to the session events of this object
+ * @param listener {@link ImsCallSessionListener} used to notify the framework of updates
+ * to the ImsCallSession
+ */
+ public void setListener(ImsCallSessionListener listener) {
+ }
+
+ /**
+ * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
*/
@Override
- public void setListener(IImsCallSessionListener listener) throws RemoteException {
+ public void close() {
+
+ }
+
+ /**
+ * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+ */
+ public String getCallId() {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+ * with.
+ */
+ public ImsCallProfile getCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ public ImsCallProfile getLocalCallProfile() {
+ return null;
+ }
+
+ /**
+ * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+ * associated with.
+ */
+ public ImsCallProfile getRemoteCallProfile() {
+ return null;
+ }
+
+ /**
+ * @param name The String extra key.
+ * @return The string extra value associated with the specified property.
+ */
+ public String getProperty(String name) {
+ return null;
+ }
+
+ /**
+ * @return The {@link ImsCallSessionImplBase} state, defined in
+ * {@link ImsCallSessionImplBase.State}.
+ */
+ public int getState() {
+ return ImsCallSessionImplBase.State.INVALID;
+ }
+
+ /**
+ * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+ */
+ public boolean isInCall() {
+ return false;
}
/**
* Mutes or unmutes the mic for the active call.
*
- * @param muted true if the call is muted, false otherwise
+ * @param muted true if the call should be muted, false otherwise.
*/
- @Override
- public void setMute(boolean muted) throws RemoteException {
+ public void setMute(boolean muted) {
}
/**
- * Initiates an IMS call with the specified target and call profile.
- * The session listener set in {@link #setListener} is called back upon defined session events.
- * The method is only valid to call when the session state is in
+ * Initiates an IMS call with the specified number and call profile.
+ * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+ * defined session events.
+ * Only valid to call when the session state is in
* {@link ImsCallSession.State#IDLE}.
*
* @param callee dialed string to make the call to
@@ -149,13 +370,13 @@
* @see {@link ImsCallSession.Listener#callSessionStarted},
* {@link ImsCallSession.Listener#callSessionStartFailed}
*/
- @Override
- public void start(String callee, ImsCallProfile profile) throws RemoteException {
+ public void start(String callee, ImsCallProfile profile) {
}
/**
* Initiates an IMS call with the specified participants and call profile.
- * The session listener set in {@link #setListener} is called back upon defined session events.
+ * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+ * defined session events.
* The method is only valid to call when the session state is in
* {@link ImsCallSession.State#IDLE}.
*
@@ -165,9 +386,7 @@
* @see {@link ImsCallSession.Listener#callSessionStarted},
* {@link ImsCallSession.Listener#callSessionStartFailed}
*/
- @Override
- public void startConference(String[] participants, ImsCallProfile profile)
- throws RemoteException {
+ public void startConference(String[] participants, ImsCallProfile profile) {
}
/**
@@ -177,31 +396,34 @@
* @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
* @see {@link ImsCallSession.Listener#callSessionStarted}
*/
- @Override
- public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ }
+
+ /**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ public void deflect(String deflectNumber) {
}
/**
* Rejects an incoming call or session update.
*
- * @param reason reason code to reject an incoming call, defined in
- * com.android.ims.ImsReasonInfo
+ * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
* {@link ImsCallSession.Listener#callSessionStartFailed}
*/
- @Override
- public void reject(int reason) throws RemoteException {
+ public void reject(int reason) {
}
/**
* Terminates a call.
*
- * @param reason reason code to terminate a call, defined in
- * com.android.ims.ImsReasonInfo
+ * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
*
* @see {@link ImsCallSession.Listener#callSessionTerminated}
*/
- @Override
- public void terminate(int reason) throws RemoteException {
+ public void terminate(int reason) {
}
/**
@@ -212,8 +434,7 @@
* @see {@link ImsCallSession.Listener#callSessionHeld},
* {@link ImsCallSession.Listener#callSessionHoldFailed}
*/
- @Override
- public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+ public void hold(ImsStreamMediaProfile profile) {
}
/**
@@ -224,12 +445,11 @@
* @see {@link ImsCallSession.Listener#callSessionResumed},
* {@link ImsCallSession.Listener#callSessionResumeFailed}
*/
- @Override
- public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+ public void resume(ImsStreamMediaProfile profile) {
}
/**
- * Merges the active & hold call. When the merge starts,
+ * Merges the active and held call. When the merge starts,
* {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
* {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
* successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
@@ -239,8 +459,7 @@
* {@link ImsCallSession.Listener#callSessionMergeComplete},
* {@link ImsCallSession.Listener#callSessionMergeFailed}
*/
- @Override
- public void merge() throws RemoteException {
+ public void merge() {
}
/**
@@ -251,8 +470,7 @@
* @see {@link ImsCallSession.Listener#callSessionUpdated},
* {@link ImsCallSession.Listener#callSessionUpdateFailed}
*/
- @Override
- public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ public void update(int callType, ImsStreamMediaProfile profile) {
}
/**
@@ -263,8 +481,7 @@
* @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
* {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
*/
- @Override
- public void extendToConference(String[] participants) throws RemoteException {
+ public void extendToConference(String[] participants) {
}
/**
@@ -274,8 +491,7 @@
* @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
* {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
*/
- @Override
- public void inviteParticipants(String[] participants) throws RemoteException {
+ public void inviteParticipants(String[] participants) {
}
/**
@@ -285,8 +501,7 @@
* @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
* {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
*/
- @Override
- public void removeParticipants(String[] participants) throws RemoteException {
+ public void removeParticipants(String[] participants) {
}
/**
@@ -296,8 +511,7 @@
*
* @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
*/
- @Override
- public void sendDtmf(char c, Message result) throws RemoteException {
+ public void sendDtmf(char c, Message result) {
}
/**
@@ -307,15 +521,13 @@
*
* @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
*/
- @Override
- public void startDtmf(char c) throws RemoteException {
+ public void startDtmf(char c) {
}
/**
* Stop a DTMF code.
*/
- @Override
- public void stopDtmf() throws RemoteException {
+ public void stopDtmf() {
}
/**
@@ -323,17 +535,23 @@
*
* @param ussdMessage USSD message to send
*/
- @Override
- public void sendUssd(String ussdMessage) throws RemoteException {
+ public void sendUssd(String ussdMessage) {
}
/**
- * Returns a binder for the video call provider implementation contained within the IMS service
- * process. This binder is used by the VideoCallProvider subclass in Telephony which
- * intermediates between the propriety implementation and Telecomm/InCall.
+ * See {@link #getImsVideoCallProvider()}, used directly in older ImsService implementations.
+ * @hide
*/
- @Override
- public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+ public IImsVideoCallProvider getVideoCallProvider() {
+ ImsVideoCallProvider provider = getImsVideoCallProvider();
+ return provider != null ? provider.getInterface() : null;
+ }
+
+ /**
+ * @return The {@link ImsVideoCallProvider} implementation contained within the IMS service
+ * process.
+ */
+ public ImsVideoCallProvider getImsVideoCallProvider() {
return null;
}
@@ -341,8 +559,7 @@
* Determines if the current session is multiparty.
* @return {@code True} if the session is multiparty.
*/
- @Override
- public boolean isMultiparty() throws RemoteException {
+ public boolean isMultiparty() {
return false;
}
@@ -350,16 +567,13 @@
* Device issues RTT modify request
* @param toProfile The profile with requested changes made
*/
- @Override
public void sendRttModifyRequest(ImsCallProfile toProfile) {
}
/**
* Device responds to Remote RTT modify request
- * @param status true Accepted the request
- * false Declined the request
+ * @param status true if the the request was accepted or false of the request is defined.
*/
- @Override
public void sendRttModifyResponse(boolean status) {
}
@@ -367,7 +581,16 @@
* Device sends RTT message
* @param rttMessage RTT message to be sent
*/
- @Override
public void sendRttMessage(String rttMessage) {
}
+
+ /** @hide */
+ public IImsCallSession getServiceImpl() {
+ return mServiceImpl;
+ }
+
+ /** @hide */
+ public void setServiceImpl(IImsCallSession serviceImpl) {
+ mServiceImpl = serviceImpl;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
deleted file mode 100644
index 6c18935..0000000
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.stub;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-
-/**
- * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
- * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
- * ImsCallSessionListener supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
- /**
- * Notifies the result of the basic session operation (setup / terminate).
- */
- @Override
- public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- @Override
- public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- /**
- * Notifies the result of the call hold/resume operation.
- */
- @Override
- public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- @Override
- public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- @Override
- public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
- // no-op
- }
-
- /**
- * Notifies the result of call merge operation.
- */
- @Override
- public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
- ImsCallProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionMergeComplete(IImsCallSession session) {
- // no-op
- }
-
- @Override
- public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- /**
- * Notifies the result of call upgrade / downgrade or any other call
- * updates.
- */
- @Override
- public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- @Override
- public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
- // no-op
- }
-
- /**
- * Notifies the result of conference extension.
- */
- @Override
- public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
- ImsCallProfile profile) {
- // no-op
- }
-
- @Override
- public void callSessionConferenceExtendFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- @Override
- public void callSessionConferenceExtendReceived(IImsCallSession session,
- IImsCallSession newSession,
- ImsCallProfile profile) {
- // no-op
- }
-
- /**
- * Notifies the result of the participant invitation / removal to/from the
- * conference session.
- */
- @Override
- public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
- // no-op
- }
-
- @Override
- public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- @Override
- public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
- // no-op
- }
-
- @Override
- public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
- ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- /**
- * Notifies the changes of the conference info. the conference session.
- */
- @Override
- public void callSessionConferenceStateUpdated(IImsCallSession session,
- ImsConferenceState state) {
- // no-op
- }
-
- /**
- * Notifies the incoming USSD message.
- */
- @Override
- public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
- String ussdMessage) {
- // no-op
- }
-
- /**
- * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
- * handover from one radio technology to another.
- * @param session
- * @param srcAccessTech The source radio access technology; one of the access technology
- * constants defined in {@link android.telephony.ServiceState}. For
- * example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
- * @param targetAccessTech The target radio access technology; one of the access technology
- * constants defined in {@link android.telephony.ServiceState}. For
- * example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
- */
- @Override
- public void callSessionMayHandover(IImsCallSession session, int srcAccessTech,
- int targetAccessTech) {
- // no-op
- }
-
- /**
- * Notifies of handover information for this call
- */
- @Override
- public void callSessionHandover(IImsCallSession session, int srcAccessTech,
- int targetAccessTech,
- ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- @Override
- public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
- int targetAccessTech,
- ImsReasonInfo reasonInfo) {
- // no-op
- }
-
- /**
- * Notifies the TTY mode change by remote party.
- *
- * @param mode one of the following: -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
- * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
- */
- @Override
- public void callSessionTtyModeReceived(IImsCallSession session, int mode) {
- // no-op
- }
-
- /**
- * Notifies of a change to the multiparty state for this
- * {@code ImsCallSession}.
- *
- * @param session The call session.
- * @param isMultiParty {@code true} if the session became multiparty,
- * {@code false} otherwise.
- */
- @Override
- public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
- // no-op
- }
-
- /**
- * Notifies the supplementary service information for the current session.
- */
- @Override
- public void callSessionSuppServiceReceived(IImsCallSession session,
- ImsSuppServiceNotification suppSrvNotification) {
- // no-op
- }
-
- /**
- * Received RTT modify request from Remote Party
- * @param session The call session.
- * @param callProfile ImsCallProfile with updated attribute
- */
- @Override
- public void callSessionRttModifyRequestReceived(IImsCallSession session,
- ImsCallProfile callProfile) {
- // no-op
- }
-
- /**
- * Received response for RTT modify request
- * @param status true : Accepted the request
- * false : Declined the request
- */
- @Override
- public void callSessionRttModifyResponseReceived(int status) {
- // no -op
- }
-
- /**
- * Device received RTT message from Remote UE
- * @param rttMessage RTT message received
- */
- @Override
- public void callSessionRttMessageReceived(String rttMessage) {
- // no-op
- }
-}
-
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 5a4db99..c6e5ddb 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,135 +16,400 @@
package android.telephony.ims.stub;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.util.Log;
import com.android.ims.ImsConfig;
-import com.android.ims.ImsConfigListener;
-import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
/**
- * Base implementation of ImsConfig, which implements stub versions of the methods
- * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports.
+ * Controls the modification of IMS specific configurations. For more information on the supported
+ * IMS configuration constants, see {@link ImsConfig}.
*
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsConfig maintained by other ImsServices.
- *
- * Provides APIs to get/set the IMS service feature/capability/parameters.
- * The config items include:
- * 1) Items provisioned by the operator.
- * 2) Items configured by user. Mainly service feature class.
- *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
* @hide
*/
+@SystemApi
+public class ImsConfigImplBase {
-public class ImsConfigImplBase extends IImsConfig.Stub {
+ private static final String TAG = "ImsConfigImplBase";
/**
- * Gets the value for ims service/capabilities parameters from the provisioned
- * value storage. Synchronous blocking call.
+ * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+ * in order to get/set configuration parameters.
*
- * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @return value in Integer format.
+ * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+ * with actual implementations from vendors. This class caches provisioned values from
+ * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+ * it first checks cache layer. If missed, it will call the vendor implementation of
+ * ImsConfigImplBase API.
+ * and cache the return value if the set succeeds.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
*/
- @Override
- public int getProvisionedValue(int item) throws RemoteException {
- return -1;
+ @VisibleForTesting
+ static public class ImsConfigStub extends IImsConfig.Stub {
+ WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+ private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+ private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+ @VisibleForTesting
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
+ mImsConfigImplBaseWeakReference =
+ new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+ }
+
+ @Override
+ public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+ getImsConfigImpl().addImsConfigCallback(c);
+ }
+
+ @Override
+ public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+ getImsConfigImpl().removeImsConfigCallback(c);
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call ImsConfigImplBase.getConfigInt.
+ * Synchronous blocking call.
+ *
+ * @param item integer key
+ * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+ * unavailable.
+ */
+ @Override
+ public synchronized int getConfigInt(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ int retVal = getImsConfigImpl().getConfigInt(item);
+ if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call #ImsConfigImplBase.getConfigString.
+ * Synchronous blocking call.
+ *
+ * @param item integer key
+ * @return value in String format.
+ */
+ @Override
+ public synchronized String getConfigString(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedStringValue.get(item);
+ } else {
+ String retVal = getImsConfigImpl().getConfigString(item);
+ if (retVal != null) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived, and write it into local cache.
+ * Synchronous blocking call.
+ *
+ * @param item integer key
+ * @param value in Integer format.
+ * @return the result of setting the configuration value, defined as either
+ * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ */
+ @Override
+ public synchronized int setConfigInt(int item, int value) throws RemoteException {
+ mProvisionedIntValue.remove(item);
+ int retVal = getImsConfigImpl().setConfig(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ } else {
+ Log.d(TAG, "Set provision value of " + item +
+ " to " + value + " failed with error code " + retVal);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived, and write it into local cache.
+ * Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return the result of setting the configuration value, defined as either
+ * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ */
+ @Override
+ public synchronized int setConfigString(int item, String value)
+ throws RemoteException {
+ mProvisionedStringValue.remove(item);
+ int retVal = getImsConfigImpl().setConfig(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, value, true);
+ }
+
+ return retVal;
+ }
+
+ private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+ ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+ if (ref == null) {
+ throw new RemoteException("Fail to get ImsConfigImpl");
+ } else {
+ return ref;
+ }
+ }
+
+ private void notifyImsConfigChanged(int item, int value) throws RemoteException {
+ getImsConfigImpl().notifyConfigChanged(item, value);
+ }
+
+ private void notifyImsConfigChanged(int item, String value) throws RemoteException {
+ getImsConfigImpl().notifyConfigChanged(item, value);
+ }
+
+ protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
+ throws RemoteException {
+ mProvisionedIntValue.put(item, value);
+ if (notifyChange) {
+ notifyImsConfigChanged(item, value);
+ }
+ }
+
+ protected synchronized void updateCachedValue(int item, String value,
+ boolean notifyChange) throws RemoteException {
+ mProvisionedStringValue.put(item, value);
+ if (notifyChange) {
+ notifyImsConfigChanged(item, value);
+ }
+ }
}
/**
- * Gets the value for ims service/capabilities parameters from the provisioned
- * value storage. Synchronous blocking call.
- *
- * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @return value in String format.
+ * Callback that the framework uses for receiving Configuration change updates.
+ * {@hide}
*/
- @Override
- public String getProvisionedStringValue(int item) throws RemoteException {
+ public static class Callback extends IImsConfigCallback.Stub {
+
+ @Override
+ public final void onIntConfigChanged(int item, int value) throws RemoteException {
+ onConfigChanged(item, value);
+ }
+
+ @Override
+ public final void onStringConfigChanged(int item, String value) throws RemoteException {
+ onConfigChanged(item, value);
+ }
+
+ /**
+ * Called when the IMS configuration has changed.
+ * @param item the IMS configuration key constant, as defined in ImsConfig.
+ * @param value the new integer value of the IMS configuration constant.
+ */
+ public void onConfigChanged(int item, int value) {
+ // Base Implementation
+ }
+
+ /**
+ * Called when the IMS configuration has changed.
+ * @param item the IMS configuration key constant, as defined in ImsConfig.
+ * @param value the new String value of the IMS configuration constant.
+ */
+ public void onConfigChanged(int item, String value) {
+ // Base Implementation
+ }
+ }
+
+ /**
+ * The configuration requested resulted in an unknown result. This may happen if the
+ * IMS configurations are unavailable.
+ */
+ public static final int CONFIG_RESULT_UNKNOWN = -1;
+ /**
+ * Setting the configuration value completed.
+ */
+ public static final int CONFIG_RESULT_SUCCESS = 0;
+ /**
+ * Setting the configuration value failed.
+ */
+ public static final int CONFIG_RESULT_FAILED = 1;
+
+ private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
+ ImsConfigStub mImsConfigStub;
+
+ /**
+ * Used for compatibility between older versions of the ImsService.
+ * @hide
+ */
+ public ImsConfigImplBase(Context context) {
+ mImsConfigStub = new ImsConfigStub(this);
+ }
+
+ public ImsConfigImplBase() {
+ mImsConfigStub = new ImsConfigStub(this);
+ }
+
+ /**
+ * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
+ * changes.
+ * @param c callback to add.
+ */
+ private void addImsConfigCallback(IImsConfigCallback c) {
+ mCallbacks.register(c);
+ }
+ /**
+ * Removes a {@link Callback} to the list of callbacks notified when a value in the
+ * configuration changes.
+ *
+ * @param c callback to remove.
+ */
+ private void removeImsConfigCallback(IImsConfigCallback c) {
+ mCallbacks.unregister(c);
+ }
+
+ /**
+ * @param item
+ * @param value
+ */
+ private final void notifyConfigChanged(int item, int value) {
+ // can be null in testing
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.broadcast(c -> {
+ try {
+ c.onIntConfigChanged(item, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
+ }
+ });
+ }
+
+ private void notifyConfigChanged(int item, String value) {
+ // can be null in testing
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.broadcast(c -> {
+ try {
+ c.onStringConfigChanged(item, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
+ }
+ });
+ }
+
+ /**
+ * @hide
+ */
+ public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded.
+ * This should only be used when the IMS implementer implicitly changed provisioned values.
+ *
+ * @param item an integer key.
+ * @param value in Integer format.
+ */
+ public final void notifyProvisionedValueChanged(int item, int value) {
+ try {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
+ }
+ }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded.
+ * This should only be used when the IMS implementer implicitly changed provisioned values.
+ *
+ * @param item an integer key.
+ * @param value in String format.
+ */
+ public final void notifyProvisionedValueChanged(int item, String value) {
+ try {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
+ }
+ }
+
+ /**
+ * Sets the configuration value for this ImsService.
+ *
+ * @param item an integer key.
+ * @param value an integer containing the configuration value.
+ * @return the result of setting the configuration value, defined as either
+ * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ */
+ public int setConfig(int item, int value) {
+ // Base Implementation - To be overridden.
+ return CONFIG_RESULT_FAILED;
+ }
+
+ /**
+ * Sets the configuration value for this ImsService.
+ *
+ * @param item an integer key.
+ * @param value a String containing the new configuration value.
+ * @return Result of setting the configuration value, defined as either
+ * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+ */
+ public int setConfig(int item, String value) {
+ // Base Implementation - To be overridden.
+ return CONFIG_RESULT_FAILED;
+ }
+
+ /**
+ * Gets the currently stored value configuration value from the ImsService for {@code item}.
+ *
+ * @param item an integer key.
+ * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+ * unavailable.
+ */
+ public int getConfigInt(int item) {
+ // Base Implementation - To be overridden.
+ return CONFIG_RESULT_UNKNOWN;
+ }
+
+ /**
+ * Gets the currently stored value configuration value from the ImsService for {@code item}.
+ *
+ * @param item an integer key.
+ * @return configuration value, stored in String format or {@code null} if unavailable.
+ */
+ public String getConfigString(int item) {
+ // Base Implementation - To be overridden.
return null;
}
-
- /**
- * Sets the value for IMS service/capabilities parameters by the operator device
- * management entity. It sets the config item value in the provisioned storage
- * from which the master value is derived. Synchronous blocking call.
- *
- * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @param value in Integer format.
- * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
- */
- @Override
- public int setProvisionedValue(int item, int value) throws RemoteException {
- return ImsConfig.OperationStatusConstants.FAILED;
- }
-
- /**
- * Sets the value for IMS service/capabilities parameters by the operator device
- * management entity. It sets the config item value in the provisioned storage
- * from which the master value is derived. Synchronous blocking call.
- *
- * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @param value in String format.
- * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
- */
- @Override
- public int setProvisionedStringValue(int item, String value) throws RemoteException {
- return ImsConfig.OperationStatusConstants.FAILED;
- }
-
- /**
- * Gets the value of the specified IMS feature item for specified network type.
- * This operation gets the feature config value from the master storage (i.e. final
- * value). Asynchronous non-blocking call.
- *
- * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
- * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
- * @param listener feature value returned asynchronously through listener.
- */
- @Override
- public void getFeatureValue(int feature, int network, ImsConfigListener listener)
- throws RemoteException {
- }
-
- /**
- * Sets the value for IMS feature item for specified network type.
- * This operation stores the user setting in setting db from which master db
- * is derived.
- *
- * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
- * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
- * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
- * @param listener, provided if caller needs to be notified for set result.
- */
- @Override
- public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
- throws RemoteException {
- }
-
- /**
- * Gets the value for IMS VoLTE provisioned.
- * This should be the same as the operator provisioned value if applies.
- */
- @Override
- public boolean getVolteProvisioned() throws RemoteException {
- return false;
- }
-
- /**
- * Gets the value for IMS feature item video quality.
- *
- * @param listener Video quality value returned asynchronously through listener.
- */
- @Override
- public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
- }
-
- /**
- * Sets the value for IMS feature item video quality.
- *
- * @param quality, defines the value of video quality.
- * @param listener, provided if caller needs to be notified for set result.
- */
- @Override
- public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
- }
-}
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 89f95ff..06c35ea 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -16,7 +16,9 @@
package android.telephony.ims.stub;
+import android.annotation.SystemApi;
import android.os.RemoteException;
+import android.util.Log;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsEcbmListener;
@@ -30,22 +32,65 @@
*
* @hide
*/
+@SystemApi
+public class ImsEcbmImplBase {
+ private static final String TAG = "ImsEcbmImplBase";
-public class ImsEcbmImplBase extends IImsEcbm.Stub {
+ private IImsEcbmListener mListener;
+ private IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+ @Override
+ public void setListener(IImsEcbmListener listener) {
+ mListener = listener;
+ }
- /**
- * Sets the listener.
- */
- @Override
- public void setListener(IImsEcbmListener listener) throws RemoteException {
+ @Override
+ public void exitEmergencyCallbackMode() {
+ ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+ }
+ };
+ /** @hide */
+ public IImsEcbm getImsEcbm() {
+ return mImsEcbm;
}
/**
- * Requests Modem to come out of ECBM mode
+ * This method should be implemented by the IMS provider. Framework will trigger this method to
+ * request to come out of ECBM mode
*/
- @Override
- public void exitEmergencyCallbackMode() throws RemoteException {
+ public void exitEmergencyCallbackMode() {
+ Log.d(TAG, "exitEmergencyCallbackMode() not implemented");
+ }
+ /**
+ * Notifies the framework when the device enters Emergency Callback Mode.
+ *
+ * @throws RuntimeException if the connection to the framework is not available.
+ */
+ public final void enteredEcbm() {
+ Log.d(TAG, "Entered ECBM.");
+ if (mListener != null) {
+ try {
+ mListener.enteredECBM();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Notifies the framework when the device exits Emergency Callback Mode.
+ *
+ * @throws RuntimeException if the connection to the framework is not available.
+ */
+ public final void exitedEcbm() {
+ Log.d(TAG, "Exited ECBM.");
+ if (mListener != null) {
+ try {
+ mListener.exitedECBM();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.aidl
similarity index 93%
rename from telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
rename to telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.aidl
index e890cf8..e2ae0e8 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.aidl
@@ -14,6 +14,6 @@
* limitations under the License
*/
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
parcelable ImsFeatureConfiguration;
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
similarity index 77%
rename from telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
rename to telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
index 244c957..98b67c3 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,29 +14,34 @@
* limitations under the License
*/
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.ims.internal.feature.ImsFeature;
+import android.telephony.ims.feature.ImsFeature;
import android.util.ArraySet;
-import java.util.Arrays;
import java.util.Set;
/**
* Container class for IMS Feature configuration. This class contains the features that the
- * ImsService supports, which are defined in {@link ImsFeature.FeatureType}.
+ * ImsService supports, which are defined in {@link ImsFeature} as
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ *
* @hide
*/
-public class ImsFeatureConfiguration implements Parcelable {
+@SystemApi
+public final class ImsFeatureConfiguration implements Parcelable {
/**
* Features that this ImsService supports.
*/
private final Set<Integer> mFeatures;
/**
- * Creates an ImsFeatureConfiguration with the features
+ * Builder for {@link ImsFeatureConfiguration} that makes adding supported {@link ImsFeature}s
+ * easier.
*/
public static class Builder {
ImsFeatureConfiguration mConfig;
@@ -72,7 +77,10 @@
* Configuration of the ImsService, which describes which features the ImsService supports
* (for registration).
* @param features an array of feature integers defined in {@link ImsFeature} that describe
- * which features this ImsService supports.
+ * which features this ImsService supports. Supported values are
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ * @hide
*/
public ImsFeatureConfiguration(int[] features) {
mFeatures = new ArraySet<>();
@@ -85,7 +93,9 @@
}
/**
- * @return an int[] containing the features that this ImsService supports.
+ * @return an int[] containing the features that this ImsService supports. Supported values are
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
*/
public int[] getServiceFeatures() {
return mFeatures.stream().mapToInt(i->i).toArray();
@@ -95,6 +105,7 @@
mFeatures.add(feature);
}
+ /** @hide */
protected ImsFeatureConfiguration(Parcel in) {
int[] features = in.createIntArray();
if (features != null) {
@@ -130,16 +141,23 @@
dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
}
+ /**
+ * @hide
+ */
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ImsFeatureConfiguration)) return false;
- ImsFeatureConfiguration that = (ImsFeatureConfiguration) o;
+ ImsFeatureConfiguration
+ that = (ImsFeatureConfiguration) o;
return mFeatures.equals(that.mFeatures);
}
+ /**
+ * @hide
+ */
@Override
public int hashCode() {
return mFeatures.hashCode();
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 05da9da..ce2d89a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -16,11 +16,16 @@
package android.telephony.ims.stub;
+import android.annotation.SystemApi;
import android.os.RemoteException;
+import android.util.Log;
+import android.telephony.ims.ImsExternalCallState;
import com.android.ims.internal.IImsExternalCallStateListener;
import com.android.ims.internal.IImsMultiEndpoint;
+import java.util.List;
+
/**
* Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
* in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
@@ -31,23 +36,49 @@
*
* @hide
*/
+@SystemApi
+public class ImsMultiEndpointImplBase {
+ private static final String TAG = "MultiEndpointImplBase";
-public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+ private IImsExternalCallStateListener mListener;
+ private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+ @Override
+ public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+ mListener = listener;
+ }
- /**
- * Sets the listener.
- */
- @Override
- public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+ @Override
+ public void requestImsExternalCallStateInfo() throws RemoteException {
+ ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+ }
+ };
+ /** @hide */
+ public IImsMultiEndpoint getIImsMultiEndpoint() {
+ return mImsMultiEndpoint;
}
/**
- * Query API to get the latest Dialog Event Package information
- * Should be invoked only after setListener is done
+ * Notifies framework when Dialog Event Package update is received
+ *
+ * @throws RuntimeException if the connection to the framework is not available.
*/
- @Override
- public void requestImsExternalCallStateInfo() throws RemoteException {
+ public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
+ Log.d(TAG, "ims external call state update triggered.");
+ if (mListener != null) {
+ try {
+ mListener.onImsExternalCallStateUpdate(externalCallDialogs);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ /**
+ * This method should be implemented by the IMS provider. Framework will trigger this to get the
+ * latest Dialog Event Package information. Should
+ */
+ public void requestImsExternalCallStateInfo() {
+ Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
}
}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
similarity index 75%
rename from telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
rename to telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 558b009..4334d3a 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -14,16 +14,20 @@
* limitations under the License
*/
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.net.Uri;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsRegistration;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,11 +37,14 @@
* registration for this ImsService has changed status.
* @hide
*/
-
+@SystemApi
public class ImsRegistrationImplBase {
private static final String LOG_TAG = "ImsRegistrationImplBase";
+ /**
+ * @hide
+ */
// Defines the underlying radio technology type that we have registered for IMS over.
@IntDef(flag = true,
value = {
@@ -62,23 +69,25 @@
// Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
// state.
+ // The unknown state is set as the initialization state. This is so that we do not call back
+ // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
+ // yet.
+ private static final int REGISTRATION_STATE_UNKNOWN = -1;
private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
private static final int REGISTRATION_STATE_REGISTERING = 1;
private static final int REGISTRATION_STATE_REGISTERED = 2;
-
/**
* Callback class for receiving Registration callback events.
+ * @hide
*/
- public static class Callback extends IImsRegistrationCallback.Stub {
-
+ public static class Callback {
/**
* Notifies the framework when the IMS Provider is connected to the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
- @Override
public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
}
@@ -88,7 +97,6 @@
* @param imsRadioTech the radio access technology. Valid values are defined in
* {@link ImsRegistrationTech}.
*/
- @Override
public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
}
@@ -97,7 +105,6 @@
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
- @Override
public void onDeregistered(ImsReasonInfo info) {
}
@@ -108,10 +115,19 @@
* @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
- @Override
public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
}
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ */
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+
+ }
}
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@@ -139,10 +155,13 @@
private @ImsRegistrationTech
int mConnectionType = REGISTRATION_TECH_NONE;
// Locked on mLock
- private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
- // Locked on mLock
- private ImsReasonInfo mLastDisconnectCause;
+ private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
+ // Locked on mLock, create unspecified disconnect cause.
+ private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
+ /**
+ * @hide
+ */
public final IImsRegistration getBinder() {
return mBinder;
}
@@ -159,8 +178,8 @@
/**
* Notify the framework that the device is connected to the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationTech}.
+ * @param imsRadioTech the radio access technology. Valid values are defined as
+ * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
*/
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
@@ -177,8 +196,8 @@
/**
* Notify the framework that the device is trying to connect the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationTech}.
+ * @param imsRadioTech the radio access technology. Valid values are defined as
+ * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
*/
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
@@ -209,6 +228,13 @@
});
}
+ /**
+ * Notify the framework that the handover from the current radio technology to the technology
+ * defined in {@code imsRadioTech} has failed.
+ * @param imsRadioTech The technology that has failed to be changed. Valid values are
+ * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
+ * @param info The {@link ImsReasonInfo} for the failure to change technology.
+ */
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
mCallbacks.broadcast((c) -> {
@@ -221,6 +247,22 @@
});
}
+ /**
+ * The this device's subscriber associated {@link Uri}s have changed, which are used to filter
+ * out this device's {@link Uri}s during conference calling.
+ * @param uris
+ */
+ public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ mCallbacks.broadcast((c) -> {
+ try {
+ c.onSubscriberAssociatedUriChanged(uris);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
private void updateToState(@ImsRegistrationTech int connType, int newState) {
synchronized (mLock) {
mConnectionType = connType;
@@ -241,7 +283,13 @@
}
}
- private @ImsRegistrationTech int getConnectionType() {
+ /**
+ * @return the current registration connection type. Valid values are
+ * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}
+ * @hide
+ */
+ @VisibleForTesting
+ public final @ImsRegistrationTech int getConnectionType() {
synchronized (mLock) {
return mConnectionType;
}
@@ -271,6 +319,10 @@
c.onRegistered(getConnectionType());
break;
}
+ case REGISTRATION_STATE_UNKNOWN: {
+ // Do not callback if the state has not been updated yet by the ImsService.
+ break;
+ }
}
}
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
new file mode 100644
index 0000000..bf89533
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -0,0 +1,312 @@
+/*
+ * 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.stub;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for SMS over IMS.
+ *
+ * Any service wishing to provide SMS over IMS should extend this class and implement all methods
+ * that the service supports.
+ *
+ * @hide
+ */
+@SystemApi
+public class ImsSmsImplBase {
+ private static final String LOG_TAG = "SmsImplBase";
+
+ /** @hide */
+ @IntDef({
+ SEND_STATUS_OK,
+ SEND_STATUS_ERROR,
+ SEND_STATUS_ERROR_RETRY,
+ SEND_STATUS_ERROR_FALLBACK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SendStatusResult {}
+ /**
+ * Message was sent successfully.
+ */
+ public static final int SEND_STATUS_OK = 1;
+
+ /**
+ * IMS provider failed to send the message and platform should not retry falling back to sending
+ * the message using the radio.
+ */
+ public static final int SEND_STATUS_ERROR = 2;
+
+ /**
+ * IMS provider failed to send the message and platform should retry again after setting TP-RD
+ * bit to high.
+ */
+ public static final int SEND_STATUS_ERROR_RETRY = 3;
+
+ /**
+ * IMS provider failed to send the message and platform should retry falling back to sending
+ * the message using the radio.
+ */
+ public static final int SEND_STATUS_ERROR_FALLBACK = 4;
+
+ /** @hide */
+ @IntDef({
+ DELIVER_STATUS_OK,
+ DELIVER_STATUS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeliverStatusResult {}
+ /**
+ * Message was delivered successfully.
+ */
+ public static final int DELIVER_STATUS_OK = 1;
+
+ /**
+ * Message was not delivered.
+ */
+ public static final int DELIVER_STATUS_ERROR = 2;
+
+ /** @hide */
+ @IntDef({
+ STATUS_REPORT_STATUS_OK,
+ STATUS_REPORT_STATUS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusReportResult {}
+
+ /**
+ * Status Report was set successfully.
+ */
+ public static final int STATUS_REPORT_STATUS_OK = 1;
+
+ /**
+ * Error while setting status report.
+ */
+ public static final int STATUS_REPORT_STATUS_ERROR = 2;
+
+
+ // Lock for feature synchronization
+ private final Object mLock = new Object();
+ private IImsSmsListener mListener;
+
+ /**
+ * Registers a listener responsible for handling tasks like delivering messages.
+ *
+ * @param listener listener to register.
+ *
+ * @hide
+ */
+ public final void registerSmsListener(IImsSmsListener listener) {
+ synchronized (mLock) {
+ mListener = listener;
+ }
+ }
+
+ /**
+ * This method will be triggered by the platform when the user attempts to send an SMS. This
+ * method should be implemented by the IMS providers to provide implementation of sending an SMS
+ * over IMS.
+ *
+ * @param token unique token generated by the platform that should be used when triggering
+ * callbacks for this specific message.
+ * @param messageRef the message reference.
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param smsc the Short Message Service Center address.
+ * @param isRetry whether it is a retry of an already attempted message or not.
+ * @param pdu PDUs representing the contents of the message.
+ */
+ public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+ byte[] pdu) {
+ // Base implementation returns error. Should be overridden.
+ try {
+ onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
+ SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
+ }
+ }
+
+ /**
+ * This method will be triggered by the platform after
+ * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
+ * provider.
+ *
+ * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+ * @param result result of delivering the message. Valid values are:
+ * {@link #DELIVER_STATUS_OK},
+ * {@link #DELIVER_STATUS_ERROR}
+ * @param messageRef the message reference
+ */
+ public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
+ Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
+ }
+
+ /**
+ * This method will be triggered by the platform after
+ * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the
+ * result to the IMS provider.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param result result of delivering the message. Valid values are:
+ * {@link #STATUS_REPORT_STATUS_OK},
+ * {@link #STATUS_REPORT_STATUS_ERROR}
+ * @param messageRef the message reference
+ */
+ public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
+ Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when there is an incoming message. The
+ * platform will deliver the message to the messages database and notify the IMS provider of the
+ * result by calling {@link #acknowledgeSms(int, int, int)}.
+ *
+ * This method must not be called before {@link #onReady()} is called.
+ *
+ * @param token unique token generated by IMS providers that the platform will use to trigger
+ * callbacks for this message.
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param pdu PDUs representing the contents of the message.
+ * @throws RuntimeException if called before {@link #onReady()} is triggered.
+ */
+ public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
+ acknowledgeSms(token, 0, DELIVER_STATUS_ERROR);
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers to pass the result of the sent message
+ * to the platform.
+ *
+ * This method must not be called before {@link #onReady()} is called.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param status result of sending the SMS. Valid values are:
+ * {@link #SEND_STATUS_OK},
+ * {@link #SEND_STATUS_ERROR},
+ * {@link #SEND_STATUS_ERROR_RETRY},
+ * {@link #SEND_STATUS_ERROR_FALLBACK},
+ * @param reason reason in case status is failure. Valid values are:
+ * {@link SmsManager#RESULT_ERROR_NONE},
+ * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
+ * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
+ * {@link SmsManager#RESULT_ERROR_NULL_PDU},
+ * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
+ * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
+ * {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
+ * {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
+ * {@link SmsManager#RESULT_NETWORK_REJECT},
+ * {@link SmsManager#RESULT_INVALID_ARGUMENTS},
+ * {@link SmsManager#RESULT_INVALID_STATE},
+ * {@link SmsManager#RESULT_NO_MEMORY},
+ * {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
+ * {@link SmsManager#RESULT_SYSTEM_ERROR},
+ * {@link SmsManager#RESULT_MODEM_ERROR},
+ * {@link SmsManager#RESULT_NETWORK_ERROR},
+ * {@link SmsManager#RESULT_ENCODING_ERROR},
+ * {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
+ * {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
+ * {@link SmsManager#RESULT_INTERNAL_ERROR},
+ * {@link SmsManager#RESULT_NO_RESOURCES},
+ * {@link SmsManager#RESULT_CANCELLED},
+ * {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ */
+ public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
+ int reason) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, status, reason);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Sets the status report of the sent message.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference.
+ * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param pdu PDUs representing the content of the status report.
+ * @throws RuntimeException if called before {@link #onReady()} is triggered
+ */
+ public final void onSmsStatusReportReceived(int token, int messageRef, String format,
+ byte[] pdu) throws RuntimeException{
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+ acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
+ * Provider.
+ *
+ * @return the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ */
+ public String getSmsFormat() {
+ return SmsMessage.FORMAT_3GPP;
+ }
+
+ /**
+ * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
+ * Any attempt by this class to access the framework before this method is called will return
+ * with a {@link RuntimeException}.
+ */
+ public void onReady() {
+ // Base Implementation - Should be overridden
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 054a8b2..fcd7faf 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -16,177 +16,332 @@
package android.telephony.ims.stub;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.RemoteException;
+import android.telephony.ims.ImsUtListener;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
/**
- * Base implementation of ImsUt, which implements stub versions of the methods
- * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsUt maintained by other ImsServices.
- *
- * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ * Base implementation of IMS UT interface, which implements stubs. Override these methods to
+ * implement functionality.
*
* @hide
*/
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtImplBase {
-public class ImsUtImplBase extends IImsUt.Stub {
+ private IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+ @Override
+ public void close() throws RemoteException {
+ ImsUtImplBase.this.close();
+ }
+
+ @Override
+ public int queryCallBarring(int cbType) throws RemoteException {
+ return ImsUtImplBase.this.queryCallBarring(cbType);
+ }
+
+ @Override
+ public int queryCallForward(int condition, String number) throws RemoteException {
+ return ImsUtImplBase.this.queryCallForward(condition, number);
+ }
+
+ @Override
+ public int queryCallWaiting() throws RemoteException {
+ return ImsUtImplBase.this.queryCallWaiting();
+ }
+
+ @Override
+ public int queryCLIR() throws RemoteException {
+ return ImsUtImplBase.this.queryCLIR();
+ }
+
+ @Override
+ public int queryCLIP() throws RemoteException {
+ return ImsUtImplBase.this.queryCLIP();
+ }
+
+ @Override
+ public int queryCOLR() throws RemoteException {
+ return ImsUtImplBase.this.queryCOLR();
+ }
+
+ @Override
+ public int queryCOLP() throws RemoteException {
+ return ImsUtImplBase.this.queryCOLP();
+ }
+
+ @Override
+ public int transact(Bundle ssInfo) throws RemoteException {
+ return ImsUtImplBase.this.transact(ssInfo);
+ }
+
+ @Override
+ public int updateCallBarring(int cbType, int action, String[] barrList) throws
+ RemoteException {
+ return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+ }
+
+ @Override
+ public int updateCallForward(int action, int condition, String number, int serviceClass,
+ int timeSeconds) throws RemoteException {
+ return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
+ timeSeconds);
+ }
+
+ @Override
+ public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+ return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+ }
+
+ @Override
+ public int updateCLIR(int clirMode) throws RemoteException {
+ return ImsUtImplBase.this.updateCLIR(clirMode);
+ }
+
+ @Override
+ public int updateCLIP(boolean enable) throws RemoteException {
+ return ImsUtImplBase.this.updateCLIP(enable);
+ }
+
+ @Override
+ public int updateCOLR(int presentation) throws RemoteException {
+ return ImsUtImplBase.this.updateCOLR(presentation);
+ }
+
+ @Override
+ public int updateCOLP(boolean enable) throws RemoteException {
+ return ImsUtImplBase.this.updateCOLP(enable);
+ }
+
+ @Override
+ public void setListener(IImsUtListener listener) throws RemoteException {
+ ImsUtImplBase.this.setListener(new ImsUtListener(listener));
+ }
+
+ @Override
+ public int queryCallBarringForServiceClass(int cbType, int serviceClass)
+ throws RemoteException {
+ return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+ }
+
+ @Override
+ public int updateCallBarringForServiceClass(int cbType, int action,
+ String[] barrList, int serviceClass) throws RemoteException {
+ return ImsUtImplBase.this.updateCallBarringForServiceClass(
+ cbType, action, barrList, serviceClass);
+ }
+ };
/**
- * Closes the object. This object is not usable after being closed.
+ * Called when the framework no longer needs to interact with the IMS UT implementation any
+ * longer.
*/
- @Override
- public void close() throws RemoteException {
+ public void close() {
}
/**
- * Retrieves the configuration of the call barring.
+ * Retrieves the call barring configuration.
+ * @param cbType
*/
- @Override
- public int queryCallBarring(int cbType) throws RemoteException {
+ public int queryCallBarring(int cbType) {
return -1;
}
/**
* Retrieves the configuration of the call barring for specified service class.
*/
- @Override
- public int queryCallBarringForServiceClass(int cbType, int serviceClass)
- throws RemoteException {
+ public int queryCallBarringForServiceClass(int cbType, int serviceClass) {
return -1;
}
/**
* Retrieves the configuration of the call forward.
*/
- @Override
- public int queryCallForward(int condition, String number) throws RemoteException {
+ public int queryCallForward(int condition, String number) {
return -1;
}
/**
* Retrieves the configuration of the call waiting.
*/
- @Override
- public int queryCallWaiting() throws RemoteException {
+ public int queryCallWaiting() {
return -1;
}
/**
* Retrieves the default CLIR setting.
+ * @hide
*/
- @Override
- public int queryCLIR() throws RemoteException {
+ public int queryCLIR() {
+ return queryClir();
+ }
+
+ /**
+ * Retrieves the CLIP call setting.
+ * @hide
+ */
+ public int queryCLIP() {
+ return queryClip();
+ }
+
+ /**
+ * Retrieves the COLR call setting.
+ * @hide
+ */
+ public int queryCOLR() {
+ return queryColr();
+ }
+
+ /**
+ * Retrieves the COLP call setting.
+ * @hide
+ */
+ public int queryCOLP() {
+ return queryColp();
+ }
+
+ /**
+ * Retrieves the default CLIR setting.
+ */
+ public int queryClir() {
return -1;
}
/**
* Retrieves the CLIP call setting.
*/
- @Override
- public int queryCLIP() throws RemoteException {
+ public int queryClip() {
return -1;
}
/**
* Retrieves the COLR call setting.
*/
- @Override
- public int queryCOLR() throws RemoteException {
+ public int queryColr() {
return -1;
}
/**
* Retrieves the COLP call setting.
*/
- @Override
- public int queryCOLP() throws RemoteException {
+ public int queryColp() {
return -1;
}
/**
* Updates or retrieves the supplementary service configuration.
*/
- @Override
- public int transact(Bundle ssInfo) throws RemoteException {
+ public int transact(Bundle ssInfo) {
return -1;
}
/**
* Updates the configuration of the call barring.
*/
- @Override
- public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+ public int updateCallBarring(int cbType, int action, String[] barrList) {
return -1;
}
/**
* Updates the configuration of the call barring for specified service class.
*/
- @Override
public int updateCallBarringForServiceClass(int cbType, int action, String[] barrList,
- int serviceClass) throws RemoteException {
+ int serviceClass) {
return -1;
}
/**
* Updates the configuration of the call forward.
*/
- @Override
public int updateCallForward(int action, int condition, String number, int serviceClass,
- int timeSeconds) throws RemoteException {
+ int timeSeconds) {
return 0;
}
/**
* Updates the configuration of the call waiting.
*/
- @Override
- public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+ public int updateCallWaiting(boolean enable, int serviceClass) {
return -1;
}
/**
* Updates the configuration of the CLIR supplementary service.
+ * @hide
*/
- @Override
- public int updateCLIR(int clirMode) throws RemoteException {
+ public int updateCLIR(int clirMode) {
+ return updateClir(clirMode);
+ }
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ * @hide
+ */
+ public int updateCLIP(boolean enable) {
+ return updateClip(enable);
+ }
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ * @hide
+ */
+ public int updateCOLR(int presentation) {
+ return updateColr(presentation);
+ }
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ * @hide
+ */
+ public int updateCOLP(boolean enable) {
+ return updateColp(enable);
+ }
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ public int updateClir(int clirMode) {
return -1;
}
/**
* Updates the configuration of the CLIP supplementary service.
*/
- @Override
- public int updateCLIP(boolean enable) throws RemoteException {
+ public int updateClip(boolean enable) {
return -1;
}
/**
* Updates the configuration of the COLR supplementary service.
*/
- @Override
- public int updateCOLR(int presentation) throws RemoteException {
+ public int updateColr(int presentation) {
return -1;
}
/**
* Updates the configuration of the COLP supplementary service.
*/
- @Override
- public int updateCOLP(boolean enable) throws RemoteException {
+ public int updateColp(boolean enable) {
return -1;
}
/**
* Sets the listener.
*/
- @Override
- public void setListener(IImsUtListener listener) throws RemoteException {
+ public void setListener(ImsUtListener listener) {
+ }
+
+ /**
+ * @hide
+ */
+ public IImsUt getInterface() {
+ return mServiceImpl;
}
}
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index f0d60b6..602c796 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -27,10 +27,13 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.File;
import java.io.IOException;
+import java.io.ObjectInput;
import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
-import java.io.Serializable;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -54,34 +57,116 @@
public static final int MAX_DESTINATION_URI_SIZE = 50000;
/** @hide */
- private static class OpaqueDataContainer implements Serializable {
- private final String appIntent;
- private final int version;
+ private static class SerializationDataContainer implements Externalizable {
+ private String fileServiceId;
+ private Uri source;
+ private Uri destination;
+ private int subscriptionId;
+ private String appIntent;
+ private int version;
- public OpaqueDataContainer(String appIntent, int version) {
- this.appIntent = appIntent;
- this.version = version;
+ public SerializationDataContainer() {}
+
+ SerializationDataContainer(DownloadRequest request) {
+ fileServiceId = request.fileServiceId;
+ source = request.sourceUri;
+ destination = request.destinationUri;
+ subscriptionId = request.subscriptionId;
+ appIntent = request.serializedResultIntentForApp;
+ version = request.version;
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput objectOutput) throws IOException {
+ objectOutput.write(version);
+ objectOutput.writeUTF(fileServiceId);
+ objectOutput.writeUTF(source.toString());
+ objectOutput.writeUTF(destination.toString());
+ objectOutput.write(subscriptionId);
+ objectOutput.writeUTF(appIntent);
+ }
+
+ @Override
+ public void readExternal(ObjectInput objectInput) throws IOException {
+ version = objectInput.read();
+ fileServiceId = objectInput.readUTF();
+ source = Uri.parse(objectInput.readUTF());
+ destination = Uri.parse(objectInput.readUTF());
+ subscriptionId = objectInput.read();
+ appIntent = objectInput.readUTF();
+ // Do version checks here -- future versions may have other fields.
}
}
public static class Builder {
private String fileServiceId;
private Uri source;
+ private Uri destination;
private int subscriptionId;
private String appIntent;
private int version = CURRENT_VERSION;
+ /**
+ * Constructs a {@link Builder} from a {@link DownloadRequest}
+ * @param other The {@link DownloadRequest} from which the data for the {@link Builder}
+ * should come.
+ * @return An instance of {@link Builder} pre-populated with data from the provided
+ * {@link DownloadRequest}.
+ */
+ public static Builder fromDownloadRequest(DownloadRequest other) {
+ Builder result = new Builder(other.sourceUri, other.destinationUri)
+ .setServiceId(other.fileServiceId)
+ .setSubscriptionId(other.subscriptionId);
+ result.appIntent = other.serializedResultIntentForApp;
+ // Version of the result is going to be the current version -- as this class gets
+ // updated, new fields will be set to default values in here.
+ return result;
+ }
+
+ /**
+ * This method constructs a new instance of {@link Builder} based on the serialized data
+ * passed in.
+ * @param data A byte array, the contents of which should have been originally obtained
+ * from {@link DownloadRequest#toByteArray()}.
+ */
+ public static Builder fromSerializedRequest(byte[] data) {
+ Builder builder;
+ try {
+ ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
+ SerializationDataContainer dataContainer =
+ (SerializationDataContainer) stream.readObject();
+ builder = new Builder(dataContainer.source, dataContainer.destination);
+ builder.version = dataContainer.version;
+ builder.appIntent = dataContainer.appIntent;
+ builder.fileServiceId = dataContainer.fileServiceId;
+ builder.subscriptionId = dataContainer.subscriptionId;
+ } catch (IOException e) {
+ // Really should never happen
+ Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
+ throw new IllegalArgumentException(e);
+ } catch (ClassNotFoundException e) {
+ Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
+ throw new IllegalArgumentException(e);
+ }
+ return builder;
+ }
/**
* Builds a new DownloadRequest.
* @param sourceUri the source URI for the DownloadRequest to be built. This URI should
* never be null.
+ * @param destinationUri The final location for the file(s) that are to be downloaded. It
+ * must be on the same filesystem as the temp file directory set via
+ * {@link android.telephony.MbmsDownloadSession#setTempFileRootDirectory(File)}.
+ * The provided path must be a directory that exists. An
+ * {@link IllegalArgumentException} will be thrown otherwise.
*/
- public Builder(@NonNull Uri sourceUri) {
- if (sourceUri == null) {
- throw new IllegalArgumentException("Source URI must be non-null.");
+ public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri) {
+ if (sourceUri == null || destinationUri == null) {
+ throw new IllegalArgumentException("Source and destination URIs must be non-null.");
}
source = sourceUri;
+ destination = destinationUri;
}
/**
@@ -130,68 +215,34 @@
return this;
}
- /**
- * For use by the middleware to set the byte array of opaque data. The opaque data
- * includes information about the download request that is used by the client app and the
- * manager code, but is irrelevant to the middleware.
- * @param data A byte array, the contents of which should have been originally obtained
- * from {@link DownloadRequest#getOpaqueData()}.
- * @hide
- */
- @SystemApi
- public Builder setOpaqueData(byte[] data) {
- try {
- ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
- OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
- version = dataContainer.version;
- appIntent = dataContainer.appIntent;
- } catch (IOException e) {
- // Really should never happen
- Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
- throw new IllegalArgumentException(e);
- } catch (ClassNotFoundException e) {
- Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
- throw new IllegalArgumentException(e);
- }
- return this;
- }
-
public DownloadRequest build() {
- return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
+ return new DownloadRequest(fileServiceId, source, destination,
+ subscriptionId, appIntent, version);
}
}
private final String fileServiceId;
private final Uri sourceUri;
+ private final Uri destinationUri;
private final int subscriptionId;
private final String serializedResultIntentForApp;
private final int version;
private DownloadRequest(String fileServiceId,
- Uri source, int sub,
+ Uri source, Uri destination, int sub,
String appIntent, int version) {
this.fileServiceId = fileServiceId;
sourceUri = source;
subscriptionId = sub;
+ destinationUri = destination;
serializedResultIntentForApp = appIntent;
this.version = version;
}
- public static DownloadRequest copy(DownloadRequest other) {
- return new DownloadRequest(other);
- }
-
- private DownloadRequest(DownloadRequest dr) {
- fileServiceId = dr.fileServiceId;
- sourceUri = dr.sourceUri;
- subscriptionId = dr.subscriptionId;
- serializedResultIntentForApp = dr.serializedResultIntentForApp;
- version = dr.version;
- }
-
private DownloadRequest(Parcel in) {
fileServiceId = in.readString();
sourceUri = in.readParcelable(getClass().getClassLoader());
+ destinationUri = in.readParcelable(getClass().getClassLoader());
subscriptionId = in.readInt();
serializedResultIntentForApp = in.readString();
version = in.readInt();
@@ -204,6 +255,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(fileServiceId);
out.writeParcelable(sourceUri, flags);
+ out.writeParcelable(destinationUri, flags);
out.writeInt(subscriptionId);
out.writeString(serializedResultIntentForApp);
out.writeInt(version);
@@ -224,6 +276,13 @@
}
/**
+ * @return The destination {@link Uri} of the downloaded file.
+ */
+ public Uri getDestinationUri() {
+ return destinationUri;
+ }
+
+ /**
* @return The subscription ID on which to perform MBMS operations.
*/
public int getSubscriptionId() {
@@ -244,19 +303,16 @@
}
/**
- * For use by the middleware only. The byte array returned from this method should be
- * persisted and sent back to the app upon download completion or failure by passing it into
- * {@link Builder#setOpaqueData(byte[])}.
- * @return A byte array of opaque data to persist.
- * @hide
+ * This method returns a byte array that may be persisted to disk and restored to a
+ * {@link DownloadRequest}. The instance of {@link DownloadRequest} persisted by this method
+ * may be recovered via {@link Builder#fromSerializedRequest(byte[])}.
+ * @return A byte array of data to persist.
*/
- @SystemApi
- public byte[] getOpaqueData() {
+ public byte[] toByteArray() {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
- OpaqueDataContainer container = new OpaqueDataContainer(
- serializedResultIntentForApp, version);
+ SerializationDataContainer container = new SerializationDataContainer(this);
stream.writeObject(container);
stream.flush();
return byteArrayOutputStream.toByteArray();
@@ -299,15 +355,6 @@
}
/**
- * @hide
- */
- public boolean isMultipartDownload() {
- // TODO: figure out what qualifies a request as a multipart download request.
- return getSourceUri().getLastPathSegment() != null &&
- getSourceUri().getLastPathSegment().contains("*");
- }
-
- /**
* Retrieves the hash string that should be used as the filename when storing a token for
* this DownloadRequest.
* @hide
@@ -320,8 +367,9 @@
throw new RuntimeException("Could not get sha256 hash object");
}
if (version >= 1) {
- // Hash the source URI and the app intent
+ // Hash the source, destination, and the app intent
digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
+ digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
if (serializedResultIntentForApp != null) {
digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
}
@@ -344,12 +392,13 @@
version == request.version &&
Objects.equals(fileServiceId, request.fileServiceId) &&
Objects.equals(sourceUri, request.sourceUri) &&
+ Objects.equals(destinationUri, request.destinationUri) &&
Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
}
@Override
public int hashCode() {
- return Objects.hash(fileServiceId, sourceUri,
+ return Objects.hash(fileServiceId, sourceUri, destinationUri,
subscriptionId, serializedResultIntentForApp, version);
}
}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index a7a5958..c2a79d8 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -16,22 +16,23 @@
package android.telephony.mbms;
-import android.os.Handler;
+import android.os.Binder;
import android.os.RemoteException;
import java.util.List;
+import java.util.concurrent.Executor;
/** @hide */
public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
- private final Handler mHandler;
+ private final Executor mExecutor;
private final MbmsDownloadSessionCallback mAppCallback;
private volatile boolean mIsStopped = false;
public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
- Handler handler) {
+ Executor executor) {
mAppCallback = appCallback;
- mHandler = handler;
+ mExecutor = executor;
}
@Override
@@ -40,10 +41,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onError(errorCode, message);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onError(errorCode, message);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -54,10 +60,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onFileServicesUpdated(services);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onFileServicesUpdated(services);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -68,18 +79,19 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onMiddlewareReady();
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onMiddlewareReady();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
- public Handler getHandler() {
- return mHandler;
- }
-
public void stop() {
mIsStopped = true;
}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
index 8702952..f30ae27 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
@@ -16,20 +16,22 @@
package android.telephony.mbms;
-import android.os.Handler;
+import android.os.Binder;
import android.os.RemoteException;
+import java.util.concurrent.Executor;
+
/**
* @hide
*/
public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
- private final Handler mHandler;
+ private final Executor mExecutor;
private final DownloadStateCallback mAppCallback;
private volatile boolean mIsStopped = false;
- public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
+ public InternalDownloadStateCallback(DownloadStateCallback appCallback, Executor executor) {
mAppCallback = appCallback;
- mHandler = handler;
+ mExecutor = executor;
}
@Override
@@ -40,11 +42,16 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
- fullDownloadSize, currentDecodedSize, fullDecodedSize);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+ fullDownloadSize, currentDecodedSize, fullDecodedSize);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -56,10 +63,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onStateUpdated(request, fileInfo, state);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onStateUpdated(request, fileInfo, state);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
index eb6579ce..e9f39ff 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -16,18 +16,21 @@
package android.telephony.mbms;
-import android.os.Handler;
+import android.os.Binder;
import android.os.RemoteException;
+import java.util.concurrent.Executor;
+
/** @hide */
public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
private final StreamingServiceCallback mAppCallback;
- private final Handler mHandler;
+ private final Executor mExecutor;
private volatile boolean mIsStopped = false;
- public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+ public InternalStreamingServiceCallback(StreamingServiceCallback appCallback,
+ Executor executor) {
mAppCallback = appCallback;
- mHandler = handler;
+ mExecutor = executor;
}
@Override
@@ -36,10 +39,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onError(errorCode, message);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onError(errorCode, message);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -50,10 +58,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onStreamStateUpdated(state, reason);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onStreamStateUpdated(state, reason);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -64,10 +77,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onMediaDescriptionUpdated();
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onMediaDescriptionUpdated();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -78,10 +96,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -92,10 +115,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onStreamMethodUpdated(methodType);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onStreamMethodUpdated(methodType);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
index d782d12..d47f5ad 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -16,21 +16,22 @@
package android.telephony.mbms;
-import android.os.Handler;
+import android.os.Binder;
import android.os.RemoteException;
import java.util.List;
+import java.util.concurrent.Executor;
/** @hide */
public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
- private final Handler mHandler;
+ private final Executor mExecutor;
private final MbmsStreamingSessionCallback mAppCallback;
private volatile boolean mIsStopped = false;
public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
- Handler handler) {
+ Executor executor) {
mAppCallback = appCallback;
- mHandler = handler;
+ mExecutor = executor;
}
@Override
@@ -39,10 +40,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onError(errorCode, message);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onError(errorCode, message);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -54,10 +60,15 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onStreamingServicesUpdated(services);
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onStreamingServicesUpdated(services);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
@@ -68,18 +79,19 @@
return;
}
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mAppCallback.onMiddlewareReady();
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onMiddlewareReady();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
});
}
- public Handler getHandler() {
- return mHandler;
- }
-
public void stop() {
mIsStopped = true;
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 9ef188c..b0c00c6 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -21,8 +21,10 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.telephony.MbmsDownloadSession;
@@ -31,14 +33,11 @@
import java.io.File;
import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -62,6 +61,8 @@
/** @hide */
public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
+ private static final String EMBMS_INTENT_PERMISSION = "android.permission.SEND_EMBMS_INTENTS";
+
/**
* Indicates that the requested operation completed without error.
* @hide
@@ -137,6 +138,8 @@
/** @hide */
@Override
public void onReceive(Context context, Intent intent) {
+ verifyPermissionIntegrity(context);
+
if (!verifyIntentContents(context, intent)) {
setResultCode(RESULT_MALFORMED_INTENT);
return;
@@ -260,20 +263,18 @@
FileInfo completedFileInfo =
(FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
- Path stagingDirectory = FileSystems.getDefault().getPath(
- MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
- TEMP_FILE_STAGING_LOCATION);
+ Path appSpecifiedDestination = FileSystems.getDefault().getPath(
+ request.getDestinationUri().getPath());
- Uri stagedFileLocation;
+ Uri finalLocation;
try {
- stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
+ finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination);
} catch (IOException e) {
Log.w(LOG_TAG, "Failed to move temp file to final destination");
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
return;
}
- intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
- stagedFileLocation);
+ intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI, finalLocation);
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
context.sendBroadcast(intentForApp);
@@ -437,19 +438,22 @@
}
/*
- * Moves a tempfile located at fromPath to a new location in the staging directory.
+ * Moves a tempfile located at fromPath to its final home where the app wants it
*/
- private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
+ private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath) throws IOException {
if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
- Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
+ Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
+ " does not have a file scheme");
return null;
}
Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
- if (!Files.isDirectory(stagingDirectory)) {
- Files.createDirectory(stagingDirectory);
+ if (!Files.isDirectory(appSpecifiedPath)) {
+ Files.createDirectory(appSpecifiedPath);
}
- Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
+ // TODO: do we want to support directory trees within the download directory?
+ Path result = Files.move(fromFile, appSpecifiedPath.resolve(fromFile.getFileName()),
+ StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
return Uri.fromFile(result.toFile());
}
@@ -513,39 +517,29 @@
return mMiddlewarePackageNameCache;
}
- private static boolean manualMove(File src, File dst) {
- InputStream in = null;
- OutputStream out = null;
- try {
- if (!dst.exists()) {
- dst.createNewFile();
- }
- in = new FileInputStream(src);
- out = new FileOutputStream(dst);
- byte[] buffer = new byte[2048];
- int len;
- do {
- len = in.read(buffer);
- out.write(buffer, 0, len);
- } while (len > 0);
- } catch (IOException e) {
- Log.w(LOG_TAG, "Manual file move failed due to exception " + e);
- if (dst.exists()) {
- dst.delete();
- }
- return false;
- } finally {
- try {
- if (in != null) {
- in.close();
- }
- if (out != null) {
- out.close();
- }
- } catch (IOException e) {
- Log.w(LOG_TAG, "Error closing streams: " + e);
- }
+ private void verifyPermissionIntegrity(Context context) {
+ PackageManager pm = context.getPackageManager();
+ Intent queryIntent = new Intent(context, MbmsDownloadReceiver.class);
+ List<ResolveInfo> infos = pm.queryBroadcastReceivers(queryIntent, 0);
+ if (infos.size() != 1) {
+ throw new IllegalStateException("Non-unique download receiver in your app");
}
- return true;
+ ActivityInfo selfInfo = infos.get(0).activityInfo;
+ if (selfInfo == null) {
+ throw new IllegalStateException("Queried ResolveInfo does not contain a receiver");
+ }
+ if (MbmsUtils.getOverrideServiceName(context,
+ MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION) != null) {
+ // If an override was specified, just make sure that the permission isn't null.
+ if (selfInfo.permission == null) {
+ throw new IllegalStateException(
+ "MbmsDownloadReceiver must require some permission");
+ }
+ return;
+ }
+ if (!Objects.equals(EMBMS_INTENT_PERMISSION, selfInfo.permission)) {
+ throw new IllegalStateException("MbmsDownloadReceiver must require the " +
+ "SEND_EMBMS_INTENTS permission.");
+ }
}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index af0af24..b5fec44 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -108,8 +108,8 @@
/**
* Indicates that the app called
- * {@link MbmsStreamingSession#startStreaming(
- * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
+ * java.util.concurrent.Executor, StreamingServiceCallback)}
* more than once for the same {@link StreamingServiceInfo}.
*/
public static final int ERROR_DUPLICATE_START_STREAM = 303;
@@ -128,6 +128,9 @@
/** Indicates that the middleware has no record of the supplied {@link DownloadRequest}. */
public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
+
+ /** Indicates the the middleware has no record of the supplied {@link FileInfo} */
+ public static final int ERROR_UNKNOWN_FILE_INFO = 403;
}
private MbmsErrors() {}
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
index 5c130a0..6e03957 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -22,11 +22,12 @@
import android.telephony.MbmsStreamingSession;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* A callback class that is used to receive information from the middleware on MBMS streaming
* services. An instance of this object should be passed into
- * {@link MbmsStreamingSession#create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+ * {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
*/
public class MbmsStreamingSessionCallback {
/**
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index b4ad1d7..ef317ee 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -50,7 +50,7 @@
return new ComponentName(ci.packageName, ci.name);
}
- private static ComponentName getOverrideServiceName(Context context, String serviceAction) {
+ public static ComponentName getOverrideServiceName(Context context, String serviceAction) {
String metaDataKey = null;
switch (serviceAction) {
case MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION:
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index ec9134a..b6239fe 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -29,11 +29,11 @@
/**
* Class used to represent a single MBMS stream. After a stream has been started with
- * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
- * StreamingServiceCallback, android.os.Handler)},
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo, java.util.concurrent.Executor,
+ * StreamingServiceCallback)},
* this class is used to hold information about the stream and control it.
*/
-public class StreamingService {
+public class StreamingService implements AutoCloseable {
private static final String LOG_TAG = "MbmsStreamingService";
/**
@@ -41,7 +41,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+ @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
public @interface StreamingState {}
public final static int STATE_STOPPED = 1;
public final static int STATE_STARTED = 2;
@@ -53,7 +53,8 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+ @IntDef(prefix = { "REASON_" },
+ value = {REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
public @interface StreamingStateChangeReason {}
@@ -64,9 +65,9 @@
public static final int REASON_NONE = 0;
/**
- * State changed due to a call to {@link #stopStreaming()} or
+ * State changed due to a call to {@link #close()} or
* {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
- * StreamingServiceCallback, android.os.Handler)}
+ * java.util.concurrent.Executor, StreamingServiceCallback)}
*/
public static final int REASON_BY_USER_REQUEST = 1;
@@ -161,7 +162,8 @@
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*/
- public void stopStreaming() {
+ @Override
+ public void close() {
if (mService == null) {
throw new IllegalStateException("No streaming service attached");
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index cb93542..7d9845f 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -46,7 +46,7 @@
int cancelDownload(in DownloadRequest downloadRequest);
- int getDownloadStatus(in DownloadRequest downloadRequest, in FileInfo fileInfo);
+ int requestDownloadState(in DownloadRequest downloadRequest, in FileInfo fileInfo);
int resetDownloadKnowledge(in DownloadRequest downloadRequest);
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 4fee3df..86b1b7a 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -370,18 +370,18 @@
}
/**
- * Gets information about the status of a file pending download.
+ * Requests information about the state of a file pending download.
*
- * If the middleware has not yet been properly initialized or if it has no records of the
+ * If the middleware has no records of the
* file indicated by {@code fileInfo} being associated with {@code downloadRequest},
- * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned.
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_FILE_INFO} must be returned.
*
* @param downloadRequest The download request to query.
* @param fileInfo The particular file within the request to get information on.
- * @return The status of the download.
+ * @return {@link MbmsErrors#SUCCESS} if the request was successful, an error code otherwise.
*/
@Override
- public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
+ public int requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo)
throws RemoteException {
return 0;
}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index cf4c47b..421b015 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -19,8 +19,9 @@
import android.content.Context;
import android.os.RemoteException;
import android.telephony.Rlog;
-
-import com.android.ims.internal.IImsConfig;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.stub.ImsConfigImplBase;
/**
* Provides APIs to get/set the IMS service feature/capability/parameters.
@@ -46,7 +47,7 @@
/**
* Broadcast action: the configuration was changed
- *
+ * @deprecated Use {@link ImsConfig#addConfigCallback(ImsConfigImplBase.Callback)} instead.
* @hide
*/
public static final String ACTION_IMS_CONFIG_CHANGED =
@@ -70,6 +71,8 @@
/**
* Defines IMS service/capability feature constants.
+ * @deprecated Use
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability} instead.
*/
public static class FeatureConstants {
public static final int FEATURE_TYPE_UNKNOWN = -1;
@@ -539,162 +542,164 @@
}
public ImsConfig(IImsConfig iconfig, Context context) {
- if (DBG) Rlog.d(TAG, "ImsConfig creates");
+ if (DBG) Rlog.d(TAG, "ImsConfig created");
miConfig = iconfig;
mContext = context;
}
/**
- * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
- * This function should not be called from the mainthread as it could block the
- * mainthread.
+ * @deprecated see {@link #getInt(int)} instead.
+ */
+ public int getProvisionedValue(int item) throws ImsException {
+ return getConfigInt(item);
+ }
+
+ /**
+ * Gets the configuration value for IMS service/capabilities parameters used by IMS stack.
*
* @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
* @return the value in Integer format.
- *
- * @throws ImsException if calling the IMS service results in an error.
+ * @throws ImsException if the ImsService is unavailable.
*/
- public int getProvisionedValue(int item) throws ImsException {
+ public int getConfigInt(int item) throws ImsException {
int ret = 0;
try {
- ret = miConfig.getProvisionedValue(item);
+ ret = miConfig.getConfigInt(item);
} catch (RemoteException e) {
- throw new ImsException("getValue()", e,
+ throw new ImsException("getInt()", e,
ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
}
- if (DBG) Rlog.d(TAG, "getProvisionedValue(): item = " + item + ", ret =" + ret);
+ if (DBG) Rlog.d(TAG, "getInt(): item = " + item + ", ret =" + ret);
return ret;
}
/**
- * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
- * This function should not be called from the mainthread as it could block the
- * mainthread.
+ * @deprecated see {@link #getConfigString(int)} instead
+ */
+ public String getProvisionedStringValue(int item) throws ImsException {
+ return getConfigString(item);
+ }
+
+ /**
+ * Gets the configuration value for IMS service/capabilities parameters used by IMS stack.
*
* @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
* @return value in String format.
*
- * @throws ImsException if calling the IMS service results in an error.
+ * @throws ImsException if the ImsService is unavailable.
*/
- public String getProvisionedStringValue(int item) throws ImsException {
+ public String getConfigString(int item) throws ImsException {
String ret = "Unknown";
try {
- ret = miConfig.getProvisionedStringValue(item);
+ ret = miConfig.getConfigString(item);
} catch (RemoteException e) {
- throw new ImsException("getProvisionedStringValue()", e,
+ throw new ImsException("getConfigString()", e,
ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
}
- if (DBG) Rlog.d(TAG, "getProvisionedStringValue(): item = " + item + ", ret =" + ret);
+ if (DBG) Rlog.d(TAG, "getConfigString(): item = " + item + ", ret =" + ret);
return ret;
}
/**
- * Sets the value for IMS service/capabilities parameters by
- * the operator device management entity.
- * This function should not be called from main thread as it could block
- * mainthread.
+ * @deprecated see {@link #setConfig(int, int)} instead.
+ */
+ public int setProvisionedValue(int item, int value) throws ImsException {
+ return setConfig(item, value);
+ }
+
+ /**
+ * @deprecated see {@link #setConfig(int, String)} instead.
+ */
+ public int setProvisionedStringValue(int item, String value) throws ImsException {
+ return setConfig(item, value);
+ }
+
+ /**
+ * Sets the value for ImsService configuration item.
*
* @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
* @param value in Integer format.
* @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
*
- * @throws ImsException if calling the IMS service results in an error.
+ * @throws ImsException if the ImsService is unavailable.
*/
- public int setProvisionedValue(int item, int value)
- throws ImsException {
+ public int setConfig(int item, int value) throws ImsException {
int ret = OperationStatusConstants.UNKNOWN;
if (DBG) {
- Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+ Rlog.d(TAG, "setConfig(): item = " + item +
"value = " + value);
}
try {
- ret = miConfig.setProvisionedValue(item, value);
+ ret = miConfig.setConfigInt(item, value);
} catch (RemoteException e) {
- throw new ImsException("setProvisionedValue()", e,
+ throw new ImsException("setConfig()", e,
ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
}
if (DBG) {
- Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+ Rlog.d(TAG, "setConfig(): item = " + item +
+ " value = " + value + " ret = " + ret);
+ }
+ return ret;
+
+ }
+
+ /**
+ * Sets the value for ImsService configuration item.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+ *
+ * @throws ImsException if the ImsService is unavailable.
+ */
+ public int setConfig(int item, String value) throws ImsException {
+ int ret = OperationStatusConstants.UNKNOWN;
+ if (DBG) {
+ Rlog.d(TAG, "setConfig(): item = " + item +
+ "value = " + value);
+ }
+ try {
+ ret = miConfig.setConfigString(item, value);
+ } catch (RemoteException e) {
+ throw new ImsException("setConfig()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) {
+ Rlog.d(TAG, "setConfig(): item = " + item +
" value = " + value + " ret = " + ret);
}
return ret;
}
/**
- * Sets the value for IMS service/capabilities parameters by
- * the operator device management entity.
- * This function should not be called from main thread as it could block
- * mainthread.
+ * Adds a {@link ImsConfigImplBase.Callback} to the ImsService to notify when a Configuration
+ * item has changed.
*
- * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
- * @param value in String format.
- * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
- *
- * @throws ImsException if calling the IMS service results in an error.
+ * Make sure to call {@link #removeConfigCallback(ImsConfigImplBase.Callback)} when finished
+ * using this callback.
*/
- public int setProvisionedStringValue(int item, String value)
- throws ImsException {
- int ret = OperationStatusConstants.UNKNOWN;
+ public void addConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+ if (DBG) Rlog.d(TAG, "addConfigCallback: " + callback);
try {
- ret = miConfig.setProvisionedStringValue(item, value);
+ miConfig.addImsConfigCallback(callback);
} catch (RemoteException e) {
- throw new ImsException("setProvisionedStringValue()", e,
- ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
- }
- if (DBG) {
- Rlog.d(TAG, "setProvisionedStringValue(): item = " + item +
- ", value =" + value);
- }
- return ret;
- }
-
- /**
- * Gets the value for IMS feature item for specified network type.
- *
- * @param feature, defined as in FeatureConstants.
- * @param network, defined as in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
- * @param listener, provided to be notified for the feature on/off status.
- * @return void
- *
- * @throws ImsException if calling the IMS service results in an error.
- */
- public void getFeatureValue(int feature, int network,
- ImsConfigListener listener) throws ImsException {
- if (DBG) {
- Rlog.d(TAG, "getFeatureValue: feature = " + feature + ", network =" + network +
- ", listener =" + listener);
- }
- try {
- miConfig.getFeatureValue(feature, network, listener);
- } catch (RemoteException e) {
- throw new ImsException("getFeatureValue()", e,
+ throw new ImsException("addConfigCallback()", e,
ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
}
}
/**
- * Sets the value for IMS feature item for specified network type.
- *
- * @param feature, as defined in FeatureConstants.
- * @param network, as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
- * @param value, as defined in FeatureValueConstants.
- * @param listener, provided if caller needs to be notified for set result.
- * @return void
- *
- * @throws ImsException if calling the IMS service results in an error.
+ * Removes a {@link ImsConfigImplBase.Callback} from the ImsService that was previously added
+ * by {@link #addConfigCallback(ImsConfigImplBase.Callback)}.
*/
- public void setFeatureValue(int feature, int network, int value,
- ImsConfigListener listener) throws ImsException {
- if (DBG) {
- Rlog.d(TAG, "setFeatureValue: feature = " + feature + ", network =" + network +
- ", value =" + value + ", listener =" + listener);
- }
+ public void removeConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+ if (DBG) Rlog.d(TAG, "removeConfigCallback: " + callback);
try {
- miConfig.setFeatureValue(feature, network, value, listener);
- } catch (RemoteException e) {
- throw new ImsException("setFeatureValue()", e,
+ miConfig.removeImsConfigCallback(callback);
+ } catch (RemoteException e) {
+ throw new ImsException("removeConfigCallback()", e,
ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
}
}
diff --git a/telephony/java/com/android/ims/ImsException.java b/telephony/java/com/android/ims/ImsException.java
index 0e8bad7..f35e886 100644
--- a/telephony/java/com/android/ims/ImsException.java
+++ b/telephony/java/com/android/ims/ImsException.java
@@ -16,6 +16,8 @@
package com.android.ims;
+import android.telephony.ims.ImsReasonInfo;
+
/**
* This class defines a general IMS-related exception.
*
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index 14c184a..c9d4405 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -18,6 +18,8 @@
import android.os.Handler;
import android.os.Message;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsSsInfo;
/**
* Provides APIs for the supplementary service settings using IMS (Ut interface).
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index c6fc5e5..15234e5 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -17,9 +17,10 @@
package com.android.ims.internal;
import android.os.Message;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.internal.IImsCallSessionListener;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
import com.android.ims.internal.IImsVideoCallProvider;
/**
@@ -135,6 +136,13 @@
void accept(int callType, in ImsStreamMediaProfile profile);
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ void deflect(String deflectNumber);
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index 748092d..a8e8b7dd 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -16,12 +16,12 @@
package com.android.ims.internal;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsConferenceState;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsConferenceState;
import com.android.ims.internal.IImsCallSession;
-import com.android.ims.ImsSuppServiceNotification;
+import android.telephony.ims.ImsSuppServiceNotification;
/**
* A listener type for receiving notification on IMS call session events.
diff --git a/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
index 1621967..b3d8139 100644
--- a/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
@@ -16,7 +16,7 @@
package com.android.ims.internal;
-import com.android.ims.ImsExternalCallState;
+import android.telephony.ims.ImsExternalCallState;
/**
* A listener type for receiving notifications about DEP through IMS
diff --git a/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
index 41b1042..b83b130 100644
--- a/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
@@ -17,9 +17,9 @@
package com.android.ims.internal;
/**
-* Interface from ImsFeature in the ImsService to ImsServiceController.
+ * Interface from ImsFeature in the ImsService to ImsServiceController.
* {@hide}
*/
oneway interface IImsFeatureStatusCallback {
void notifyImsFeatureStatus(int featureStatus);
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
index 52b3853..5151192 100644
--- a/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
+++ b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl
@@ -18,9 +18,8 @@
import android.app.PendingIntent;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
@@ -43,8 +42,7 @@
void addRegistrationListener(in IImsRegistrationListener listener);
void removeRegistrationListener(in IImsRegistrationListener listener);
ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType);
- IImsCallSession createCallSession(int sessionId, in ImsCallProfile profile,
- IImsCallSessionListener listener);
+ IImsCallSession createCallSession(int sessionId, in ImsCallProfile profile);
IImsCallSession getPendingCallSession(int sessionId, String callId);
IImsUt getUtInterface();
IImsConfig getConfigInterface();
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 15f8726..2212109 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -16,7 +16,7 @@
package com.android.ims.internal;
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
import android.net.Uri;
diff --git a/telephony/java/com/android/ims/internal/IImsService.aidl b/telephony/java/com/android/ims/internal/IImsService.aidl
index 406d22d..c3cc6fb 100644
--- a/telephony/java/com/android/ims/internal/IImsService.aidl
+++ b/telephony/java/com/android/ims/internal/IImsService.aidl
@@ -18,7 +18,7 @@
import android.app.PendingIntent;
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
import com.android.ims.internal.IImsConfig;
diff --git a/telephony/java/com/android/ims/internal/IImsUtListener.aidl b/telephony/java/com/android/ims/internal/IImsUtListener.aidl
index 1bc0369..a603cd3 100644
--- a/telephony/java/com/android/ims/internal/IImsUtListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUtListener.aidl
@@ -18,11 +18,11 @@
import android.os.Bundle;
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsSsData;
-import com.android.ims.ImsSsInfo;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsSsInfo;
import com.android.ims.internal.IImsUt;
-import com.android.ims.ImsReasonInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
/**
* {@hide}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 91032f3..d999c13 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -108,6 +108,7 @@
public static final int EVENT_SET_CARRIER_DATA_ENABLED = BASE + 46;
public static final int EVENT_DATA_RECONNECT = BASE + 47;
public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
+ public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/ExponentialBackoff.java b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
index 80958c0..f323a0c 100644
--- a/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
+++ b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
@@ -20,6 +20,8 @@
import android.os.Handler;
import android.os.Looper;
+import com.android.internal.annotations.VisibleForTesting;
+
/** The implementation of exponential backoff with jitter applied. */
public class ExponentialBackoff {
private int mRetryCounter;
@@ -27,8 +29,31 @@
private long mMaximumDelayMs;
private long mCurrentDelayMs;
private int mMultiplier;
- private Runnable mRunnable;
- private Handler mHandler;
+ private final Runnable mRunnable;
+ private final Handler mHandler;
+
+ /**
+ * Implementation of Handler methods, Adapter for testing (can't spy on final methods).
+ */
+ private HandlerAdapter mHandlerAdapter = new HandlerAdapter() {
+ @Override
+ public boolean postDelayed(Runnable runnable, long delayMillis) {
+ return mHandler.postDelayed(runnable, delayMillis);
+ }
+
+ @Override
+ public void removeCallbacks(Runnable runnable) {
+ mHandler.removeCallbacks(runnable);
+ }
+ };
+
+ /**
+ * Need to spy final methods for testing.
+ */
+ public interface HandlerAdapter {
+ boolean postDelayed(Runnable runnable, long delayMillis);
+ void removeCallbacks(Runnable runnable);
+ }
public ExponentialBackoff(
long initialDelayMs,
@@ -57,14 +82,14 @@
public void start() {
mRetryCounter = 0;
mCurrentDelayMs = mStartDelayMs;
- mHandler.removeCallbacks(mRunnable);
- mHandler.postDelayed(mRunnable, mCurrentDelayMs);
+ mHandlerAdapter.removeCallbacks(mRunnable);
+ mHandlerAdapter.postDelayed(mRunnable, mCurrentDelayMs);
}
/** Stops the backoff, all pending messages will be removed from the message queue. */
public void stop() {
mRetryCounter = 0;
- mHandler.removeCallbacks(mRunnable);
+ mHandlerAdapter.removeCallbacks(mRunnable);
}
/** Should call when the retry action has failed and we want to retry after a longer delay. */
@@ -73,12 +98,17 @@
long temp = Math.min(
mMaximumDelayMs, (long) (mStartDelayMs * Math.pow(mMultiplier, mRetryCounter)));
mCurrentDelayMs = (long) (((1 + Math.random()) / 2) * temp);
- mHandler.removeCallbacks(mRunnable);
- mHandler.postDelayed(mRunnable, mCurrentDelayMs);
+ mHandlerAdapter.removeCallbacks(mRunnable);
+ mHandlerAdapter.postDelayed(mRunnable, mCurrentDelayMs);
}
/** Returns the delay for the most recently posted message. */
public long getCurrentDelay() {
return mCurrentDelayMs;
}
+
+ @VisibleForTesting
+ public void setHandlerAdapter(HandlerAdapter a) {
+ mHandlerAdapter = a;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index ac16139..8e3f4c0 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -46,5 +46,6 @@
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
void onCarrierNetworkChange(in boolean active);
+ void onUserMobileDataStateChanged(in boolean enabled);
}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 0f31821..303a068 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -195,14 +195,6 @@
String[] getIsimPcscf(int subId);
/**
- * TODO: Deprecate and remove this interface. Superceded by getIccsimChallengeResponse.
- * Returns the response of ISIM Authetification through RIL.
- * @return the response of ISIM Authetification, or null if
- * the Authentification hasn't been successed or isn't present iphonesubinfo.
- */
- String getIsimChallengeResponse(String nonce);
-
- /**
* Returns the response of the SIM application on the UICC to authentication
* challenge/response algorithm. The data string and challenge response are
* Base64 encoded Strings.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 64083e3..9e2b519 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -38,14 +38,17 @@
import android.telephony.SignalStrength;
import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.OperatorInfo;
import java.util.List;
+import android.telephony.UiccSlotInfo;
/**
* Interface used to interact with the phone. Mostly this is used by the
@@ -678,6 +681,7 @@
* Input parameters equivalent to TS 27.007 AT+CSIM command.
*
* @param subId The subscription to use.
+ * @param callingPackage the name of the package making the call.
* @param cla Class of the APDU command.
* @param instruction Instruction of the APDU command.
* @param p1 P1 value of the APDU command.
@@ -688,7 +692,7 @@
* @return The APDU response from the ICC card with the status appended at
* the end.
*/
- String iccTransmitApduBasicChannel(int subId, int cla, int instruction,
+ String iccTransmitApduBasicChannel(int subId, String callingPackage, int cla, int instruction,
int p1, int p2, int p3, String data);
/**
@@ -785,20 +789,21 @@
int getTetherApnRequired();
/**
- * Get IImsMMTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
- * as well as registering the MMTelFeature for callbacks using the IImsServiceFeatureCallback
- * interface.
- */
- IImsMMTelFeature getMMTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
+ * Enables framework IMS and triggers IMS Registration.
+ */
+ void enableIms(int slotId);
/**
- * Get IImsMMTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
- * as well as registering the MMTelFeature for callbacks using the IImsServiceFeatureCallback
+ * Disables framework IMS and triggers IMS deregistration.
+ */
+ void disableIms(int slotId);
+
+ /**
+ * Get IImsMmTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature
+ * as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback
* interface.
- * Used for emergency calling only.
*/
- IImsMMTelFeature getEmergencyMMTelFeatureAndListen(int slotId,
- in IImsServiceFeatureCallback callback);
+ IImsMmTelFeature getMmTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
/**
* Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature
@@ -808,6 +813,16 @@
IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
/**
+ * Returns the IImsRegistration associated with the slot and feature specified.
+ */
+ IImsRegistration getImsRegistration(int slotId, int feature);
+
+ /**
+ * Returns the IImsConfig associated with the slot and feature specified.
+ */
+ IImsConfig getImsConfig(int slotId, int feature);
+
+ /**
* Set the network selection mode to automatic.
*
* @param subId the id of the subscription to update.
@@ -870,14 +885,33 @@
*
* @param enable true to turn on, else false
*/
- void setDataEnabled(int subId, boolean enable);
+ void setUserDataEnabled(int subId, boolean enable);
+
+ /**
+ * Get the user enabled state of Mobile Data.
+ *
+ * TODO: remove and use isUserDataEnabled.
+ * This can't be removed now because some vendor codes
+ * calls through ITelephony directly while they should
+ * use TelephonyManager.
+ *
+ * @return true on enabled
+ */
+ boolean getDataEnabled(int subId);
/**
* Get the user enabled state of Mobile Data.
*
* @return true on enabled
*/
- boolean getDataEnabled(int subId);
+ boolean isUserDataEnabled(int subId);
+
+ /**
+ * Get the overall enabled state of Mobile Data.
+ *
+ * @return true on enabled
+ */
+ boolean isDataEnabled(int subId);
/**
* Get P-CSCF address from PCO after data connection is established or modified.
@@ -918,6 +952,11 @@
int getCarrierPrivilegeStatus(int subId);
/**
+ * Similar to above, but check for the given uid.
+ */
+ int getCarrierPrivilegeStatusForUid(int subId, int uid);
+
+ /**
* Similar to above, but check for the package whose name is pkgName.
*/
int checkCarrierPrivilegesForPackage(String pkgName);
@@ -1089,33 +1128,33 @@
boolean isHearingAidCompatibilitySupported();
/**
- * Get IMS Registration Status
- */
- boolean isImsRegistered();
-
- /**
* Get IMS Registration Status on a particular subid.
*
* @param subId user preferred subId.
*
* @return {@code true} if the IMS status is registered.
*/
- boolean isImsRegisteredForSubscriber(int subId);
+ boolean isImsRegistered(int subId);
/**
- * Returns the Status of Wi-Fi Calling
+ * Returns the Status of Wi-Fi Calling for the subscription id specified.
*/
- boolean isWifiCallingAvailable();
+ boolean isWifiCallingAvailable(int subId);
/**
- * Returns the Status of Volte
+ * Returns the Status of VoLTE for the subscription ID specified.
*/
- boolean isVolteAvailable();
+ boolean isVolteAvailable(int subId);
/**
- * Returns the Status of VT (video telephony)
+ * Returns the Status of VT (video telephony) for the subscription ID specified.
*/
- boolean isVideoTelephonyAvailable();
+ boolean isVideoTelephonyAvailable(int subId);
+
+ /**
+ * Returns the MMTEL IMS registration technology for the subsciption ID specified.
+ */
+ int getImsRegTechnologyForMmTel(int subId);
/**
* Returns the unique device ID of phone, for example, the IMEI for
@@ -1298,6 +1337,34 @@
*/
List<CarrierIdentifier> getAllowedCarriers(int slotIndex);
+ /**
+ * Returns carrier id of the given subscription.
+ * <p>To recognize carrier as a first class identity, assign each carrier with a canonical
+ * integer a.k.a carrier id.
+ *
+ * @param subId The subscription id
+ * @return Carrier id of given subscription id. return {@link #UNKNOWN_CARRIER_ID} if
+ * subscription is unavailable or carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ * @hide
+ */
+ int getSubscriptionCarrierId(int subId);
+
+ /**
+ * Returns carrier name of the given subscription.
+ * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId(int)},
+ * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
+ * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
+ * Carrier name is not canonical identity, use {@link #getSubscriptionCarrierId(int)} instead.
+ * <p>Returned carrier name is unlocalized.
+ *
+ * @return Carrier name of given subscription id. return {@code null} if subscription is
+ * unavailable or carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ * @hide
+ */
+ String getSubscriptionCarrierName(int subId);
+
/**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns
* Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
@@ -1391,4 +1458,19 @@
* @hide
*/
SignalStrength getSignalStrength(int subId);
+
+ /**
+ * Get slot info for all the UICC slots.
+ * @return UiccSlotInfo array.
+ * @hide
+ */
+ UiccSlotInfo[] getUiccSlotsInfo();
+
+ /**
+ * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive.
+ * @param physicalSlots Index i in the array representing physical slot for phone i. The array
+ * size should be same as getPhoneCount().
+ * @return boolean Return true if the switch succeeds, false if the switch fails.
+ */
+ boolean switchSlots(in int[] physicalSlots);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 75d8f3f..188167c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -69,4 +69,5 @@
int activationState, int activationType);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
+ void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
}
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index f3d9335..d57f9af 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -30,16 +30,14 @@
public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
/* ABSENT means ICC is missing */
public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
+ /* PRESENT means ICC is present */
+ public static final String INTENT_VALUE_ICC_PRESENT = "PRESENT";
/* CARD_IO_ERROR means for three consecutive times there was SIM IO error */
static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
/* CARD_RESTRICTED means card is present but not usable due to carrier restrictions */
static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = "CARD_RESTRICTED";
/* LOCKED means ICC is locked by pin or by network */
public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
- //TODO: we can remove this state in the future if Bug 18489776 analysis
- //#42's first race condition is resolved
- /* INTERNAL LOCKED means ICC is locked by pin or by network */
- public static final String INTENT_VALUE_ICC_INTERNAL_LOCKED = "INTERNAL_LOCKED";
/* READY means ICC is ready to access */
public static final String INTENT_VALUE_ICC_READY = "READY";
/* IMSI means ICC IMSI is ready in property */
@@ -77,7 +75,8 @@
NOT_READY, /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
PERM_DISABLED, /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
CARD_IO_ERROR, /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
- CARD_RESTRICTED;/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
+ CARD_RESTRICTED,/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
+ LOADED; /** ordinal(9) == {@See TelephonyManager#SIM_STATE_LOADED} */
public boolean isPinLocked() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
@@ -85,9 +84,9 @@
public boolean iccCardExist() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
- || (this == NETWORK_LOCKED) || (this == READY)
+ || (this == NETWORK_LOCKED) || (this == READY) || (this == NOT_READY)
|| (this == PERM_DISABLED) || (this == CARD_IO_ERROR)
- || (this == CARD_RESTRICTED));
+ || (this == CARD_RESTRICTED) || (this == LOADED));
}
public static State intToState(int state) throws IllegalArgumentException {
@@ -102,6 +101,7 @@
case 7: return PERM_DISABLED;
case 8: return CARD_IO_ERROR;
case 9: return CARD_RESTRICTED;
+ case 10: return LOADED;
default:
throw new IllegalArgumentException();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index e2d25b8..a3a3080 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -105,6 +105,8 @@
int DEVICE_IN_USE = 64; /* Operation cannot be performed because the device
is currently in use */
int ABORTED = 65; /* Operation aborted */
+ int INVALID_RESPONSE = 66; /* Invalid response sent by vendor code */
+
// Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
// reveal particular replacement for Generic failure
int OEM_ERROR_1 = 501;
@@ -218,11 +220,6 @@
String SETUP_DATA_PROTOCOL_IPV6 = "IPV6";
String SETUP_DATA_PROTOCOL_IPV4V6 = "IPV4V6";
- /* Deactivate data call reasons */
- int DEACTIVATE_REASON_NONE = 0;
- int DEACTIVATE_REASON_RADIO_OFF = 1;
- int DEACTIVATE_REASON_PDP_RESET = 2;
-
/* NV config radio reset types. */
int NV_CONFIG_RELOAD_RESET = 1;
int NV_CONFIG_ERASE_RESET = 2;
@@ -417,6 +414,10 @@
int RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION = 141;
int RIL_REQUEST_START_NETWORK_SCAN = 142;
int RIL_REQUEST_STOP_NETWORK_SCAN = 143;
+ int RIL_REQUEST_GET_SLOT_STATUS = 144;
+ int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145;
+ int RIL_REQUEST_START_KEEPALIVE = 146;
+ int RIL_REQUEST_STOP_KEEPALIVE = 147;
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -471,4 +472,6 @@
int RIL_UNSOL_MODEM_RESTART = 1047;
int RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION = 1048;
int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049;
+ int RIL_UNSOL_ICC_SLOT_STATUS = 1050;
+ int RIL_UNSOL_KEEPALIVE_STATUS = 1051;
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
new file mode 100644
index 0000000..da8471f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -0,0 +1,209 @@
+/*
+ * 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.internal.telephony;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ITelephony;
+
+/** Utility class for Telephony permission enforcement. */
+public final class TelephonyPermissions {
+ private static final String LOG_TAG = "TelephonyPermissions";
+
+ private static final boolean DBG = false;
+
+ private TelephonyPermissions() {}
+
+ /**
+ * Check whether the caller (or self, if not processing an IPC) can read phone state.
+ *
+ * <p>This method behaves in one of the following ways:
+ * <ul>
+ * <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
+ * READ_PHONE_STATE runtime permission.
+ * <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
+ * apps which support runtime permissions, if the caller does not currently have any of
+ * these permissions.
+ * <li>return false: if the caller lacks all of these permissions and doesn't support runtime
+ * permissions. This implies that the user revoked the ability to read phone state
+ * manually (via AppOps). In this case we can't throw as it would break app compatibility,
+ * so we return false to indicate that the calling function should return dummy data.
+ * </ul>
+ */
+ public static boolean checkCallingOrSelfReadPhoneState(
+ Context context, String callingPackage, String message) {
+ return checkReadPhoneState(context, Binder.getCallingPid(), Binder.getCallingUid(),
+ callingPackage, message);
+ }
+
+ /**
+ * Check whether the app with the given pid/uid can read phone state.
+ *
+ * <p>This method behaves in one of the following ways:
+ * <ul>
+ * <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
+ * READ_PHONE_STATE runtime permission.
+ * <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
+ * apps which support runtime permissions, if the caller does not currently have any of
+ * these permissions.
+ * <li>return false: if the caller lacks all of these permissions and doesn't support runtime
+ * permissions. This implies that the user revoked the ability to read phone state
+ * manually (via AppOps). In this case we can't throw as it would break app compatibility,
+ * so we return false to indicate that the calling function should return dummy data.
+ * </ul>
+ */
+ public static boolean checkReadPhoneState(
+ Context context, int pid, int uid, String callingPackage, String message) {
+ try {
+ context.enforcePermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
+
+ // SKIP checking for run-time permission since caller has PRIVILEGED permission
+ return true;
+ } catch (SecurityException privilegedPhoneStateException) {
+ context.enforcePermission(
+ android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+ }
+
+ // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
+ // revoked.
+ AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage) ==
+ AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * Returns whether the caller can read phone numbers.
+ *
+ * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
+ * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+ */
+ public static boolean checkCallingOrSelfReadPhoneNumber(
+ Context context, String callingPackage, String message) {
+ return checkReadPhoneNumber(
+ context, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
+ }
+
+ @VisibleForTesting
+ public static boolean checkReadPhoneNumber(
+ Context context, int pid, int uid, String callingPackage, String message) {
+ // Default SMS app can always read it.
+ AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, uid, callingPackage) ==
+ AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+
+ // NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they
+ // will be denied access, even if they have another permission and AppOps bit if needed.
+
+ // First, check if we can read the phone state.
+ try {
+ return checkReadPhoneState(context, pid, uid, callingPackage, message);
+ } catch (SecurityException readPhoneStateSecurityException) {
+ }
+ // Can be read with READ_SMS too.
+ try {
+ context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message);
+ int opCode = AppOpsManager.permissionToOpCode(android.Manifest.permission.READ_SMS);
+ if (opCode != AppOpsManager.OP_NONE) {
+ return appOps.noteOp(opCode, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
+ } else {
+ return true;
+ }
+ } catch (SecurityException readSmsSecurityException) {
+ }
+ // Can be read with READ_PHONE_NUMBERS too.
+ try {
+ context.enforcePermission(android.Manifest.permission.READ_PHONE_NUMBERS, pid, uid,
+ message);
+ int opCode = AppOpsManager.permissionToOpCode(
+ android.Manifest.permission.READ_PHONE_NUMBERS);
+ if (opCode != AppOpsManager.OP_NONE) {
+ return appOps.noteOp(opCode, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
+ } else {
+ return true;
+ }
+ } catch (SecurityException readPhoneNumberSecurityException) {
+ }
+
+ throw new SecurityException(message + ": Neither user " + uid +
+ " nor current process has " + android.Manifest.permission.READ_PHONE_STATE +
+ ", " + android.Manifest.permission.READ_SMS + ", or " +
+ android.Manifest.permission.READ_PHONE_NUMBERS);
+ }
+
+ /**
+ * Ensure the caller (or self, if not processing an IPC) has MODIFY_PHONE_STATE (and is thus a
+ * privileged app) or carrier privileges.
+ *
+ * @throws SecurityException if the caller does not have the required permission/privileges
+ */
+ public static void enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ Context context, int subId, String message) {
+ if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) ==
+ PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ if (DBG) Rlog.d(LOG_TAG, "No modify permission, check carrier privilege next.");
+ enforceCallingOrSelfCarrierPrivilege(subId, message);
+ }
+
+ /**
+ * Make sure the caller (or self, if not processing an IPC) has carrier privileges.
+ *
+ * @throws SecurityException if the caller does not have the required privileges
+ */
+ public static void enforceCallingOrSelfCarrierPrivilege(int subId, String message) {
+ // NOTE: It's critical that we explicitly pass the calling UID here rather than call
+ // TelephonyManager#hasCarrierPrivileges directly, as the latter only works when called from
+ // the phone process. When called from another process, it will check whether that process
+ // has carrier privileges instead.
+ enforceCarrierPrivilege(subId, Binder.getCallingUid(), message);
+ }
+
+ private static void enforceCarrierPrivilege(int subId, int uid, String message) {
+ if (getCarrierPrivilegeStatus(subId, uid) !=
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
+ throw new SecurityException(message);
+ }
+ }
+
+ private static int getCarrierPrivilegeStatus(int subId, int uid) {
+ ITelephony telephony =
+ ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ try {
+ if (telephony != null) {
+ return telephony.getCarrierPrivilegeStatusForUid(subId, uid);
+ }
+ } catch (RemoteException e) {
+ // Fallback below.
+ }
+ Rlog.e(LOG_TAG, "Phone process is down, cannot check carrier privileges");
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
index d750363..8a77bf1 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IAuthenticateServerCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
index d750363..f6b99e2 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ICancelSessionCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
index d750363..23a642e 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IDeleteProfileCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
similarity index 71%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
index d750363..3ee0b3a 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IDisableProfileCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
new file mode 100644
index 0000000..e33f44c
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -0,0 +1,89 @@
+/*
+ * 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.internal.telephony.euicc;
+
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetProfileCallback;
+import com.android.internal.telephony.euicc.IDisableProfileCallback;
+import com.android.internal.telephony.euicc.ISwitchToProfileCallback;
+import com.android.internal.telephony.euicc.ISetNicknameCallback;
+import com.android.internal.telephony.euicc.IDeleteProfileCallback;
+import com.android.internal.telephony.euicc.IResetMemoryCallback;
+import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IGetSmdsAddressCallback;
+import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
+
+/** @hide */
+interface IEuiccCardController {
+ oneway void getAllProfiles(String callingPackage, String cardId,
+ in IGetAllProfilesCallback callback);
+ oneway void getProfile(String callingPackage, String cardId, String iccid,
+ in IGetProfileCallback callback);
+ oneway void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh,
+ in IDisableProfileCallback callback);
+ oneway void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh,
+ in ISwitchToProfileCallback callback);
+ oneway void setNickname(String callingPackage, String cardId, String iccid, String nickname,
+ in ISetNicknameCallback callback);
+ oneway void deleteProfile(String callingPackage, String cardId, String iccid,
+ in IDeleteProfileCallback callback);
+ oneway void resetMemory(String callingPackage, String cardId, int options, in IResetMemoryCallback callback);
+ oneway void getDefaultSmdpAddress(String callingPackage, String cardId,
+ in IGetDefaultSmdpAddressCallback callback);
+ oneway void getSmdsAddress(String callingPackage, String cardId,
+ in IGetSmdsAddressCallback callback);
+ oneway void setDefaultSmdpAddress(String callingPackage, String cardId, String address,
+ in ISetDefaultSmdpAddressCallback callback);
+ oneway void getRulesAuthTable(String callingPackage, String cardId,
+ in IGetRulesAuthTableCallback callback);
+ oneway void getEuiccChallenge(String callingPackage, String cardId,
+ in IGetEuiccChallengeCallback callback);
+ oneway void getEuiccInfo1(String callingPackage, String cardId,
+ in IGetEuiccInfo1Callback callback);
+ oneway void getEuiccInfo2(String callingPackage, String cardId,
+ in IGetEuiccInfo2Callback callback);
+ oneway void authenticateServer(String callingPackage, String cardId, String matchingId,
+ in byte[] serverSigned1, in byte[] serverSignature1, in byte[] euiccCiPkIdToBeUsed,
+ in byte[] serverCertificatein, in IAuthenticateServerCallback callback);
+ oneway void prepareDownload(String callingPackage, String cardId, in byte[] hashCc,
+ in byte[] smdpSigned2, in byte[] smdpSignature2, in byte[] smdpCertificate,
+ in IPrepareDownloadCallback callback);
+ oneway void loadBoundProfilePackage(String callingPackage, String cardId,
+ in byte[] boundProfilePackage, in ILoadBoundProfilePackageCallback callback);
+ oneway void cancelSession(String callingPackage, String cardId, in byte[] transactionId,
+ int reason, in ICancelSessionCallback callback);
+ oneway void listNotifications(String callingPackage, String cardId, int events,
+ in IListNotificationsCallback callback);
+ oneway void retrieveNotificationList(String callingPackage, String cardId, int events,
+ in IRetrieveNotificationListCallback callback);
+ oneway void retrieveNotification(String callingPackage, String cardId, int seqNumber,
+ in IRetrieveNotificationCallback callback);
+ oneway void removeNotificationFromList(String callingPackage, String cardId, int seqNumber,
+ in IRemoveNotificationFromListCallback callback);
+}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index b3fc90d..0a0ad90 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -30,6 +30,7 @@
oneway void getDefaultDownloadableSubscriptionList(
String callingPackage, in PendingIntent callbackIntent);
String getEid();
+ int getOtaStatus();
oneway void downloadSubscription(in DownloadableSubscription subscription,
boolean switchAfterDownload, String callingPackage, in PendingIntent callbackIntent);
EuiccInfo getEuiccInfo();
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
index d750363..97b0768 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetAllProfilesCallback {
+ void onComplete(int resultCode, in EuiccProfileInfo[] profiles);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
index d750363..79b659d 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetDefaultSmdpAddressCallback {
+ void onComplete(int resultCode, String address);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
index d750363..5ffb340 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccChallengeCallback {
+ void onComplete(int resultCode, in byte[] challenge);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
index d750363..9592acb 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccInfo1Callback {
+ void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
index d750363..5256b35 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetEuiccInfo2Callback {
+ void onComplete(int resultCode, in byte[] info);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
similarity index 69%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
index d750363..e9fc9e9 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetProfileCallback {
+ void onComplete(int resultCode, in EuiccProfileInfo profile);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
index d750363..58f0bde 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccRulesAuthTable;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetRulesAuthTableCallback {
+ void onComplete(int resultCode, in EuiccRulesAuthTable rat);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
index d750363..09a83aa 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IGetSmdsAddressCallback {
+ void onComplete(int resultCode, String address);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
similarity index 67%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
index d750363..65aa302 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IListNotificationsCallback {
+ void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
similarity index 72%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
index d750363..4ad7081 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ILoadBoundProfilePackageCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
similarity index 73%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
index d750363..c035184 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IPrepareDownloadCallback {
+ void onComplete(int resultCode, in byte[] response);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
similarity index 69%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
index d750363..b22d0da 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRemoveNotificationFromListCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
index d750363..860c158 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IResetMemoryCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
similarity index 67%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
index d750363..dd8889a9 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRetrieveNotificationCallback {
+ void onComplete(int resultCode, in EuiccNotification notification);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
similarity index 66%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
index d750363..bc4e451 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.telephony.euicc.EuiccNotification;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface IRetrieveNotificationListCallback {
+ void onComplete(int resultCode, in EuiccNotification[] notifications);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
similarity index 74%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
index d750363..1e47125 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISetDefaultSmdpAddressCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
similarity index 75%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
index d750363..5899980 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISetNicknameCallback {
+ void onComplete(int resultCode);
+}
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
similarity index 68%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
index d750363..21ff084 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.telephony.euicc;
+
+import android.service.euicc.EuiccProfileInfo;
/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+oneway interface ISwitchToProfileCallback {
+ void onComplete(int resultCode, in EuiccProfileInfo profile);
+}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 9f8b3a8..c095438 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -837,6 +837,13 @@
}
/**
+ * Strip all the trailing 'F' characters of a string, e.g., an ICCID.
+ */
+ public static String stripTrailingFs(String s) {
+ return s == null ? null : s.replaceAll("(?i)f*$", "");
+ }
+
+ /**
* Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
* hex number, 0 will be returned.
*/
diff --git a/test-base/Android.bp b/test-base/Android.bp
new file mode 100644
index 0000000..4d149f7
--- /dev/null
+++ b/test-base/Android.bp
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Build the android.test.base library
+// ===================================
+// This contains the junit.framework and android.test classes that were in
+// Android API level 25 excluding those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library {
+ name: "android.test.base",
+
+ srcs: ["src/**/*.java"],
+
+ errorprone: {
+ javacflags: ["-Xep:DepAnn:ERROR"],
+ },
+
+ // Needs to be consistent with the repackaged version of this make target.
+ java_version: "1.8",
+
+ sdk_version: "current",
+ hostdex: true,
+}
+
+// Build the legacy-test library
+// =============================
+// This contains the junit.framework and android.test classes that were in
+// Android API level 25 excluding those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library {
+ name: "legacy-test",
+
+ sdk_version: "current",
+ static_libs: ["android.test.base"],
+}
+
+// Build the repackaged.android.test.base library
+// ==============================================
+// This contains repackaged versions of the classes from
+// android.test.base.
+java_library_static {
+ name: "repackaged.android.test.base",
+
+ sdk_version: "current",
+ static_libs: ["android.test.base"],
+
+ jarjar_rules: "jarjar-rules.txt",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
+}
+
+// Build the android.test.base-minus-junit library
+// ===============================================
+// This contains the android.test classes from android.test.base plus
+// the com.android.internal.util.Predicate[s] classes. This is only
+// intended for inclusion in the android.test.legacy and
+// legacy-android-test static libraries and must not be used elsewhere.
+java_library_static {
+ name: "android.test.base-minus-junit",
+
+ srcs: [
+ "src/android/**/*.java",
+ "src/com/**/*.java",
+ ],
+
+ sdk_version: "current",
+ libs: [
+ "junit",
+ ],
+}
diff --git a/test-base/Android.mk b/test-base/Android.mk
new file mode 100644
index 0000000..ebb33de
--- /dev/null
+++ b/test-base/Android.mk
@@ -0,0 +1,122 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
+# Generate the stub source files for android.test.base.stubs
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src
+
+ANDROID_TEST_BASE_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.base.stubs_intermediates/api.txt
+ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.base.stubs_intermediates/removed.txt
+
+ANDROID_TEST_BASE_API_FILE := $(LOCAL_PATH)/api/android-test-base-current.txt
+ANDROID_TEST_BASE_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-base-removed.txt
+
+LOCAL_DROIDDOC_OPTIONS:= \
+ -stubpackages android.test:android.test.suitebuilder.annotation:com.android.internal.util:junit.framework \
+ -stubsourceonly \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.base.stubs_intermediates/src \
+ -nodocs \
+ -api $(ANDROID_TEST_BASE_OUTPUT_API_FILE) \
+ -removedApi $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE) \
+
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_MODULE := android-test-base-api-stubs-gen
+
+include $(BUILD_DROIDDOC)
+
+# Remember the target that will trigger the code generation.
+android_test_base_gen_stamp := $(full_target)
+
+# Add some additional dependencies
+$(ANDROID_TEST_BASE_OUTPUT_API_FILE): $(full_target)
+$(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE): $(full_target)
+
+# Build the android.test.base.stubs library
+# =========================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.base.stubs
+
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SDK_VERSION := current
+
+# Make sure to run droiddoc first to generate the stub source files.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_base_gen_stamp)
+android_test_base_gen_stamp :=
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.base.stubs.jar)
+
+# Check that the android.test.base.stubs library has not changed
+# ==============================================================
+
+# Check that the API we're building hasn't changed from the not-yet-released
+# SDK version.
+$(eval $(call check-api, \
+ check-android-test-base-api-current, \
+ $(ANDROID_TEST_BASE_API_FILE), \
+ $(ANDROID_TEST_BASE_OUTPUT_API_FILE), \
+ $(ANDROID_TEST_BASE_REMOVED_API_FILE), \
+ $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE), \
+ -error 2 -error 3 -error 4 -error 5 -error 6 \
+ -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+ -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
+ -error 25 -error 26 -error 27, \
+ cat $(LOCAL_PATH)/api/apicheck_msg_android_test_base.txt, \
+ check-android-test-base-api, \
+ $(call doc-timestamp-for,android-test-base-api-stubs-gen) \
+ ))
+
+.PHONY: check-android-test-base-api
+checkapi: check-android-test-base-api
+
+.PHONY: update-android-test-base-api
+update-api: update-android-test-base-api
+
+update-android-test-base-api: $(ANDROID_TEST_BASE_OUTPUT_API_FILE) | $(ACP)
+ @echo Copying current.txt
+ $(hide) $(ACP) $(ANDROID_TEST_BASE_OUTPUT_API_FILE) $(ANDROID_TEST_BASE_API_FILE)
+ @echo Copying removed.txt
+ $(hide) $(ACP) $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_BASE_REMOVED_API_FILE)
+
+endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
+
+ifeq ($(HOST_OS),linux)
+# Build the legacy-performance-test-hostdex library
+# =================================================
+# This contains the android.test.PerformanceTestCase class only
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
+LOCAL_MODULE := legacy-performance-test-hostdex
+
+include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
+endif # HOST_OS == linux
diff --git a/legacy-test/api/legacy-test-current.txt b/test-base/api/android-test-base-current.txt
similarity index 100%
rename from legacy-test/api/legacy-test-current.txt
rename to test-base/api/android-test-base-current.txt
diff --git a/legacy-test/api/legacy-test-removed.txt b/test-base/api/android-test-base-removed.txt
similarity index 100%
rename from legacy-test/api/legacy-test-removed.txt
rename to test-base/api/android-test-base-removed.txt
diff --git a/test-base/api/apicheck_msg_android_test_base.txt b/test-base/api/apicheck_msg_android_test_base.txt
new file mode 100644
index 0000000..144aecc
--- /dev/null
+++ b/test-base/api/apicheck_msg_android_test_base.txt
@@ -0,0 +1,17 @@
+
+******************************
+You have tried to change the API from what has been previously approved.
+
+To make these errors go away, you have two choices:
+ 1) You can add "@hide" javadoc comments to the methods, etc. listed in the
+ errors above.
+
+ 2) You can update android-test-base-current.txt by executing the following command:
+ make update-android-test-base-api
+
+ To submit the revised android-test-base-current.txt to the main Android repository,
+ you will need approval.
+******************************
+
+
+
diff --git a/legacy-test/jarjar-rules.txt b/test-base/jarjar-rules.txt
similarity index 100%
rename from legacy-test/jarjar-rules.txt
rename to test-base/jarjar-rules.txt
diff --git a/legacy-test/src/android/test/AndroidTestCase.java b/test-base/src/android/test/AndroidTestCase.java
similarity index 100%
rename from legacy-test/src/android/test/AndroidTestCase.java
rename to test-base/src/android/test/AndroidTestCase.java
diff --git a/legacy-test/src/android/test/FlakyTest.java b/test-base/src/android/test/FlakyTest.java
similarity index 100%
rename from legacy-test/src/android/test/FlakyTest.java
rename to test-base/src/android/test/FlakyTest.java
diff --git a/legacy-test/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java
similarity index 100%
rename from legacy-test/src/android/test/InstrumentationTestCase.java
rename to test-base/src/android/test/InstrumentationTestCase.java
diff --git a/legacy-test/src/android/test/InstrumentationTestSuite.java b/test-base/src/android/test/InstrumentationTestSuite.java
similarity index 100%
rename from legacy-test/src/android/test/InstrumentationTestSuite.java
rename to test-base/src/android/test/InstrumentationTestSuite.java
diff --git a/legacy-test/src/android/test/PerformanceTestCase.java b/test-base/src/android/test/PerformanceTestCase.java
similarity index 88%
rename from legacy-test/src/android/test/PerformanceTestCase.java
rename to test-base/src/android/test/PerformanceTestCase.java
index 65bd4a4..2584da2 100644
--- a/legacy-test/src/android/test/PerformanceTestCase.java
+++ b/test-base/src/android/test/PerformanceTestCase.java
@@ -21,6 +21,11 @@
*
* If you want your test to be used as a performance test, you must
* implement this interface.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
@Deprecated
public interface PerformanceTestCase
diff --git a/legacy-test/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java
similarity index 100%
rename from legacy-test/src/android/test/RepetitiveTest.java
rename to test-base/src/android/test/RepetitiveTest.java
diff --git a/legacy-test/src/android/test/UiThreadTest.java b/test-base/src/android/test/UiThreadTest.java
similarity index 100%
rename from legacy-test/src/android/test/UiThreadTest.java
rename to test-base/src/android/test/UiThreadTest.java
diff --git a/legacy-test/src/android/test/package.html b/test-base/src/android/test/package.html
similarity index 100%
rename from legacy-test/src/android/test/package.html
rename to test-base/src/android/test/package.html
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java b/test-base/src/android/test/suitebuilder/annotation/LargeTest.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java
rename to test-base/src/android/test/suitebuilder/annotation/LargeTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java b/test-base/src/android/test/suitebuilder/annotation/MediumTest.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java
rename to test-base/src/android/test/suitebuilder/annotation/MediumTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java b/test-base/src/android/test/suitebuilder/annotation/SmallTest.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java
rename to test-base/src/android/test/suitebuilder/annotation/SmallTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Smoke.java b/test-base/src/android/test/suitebuilder/annotation/Smoke.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/Smoke.java
rename to test-base/src/android/test/suitebuilder/annotation/Smoke.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Suppress.java b/test-base/src/android/test/suitebuilder/annotation/Suppress.java
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/Suppress.java
rename to test-base/src/android/test/suitebuilder/annotation/Suppress.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/package.html b/test-base/src/android/test/suitebuilder/annotation/package.html
similarity index 100%
rename from legacy-test/src/android/test/suitebuilder/annotation/package.html
rename to test-base/src/android/test/suitebuilder/annotation/package.html
diff --git a/legacy-test/src/com/android/internal/util/Predicate.java b/test-base/src/com/android/internal/util/Predicate.java
similarity index 100%
rename from legacy-test/src/com/android/internal/util/Predicate.java
rename to test-base/src/com/android/internal/util/Predicate.java
diff --git a/legacy-test/src/junit/MODULE_LICENSE_CPL b/test-base/src/junit/MODULE_LICENSE_CPL
similarity index 100%
rename from legacy-test/src/junit/MODULE_LICENSE_CPL
rename to test-base/src/junit/MODULE_LICENSE_CPL
diff --git a/legacy-test/src/junit/README.android b/test-base/src/junit/README.android
similarity index 100%
rename from legacy-test/src/junit/README.android
rename to test-base/src/junit/README.android
diff --git a/legacy-test/src/junit/cpl-v10.html b/test-base/src/junit/cpl-v10.html
similarity index 100%
rename from legacy-test/src/junit/cpl-v10.html
rename to test-base/src/junit/cpl-v10.html
diff --git a/legacy-test/src/junit/framework/Assert.java b/test-base/src/junit/framework/Assert.java
similarity index 100%
rename from legacy-test/src/junit/framework/Assert.java
rename to test-base/src/junit/framework/Assert.java
diff --git a/legacy-test/src/junit/framework/AssertionFailedError.java b/test-base/src/junit/framework/AssertionFailedError.java
similarity index 100%
rename from legacy-test/src/junit/framework/AssertionFailedError.java
rename to test-base/src/junit/framework/AssertionFailedError.java
diff --git a/legacy-test/src/junit/framework/ComparisonCompactor.java b/test-base/src/junit/framework/ComparisonCompactor.java
similarity index 100%
rename from legacy-test/src/junit/framework/ComparisonCompactor.java
rename to test-base/src/junit/framework/ComparisonCompactor.java
diff --git a/legacy-test/src/junit/framework/ComparisonFailure.java b/test-base/src/junit/framework/ComparisonFailure.java
similarity index 100%
rename from legacy-test/src/junit/framework/ComparisonFailure.java
rename to test-base/src/junit/framework/ComparisonFailure.java
diff --git a/legacy-test/src/junit/framework/Protectable.java b/test-base/src/junit/framework/Protectable.java
similarity index 100%
rename from legacy-test/src/junit/framework/Protectable.java
rename to test-base/src/junit/framework/Protectable.java
diff --git a/legacy-test/src/junit/framework/Test.java b/test-base/src/junit/framework/Test.java
similarity index 100%
rename from legacy-test/src/junit/framework/Test.java
rename to test-base/src/junit/framework/Test.java
diff --git a/legacy-test/src/junit/framework/TestCase.java b/test-base/src/junit/framework/TestCase.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestCase.java
rename to test-base/src/junit/framework/TestCase.java
diff --git a/legacy-test/src/junit/framework/TestFailure.java b/test-base/src/junit/framework/TestFailure.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestFailure.java
rename to test-base/src/junit/framework/TestFailure.java
diff --git a/legacy-test/src/junit/framework/TestListener.java b/test-base/src/junit/framework/TestListener.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestListener.java
rename to test-base/src/junit/framework/TestListener.java
diff --git a/legacy-test/src/junit/framework/TestResult.java b/test-base/src/junit/framework/TestResult.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestResult.java
rename to test-base/src/junit/framework/TestResult.java
diff --git a/legacy-test/src/junit/framework/TestSuite.java b/test-base/src/junit/framework/TestSuite.java
similarity index 100%
rename from legacy-test/src/junit/framework/TestSuite.java
rename to test-base/src/junit/framework/TestSuite.java
diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp
new file mode 100644
index 0000000..d2af8a9
--- /dev/null
+++ b/test-legacy/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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.
+//
+
+// Build the legacy-android-test library
+// =====================================
+// This contains the android.test classes that were in Android API level 25,
+// including those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library_static {
+ name: "legacy-android-test",
+
+ static_libs: [
+ "android.test.base-minus-junit",
+ "android.test.runner-minus-junit",
+ "android.test.mock",
+ ],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ "junit",
+ ],
+}
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
new file mode 100644
index 0000000..da47de0
--- /dev/null
+++ b/test-legacy/Android.mk
@@ -0,0 +1,62 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
+# Build the android.test.legacy library
+# =====================================
+# Built against the SDK so that it can be statically included in APKs
+# without breaking link type checks.
+#
+# This builds directly from the source rather than simply statically
+# including the android.test.base-minus-junit and
+# android.test.runner-minus-junit libraries because the latter library
+# cannot itself be built against the SDK. That is because it uses on
+# an internal method (setTestContext) on the AndroidTestCase class.
+# That class is provided by both the android.test.base-minus-junit and
+# the current SDK and as the latter is first on the classpath its
+# version is used. Unfortunately, it does not provide the internal
+# method and so compilation fails.
+#
+# Building from source avoids that because the compiler will use the
+# source version of AndroidTestCase instead of the one from the current
+# SDK.
+#
+# The use of the internal method does not prevent this from being
+# statically included because the class that provides the method is
+# also included in this library.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.legacy
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, ../test-base/src/android) \
+ $(call all-java-files-under, ../test-base/src/com) \
+ $(call all-java-files-under, ../test-runner/src/android) \
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_JAVA_LIBRARIES := junit android.test.mock.stubs
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.legacy.jar)
+
+endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 8eddec4..54e07a16 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -19,12 +19,13 @@
java_library {
name: "android.test.mock",
+ // Needs to be consistent with the repackaged version of this make target.
+ java_version: "1.8",
srcs: ["src/**/*.java"],
no_framework_libs: true,
libs: [
"framework",
- "legacy-test",
],
}
@@ -36,4 +37,6 @@
static_libs: ["android.test.mock"],
jarjar_rules: "jarjar-rules.txt",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
}
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
index d81ca01..15fa12d1 100644
--- a/test-mock/Android.mk
+++ b/test-mock/Android.mk
@@ -16,14 +16,19 @@
LOCAL_PATH:= $(call my-dir)
-android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock)
+# Includes the main framework source to ensure that doclava has access to the
+# visibility information for the base classes of the mock classes. Without it
+# otherwise hidden methods could be visible.
+android_test_mock_source_files := \
+ $(call all-java-files-under, src/android/test/mock) \
+ $(call all-java-files-under, ../core/java/android)
# Generate the stub source files for android.test.mock.stubs
# ==========================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(android_test_mock_source_files)
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework conscrypt okhttp bouncycastle
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
@@ -34,6 +39,7 @@
ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
LOCAL_DROIDDOC_OPTIONS:= \
+ -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-stubpackages android.test.mock \
-stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/src \
-nodocs \
@@ -62,6 +68,9 @@
# Make sure to run droiddoc first to generate the stub source files.
LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
+android_test_mock_gen_stamp :=
+
+LOCAL_SDK_VERSION := current
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/test-mock/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
index 73f794b..07acfef 100644
--- a/test-mock/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -1,5 +1,9 @@
package android.test.mock {
+ public deprecated class MockAccountManager {
+ method public static android.accounts.AccountManager newMockAccountManager(android.content.Context);
+ }
+
public deprecated class MockApplication extends android.app.Application {
ctor public MockApplication();
}
@@ -9,8 +13,8 @@
ctor public MockContentProvider(android.content.Context);
ctor public MockContentProvider(android.content.Context, java.lang.String, java.lang.String, android.content.pm.PathPermission[]);
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>);
+ method public static deprecated void attachInfoForTesting(android.content.ContentProvider, android.content.Context, android.content.pm.ProviderInfo);
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
- method public final android.content.IContentProvider getIContentProvider();
method public java.lang.String getType(android.net.Uri);
method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public boolean onCreate();
@@ -22,37 +26,26 @@
public class MockContentResolver extends android.content.ContentResolver {
ctor public MockContentResolver();
ctor public MockContentResolver(android.content.Context);
- method protected android.content.IContentProvider acquireProvider(android.content.Context, java.lang.String);
- method protected android.content.IContentProvider acquireUnstableProvider(android.content.Context, java.lang.String);
method public void addProvider(java.lang.String, android.content.ContentProvider);
- method public boolean releaseProvider(android.content.IContentProvider);
- method public boolean releaseUnstableProvider(android.content.IContentProvider);
- method public void unstableProviderDied(android.content.IContentProvider);
}
public class MockContext extends android.content.Context {
ctor public MockContext();
method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
- method public boolean canLoadUnsafeResources();
method public int checkCallingOrSelfPermission(java.lang.String);
method public int checkCallingOrSelfUriPermission(android.net.Uri, int);
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
- method public int checkPermission(java.lang.String, int, int, android.os.IBinder);
method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
- method public int checkUriPermission(android.net.Uri, int, int, int, android.os.IBinder);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
- method public android.content.Context createApplicationContext(android.content.pm.ApplicationInfo, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.Context createCredentialProtectedStorageContext();
method public android.content.Context createDeviceProtectedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
method public boolean deleteFile(java.lang.String);
@@ -68,7 +61,6 @@
method public android.content.Context getApplicationContext();
method public android.content.pm.ApplicationInfo getApplicationInfo();
method public android.content.res.AssetManager getAssets();
- method public java.lang.String getBasePackageName();
method public java.io.File getCacheDir();
method public java.lang.ClassLoader getClassLoader();
method public java.io.File getCodeCacheDir();
@@ -76,8 +68,6 @@
method public java.io.File getDataDir();
method public java.io.File getDatabasePath(java.lang.String);
method public java.io.File getDir(java.lang.String, int);
- method public android.view.Display getDisplay();
- method public android.view.DisplayAdjustments getDisplayAdjustments(int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
method public java.io.File getExternalFilesDir(java.lang.String);
@@ -89,25 +79,19 @@
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
- method public java.lang.String getOpPackageName();
method public java.lang.String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public java.lang.String getPackageName();
method public java.lang.String getPackageResourcePath();
- method public java.io.File getPreloadsFileCache();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
- method public int getUserId();
method public android.graphics.drawable.Drawable getWallpaper();
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialProtectedStorage();
method public boolean isDeviceProtectedStorage();
method public boolean moveDatabaseFrom(android.content.Context, java.lang.String);
method public boolean moveSharedPreferencesFrom(android.content.Context, java.lang.String);
@@ -120,31 +104,19 @@
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, int);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, int);
- method public android.content.Intent registerReceiverAsUser(android.content.BroadcastReceiver, android.os.UserHandle, android.content.IntentFilter, java.lang.String, android.os.Handler);
- method public void reloadSharedPreferences();
method public void removeStickyBroadcast(android.content.Intent);
method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
method public void revokeUriPermission(java.lang.String, android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
- method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle);
- method public void sendBroadcast(android.content.Intent, java.lang.String, int);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
- method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.os.Bundle);
- method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, int);
- method public void sendBroadcastMultiplePermissions(android.content.Intent, java.lang.String[]);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, int, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
- method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.os.Bundle);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
@@ -155,17 +127,13 @@
method public void startActivity(android.content.Intent);
method public void startActivity(android.content.Intent, android.os.Bundle);
method public android.content.ComponentName startForegroundService(android.content.Intent);
- method public android.content.ComponentName startForegroundServiceAsUser(android.content.Intent, android.os.UserHandle);
method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
- method public android.content.ComponentName startServiceAsUser(android.content.Intent, android.os.UserHandle);
method public boolean stopService(android.content.Intent);
- method public boolean stopServiceAsUser(android.content.Intent, android.os.UserHandle);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
- method public void updateDisplay(int);
}
public deprecated class MockCursor implements android.database.Cursor {
@@ -221,8 +189,6 @@
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
ctor public MockPackageManager();
- method public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
- method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public void addPackageToPreferred(java.lang.String);
method public boolean addPermission(android.content.pm.PermissionInfo);
method public boolean addPermissionAsync(android.content.pm.PermissionInfo);
@@ -232,19 +198,10 @@
method public int checkPermission(java.lang.String, java.lang.String);
method public int checkSignatures(java.lang.String, java.lang.String);
method public int checkSignatures(int, int);
- method public void clearApplicationUserData(java.lang.String, android.content.pm.IPackageDataObserver);
- method public void clearCrossProfileIntentFilters(int);
method public void clearInstantAppCookie();
method public void clearPackagePreferredActivities(java.lang.String);
method public java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
- method public void deleteApplicationCacheFiles(java.lang.String, android.content.pm.IPackageDataObserver);
- method public void deleteApplicationCacheFilesAsUser(java.lang.String, int, android.content.pm.IPackageDataObserver);
- method public void deletePackage(java.lang.String, android.content.pm.IPackageDeleteObserver, int);
- method public void deletePackageAsUser(java.lang.String, android.content.pm.IPackageDeleteObserver, int, int);
method public void extendVerificationTimeout(int, int, long);
- method public void flushPackageRestrictionsAsUser(int);
- method public void freeStorage(java.lang.String, long, android.content.IntentSender);
- method public void freeStorageAndNotify(java.lang.String, long, android.content.pm.IPackageDataObserver);
method public android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.graphics.drawable.Drawable getActivityBanner(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.graphics.drawable.Drawable getActivityIcon(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -257,149 +214,75 @@
method public android.graphics.drawable.Drawable getApplicationBanner(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationBanner(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public int getApplicationEnabledSetting(java.lang.String);
- method public boolean getApplicationHiddenSettingAsUser(java.lang.String, android.os.UserHandle);
method public android.graphics.drawable.Drawable getApplicationIcon(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationIcon(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.ApplicationInfo getApplicationInfoAsUser(java.lang.String, int, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ChangedPackages getChangedPackages(int);
method public int getComponentEnabledSetting(android.content.ComponentName);
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
- method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public android.content.ComponentName getHomeActivities(java.util.List<android.content.pm.ResolveInfo>);
- method public int getInstallReason(java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
- method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
- method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public java.lang.String getInstallerPackageName(java.lang.String);
- method public java.lang.String getInstantAppAndroidId(java.lang.String, android.os.UserHandle);
method public byte[] getInstantAppCookie();
method public int getInstantAppCookieMaxBytes();
- method public int getInstantAppCookieMaxSize();
- method public android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
- method public android.content.ComponentName getInstantAppInstallerComponent();
- method public android.content.ComponentName getInstantAppResolverSettingsComponent();
- method public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
- method public int getIntentVerificationStatusAsUser(java.lang.String, int);
- method public android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
- method public int getMoveStatus(int);
method public java.lang.String getNameForUid(int);
- method public java.lang.String[] getNamesForUids(int[]);
- method public java.util.List<android.os.storage.VolumeInfo> getPackageCandidateVolumes(android.content.pm.ApplicationInfo);
- method public android.os.storage.VolumeInfo getPackageCurrentVolume(android.content.pm.ApplicationInfo);
method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.PackageInfo getPackageInfoAsUser(java.lang.String, int, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PackageInstaller getPackageInstaller();
- method public void getPackageSizeInfoAsUser(java.lang.String, int, android.content.pm.IPackageStatsObserver);
method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int getPackageUidAsUser(java.lang.String, int, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int getPackageUidAsUser(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] getPackagesForUid(int);
method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
- method public java.lang.String getPermissionControllerPackageName();
- method public int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
method public java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
- method public java.util.List<android.os.storage.VolumeInfo> getPrimaryStorageCandidateVolumes();
- method public android.os.storage.VolumeInfo getPrimaryStorageCurrentVolume();
method public android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.res.Resources getResourcesForApplicationAsUser(java.lang.String, int);
method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public java.lang.String getServicesSystemSharedLibraryPackageName();
method public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
- method public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibrariesAsUser(int, int);
- method public java.lang.String getSharedSystemSharedLibraryPackageName();
- method public android.content.pm.KeySet getSigningKeySet(java.lang.String);
method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method public java.lang.String[] getSystemSharedLibraryNames();
method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public int getUidForSharedUser(java.lang.String);
- method public android.graphics.drawable.Drawable getUserBadgeForDensity(android.os.UserHandle, int);
- method public android.graphics.drawable.Drawable getUserBadgeForDensityNoBackground(android.os.UserHandle, int);
method public android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int);
method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
- method public android.content.pm.VerifierDeviceIdentity getVerifierDeviceIdentity();
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean hasSystemFeature(java.lang.String);
method public boolean hasSystemFeature(java.lang.String, int);
- method public int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int installExistingPackageAsUser(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void installPackage(android.net.Uri, android.content.pm.IPackageInstallObserver, int, java.lang.String);
- method public void installPackage(android.net.Uri, android.app.PackageInstallObserver, int, java.lang.String);
method public boolean isInstantApp();
method public boolean isInstantApp(java.lang.String);
- method public boolean isPackageAvailable(java.lang.String);
- method public boolean isPackageSuspendedForUser(java.lang.String, int);
- method public boolean isPermissionReviewModeEnabled();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
- method public boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
- method public boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
- method public boolean isUpgrade();
- method public android.graphics.drawable.Drawable loadItemIcon(android.content.pm.PackageItemInfo, android.content.pm.ApplicationInfo);
- method public android.graphics.drawable.Drawable loadUnbadgedItemIcon(android.content.pm.PackageItemInfo, android.content.pm.ApplicationInfo);
- method public int movePackage(java.lang.String, android.os.storage.VolumeInfo);
- method public int movePrimaryStorage(android.os.storage.VolumeInfo);
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(android.content.ComponentName, android.content.Intent[], android.content.Intent, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
- method public void registerMoveCallback(android.content.pm.PackageManager.MoveCallback, android.os.Handler);
- method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public void removePackageFromPreferred(java.lang.String);
method public void removePermission(java.lang.String);
- method public void replacePreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
- method public android.content.pm.ResolveInfo resolveActivityAsUser(android.content.Intent, int, int);
method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
- method public android.content.pm.ProviderInfo resolveContentProviderAsUser(java.lang.String, int, int);
method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
- method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public void setApplicationCategoryHint(java.lang.String, int);
method public void setApplicationEnabledSetting(java.lang.String, int, int);
- method public boolean setApplicationHiddenSettingAsUser(java.lang.String, boolean, android.os.UserHandle);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
- method public boolean setInstantAppCookie(byte[]);
- method public java.lang.String[] setPackagesSuspendedAsUser(java.lang.String[], boolean, int);
- method public void setUpdateAvailable(java.lang.String, boolean);
- method public boolean shouldShowRequestPermissionRationale(java.lang.String);
- method public void unregisterMoveCallback(android.content.pm.PackageManager.MoveCallback);
method public void updateInstantAppCookie(byte[]);
- method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
- method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
- method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
method public void verifyPendingInstall(int, int);
}
@@ -411,5 +294,9 @@
method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
}
+ public deprecated class MockService {
+ method public static <T extends android.app.Service> void attachForTesting(android.app.Service, android.content.Context, java.lang.String, android.app.Application);
+ }
+
}
diff --git a/test-mock/api/android-test-mock-removed.txt b/test-mock/api/android-test-mock-removed.txt
index 5b358cf..bd109a8 100644
--- a/test-mock/api/android-test-mock-removed.txt
+++ b/test-mock/api/android-test-mock-removed.txt
@@ -8,6 +8,7 @@
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
method public deprecated java.lang.String getDefaultBrowserPackageName(int);
method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
+ method public boolean setInstantAppCookie(byte[]);
}
}
diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt
index b0e4bea..f6f7913 120000
--- a/test-mock/jarjar-rules.txt
+++ b/test-mock/jarjar-rules.txt
@@ -1 +1 @@
-../legacy-test/jarjar-rules.txt
\ No newline at end of file
+../test-base/jarjar-rules.txt
\ No newline at end of file
diff --git a/test-mock/src/android/test/mock/MockAccountManager.java b/test-mock/src/android/test/mock/MockAccountManager.java
new file mode 100644
index 0000000..c9b4c7b
--- /dev/null
+++ b/test-mock/src/android/test/mock/MockAccountManager.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.test.mock;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OnAccountsUpdateListener;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.os.Handler;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A mock {@link android.accounts.AccountManager} class.
+ *
+ * <p>Provided for use by {@code android.test.IsolatedContext}.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}
+ * tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ */
+@Deprecated
+public class MockAccountManager {
+
+ /**
+ * Create a new mock {@link AccountManager} instance.
+ *
+ * @param context the {@link Context} to which the returned object belongs.
+ * @return the new instance.
+ */
+ public static AccountManager newMockAccountManager(Context context) {
+ return new MockAccountManagerImpl(context);
+ }
+
+ private MockAccountManager() {
+ }
+
+ private static class MockAccountManagerImpl extends AccountManager {
+
+ MockAccountManagerImpl(Context context) {
+ super(context, null /* IAccountManager */, null /* handler */);
+ }
+
+ public void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener,
+ Handler handler, boolean updateImmediately) {
+ // do nothing
+ }
+
+ public Account[] getAccounts() {
+ return new Account[] {};
+ }
+
+ public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
+ final String type, final String[] features,
+ AccountManagerCallback<Account[]> callback, Handler handler) {
+ return new MockAccountManagerFuture<Account[]>(new Account[0]);
+ }
+
+ public String blockingGetAuthToken(Account account, String authTokenType,
+ boolean notifyAuthFailure)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return null;
+ }
+ }
+
+ /**
+ * A very simple AccountManagerFuture class
+ * that returns what ever was passed in
+ */
+ private static class MockAccountManagerFuture<T>
+ implements AccountManagerFuture<T> {
+
+ T mResult;
+
+ MockAccountManagerFuture(T result) {
+ mResult = result;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+
+ public boolean isDone() {
+ return true;
+ }
+
+ public T getResult()
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return mResult;
+ }
+
+ public T getResult(long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return getResult();
+ }
+ }
+}
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index d5f3ce8..b917fbd 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -277,4 +277,21 @@
public final IContentProvider getIContentProvider() {
return mIContentProvider;
}
+
+ /**
+ * Like {@link #attachInfo(Context, android.content.pm.ProviderInfo)}, but for use
+ * when directly instantiating the provider for testing.
+ *
+ * <p>Provided for use by {@code android.test.ProviderTestCase2} and
+ * {@code android.test.RenamingDelegatingContext}.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ */
+ @Deprecated
+ public static void attachInfoForTesting(
+ ContentProvider provider, Context context, ProviderInfo providerInfo) {
+ provider.attachInfoForTesting(context, providerInfo);
+ }
}
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 7e08f51..da70f0f 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -47,6 +47,7 @@
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
@@ -1184,4 +1185,12 @@
@Nullable DexModuleRegisterCallback callback) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public ArtManager getArtManager() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/test-mock/src/android/test/mock/MockService.java b/test-mock/src/android/test/mock/MockService.java
new file mode 100644
index 0000000..dbba4f3
--- /dev/null
+++ b/test-mock/src/android/test/mock/MockService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.test.mock;
+
+import android.app.Application;
+import android.app.Service;
+import android.content.Context;
+
+/**
+ * A mock {@link android.app.Service} class.
+ *
+ * <p>Provided for use by {@code android.test.ServiceTestCase}.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ */
+@Deprecated
+public class MockService {
+
+ public static <T extends Service> void attachForTesting(Service service, Context context,
+ String serviceClassName,
+ Application application) {
+ service.attach(
+ context,
+ null, // ActivityThread not actually used in Service
+ serviceClassName,
+ null, // token not needed when not talking with the activity manager
+ application,
+ null // mocked services don't talk with the activity manager
+ );
+ }
+
+ private MockService() {
+ }
+}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 104ae82..a2edb04 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -19,16 +19,40 @@
java_library {
name: "android.test.runner",
+ // Needs to be consistent with the repackaged version of this make target.
+ java_version: "1.8",
srcs: ["src/**/*.java"],
+ errorprone: {
+ javacflags: ["-Xep:DepAnn:ERROR"],
+ },
+
no_framework_libs: true,
libs: [
"framework",
- "legacy-test",
+ "android.test.base",
"android.test.mock",
],
}
+// Build the android.test.runner-minus-junit library
+// =================================================
+// This is only intended for inclusion in the legacy-android-test static
+// library and must not be used elsewhere.
+java_library {
+ name: "android.test.runner-minus-junit",
+
+ srcs: ["src/android/**/*.java"],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ "android.test.base",
+ "android.test.mock",
+ "junit",
+ ],
+}
+
// Build the repackaged.android.test.runner library
// ================================================
java_library_static {
@@ -37,4 +61,6 @@
static_libs: ["android.test.runner"],
jarjar_rules: "jarjar-rules.txt",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
}
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 6cf2d56..706f636 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -16,6 +16,9 @@
LOCAL_PATH:= $(call my-dir)
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
# Generate the stub source files for android.test.runner.stubs
# ============================================================
include $(CLEAR_VARS)
@@ -26,7 +29,7 @@
core-oj \
core-libart \
framework \
- legacy-test \
+ android.test.base \
android.test.mock \
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
@@ -65,13 +68,15 @@
LOCAL_MODULE := android.test.runner.stubs
LOCAL_JAVA_LIBRARIES := \
- legacy.test.stubs \
+ android.test.base.stubs \
android.test.mock.stubs \
LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SDK_VERSION := current
# Make sure to run droiddoc first to generate the stub source files.
LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_runner_api_gen_stamp)
+android_test_runner_api_gen_stamp :=
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -109,3 +114,8 @@
$(hide) $(ACP) $(ANDROID_TEST_RUNNER_OUTPUT_API_FILE) $(ANDROID_TEST_RUNNER_API_FILE)
@echo Copying removed.txt
$(hide) $(ACP) $(ANDROID_TEST_RUNNER_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_RUNNER_REMOVED_API_FILE)
+
+endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
+
+# additionally, build unit tests in a separate .apk
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test-runner/jarjar-rules.txt b/test-runner/jarjar-rules.txt
index b0e4bea..f6f7913 120000
--- a/test-runner/jarjar-rules.txt
+++ b/test-runner/jarjar-rules.txt
@@ -1 +1 @@
-../legacy-test/jarjar-rules.txt
\ No newline at end of file
+../test-base/jarjar-rules.txt
\ No newline at end of file
diff --git a/test-runner/src/android/test/ComparisonFailure.java b/test-runner/src/android/test/ComparisonFailure.java
index 3fa76f5..d86b700 100644
--- a/test-runner/src/android/test/ComparisonFailure.java
+++ b/test-runner/src/android/test/ComparisonFailure.java
@@ -19,8 +19,9 @@
/**
* Thrown when an assert equals for Strings failed.
*
- * @deprecated use junit.framework.ComparisonFailure
+ * @deprecated use org.junit.ComparisonFailure
*/
+@Deprecated
public class ComparisonFailure extends AssertionFailedError {
private junit.framework.ComparisonFailure mComparison;
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index 0b77c00..6e4c41e 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -17,12 +17,6 @@
package android.test;
import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OnAccountsUpdateListener;
-import android.accounts.OperationCanceledException;
-import android.accounts.Account;
import android.content.ContextWrapper;
import android.content.ContentResolver;
import android.content.Intent;
@@ -32,12 +26,10 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.os.Handler;
+import android.test.mock.MockAccountManager;
import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
import java.util.List;
@@ -52,7 +44,7 @@
public class IsolatedContext extends ContextWrapper {
private ContentResolver mResolver;
- private final MockAccountManager mMockAccountManager;
+ private final AccountManager mMockAccountManager;
private List<Intent> mBroadcastIntents = new ArrayList<>();
@@ -60,7 +52,7 @@
ContentResolver resolver, Context targetContext) {
super(targetContext);
mResolver = resolver;
- mMockAccountManager = new MockAccountManager();
+ mMockAccountManager = MockAccountManager.newMockAccountManager(IsolatedContext.this);
}
/** Returns the list of intents that were broadcast since the last call to this method. */
@@ -123,71 +115,6 @@
return null;
}
- private class MockAccountManager extends AccountManager {
- public MockAccountManager() {
- super(IsolatedContext.this, null /* IAccountManager */, null /* handler */);
- }
-
- public void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener,
- Handler handler, boolean updateImmediately) {
- // do nothing
- }
-
- public Account[] getAccounts() {
- return new Account[]{};
- }
-
- public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
- final String type, final String[] features,
- AccountManagerCallback<Account[]> callback, Handler handler) {
- return new MockAccountManagerFuture<Account[]>(new Account[0]);
- }
-
- public String blockingGetAuthToken(Account account, String authTokenType,
- boolean notifyAuthFailure)
- throws OperationCanceledException, IOException, AuthenticatorException {
- return null;
- }
-
-
- /**
- * A very simple AccountManagerFuture class
- * that returns what ever was passed in
- */
- private class MockAccountManagerFuture<T>
- implements AccountManagerFuture<T> {
-
- T mResult;
-
- public MockAccountManagerFuture(T result) {
- mResult = result;
- }
-
- public boolean cancel(boolean mayInterruptIfRunning) {
- return false;
- }
-
- public boolean isCancelled() {
- return false;
- }
-
- public boolean isDone() {
- return true;
- }
-
- public T getResult()
- throws OperationCanceledException, IOException, AuthenticatorException {
- return mResult;
- }
-
- public T getResult(long timeout, TimeUnit unit)
- throws OperationCanceledException, IOException, AuthenticatorException {
- return getResult();
- }
- }
-
- }
-
@Override
public File getFilesDir() {
return new File("/dev/null");
diff --git a/test-runner/src/android/test/ProviderTestCase2.java b/test-runner/src/android/test/ProviderTestCase2.java
index 1fa633e..be18b53 100644
--- a/test-runner/src/android/test/ProviderTestCase2.java
+++ b/test-runner/src/android/test/ProviderTestCase2.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
+import android.test.mock.MockContentProvider;
import android.test.mock.MockContext;
import android.test.mock.MockContentResolver;
import android.database.DatabaseUtils;
@@ -152,7 +153,7 @@
T instance = providerClass.newInstance();
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = authority;
- instance.attachInfoForTesting(context, providerInfo);
+ MockContentProvider.attachInfoForTesting(instance, context, providerInfo);
return instance;
}
diff --git a/test-runner/src/android/test/RenamingDelegatingContext.java b/test-runner/src/android/test/RenamingDelegatingContext.java
index fd33321..10ccebc 100644
--- a/test-runner/src/android/test/RenamingDelegatingContext.java
+++ b/test-runner/src/android/test/RenamingDelegatingContext.java
@@ -21,6 +21,7 @@
import android.content.ContentProvider;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
+import android.test.mock.MockContentProvider;
import android.util.Log;
import java.io.File;
@@ -71,7 +72,7 @@
if (allowAccessToExistingFilesAndDbs) {
mContext.makeExistingFilesAndDbsAccessible();
}
- mProvider.attachInfoForTesting(mContext, null);
+ MockContentProvider.attachInfoForTesting(mProvider, mContext, null);
return mProvider;
}
diff --git a/test-runner/src/android/test/ServiceTestCase.java b/test-runner/src/android/test/ServiceTestCase.java
index c8ff0f9..cd54955 100644
--- a/test-runner/src/android/test/ServiceTestCase.java
+++ b/test-runner/src/android/test/ServiceTestCase.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.test.mock.MockApplication;
+import android.test.mock.MockService;
import java.util.Random;
/**
@@ -163,14 +164,8 @@
if (getApplication() == null) {
setApplication(new MockApplication());
}
- mService.attach(
- getContext(),
- null, // ActivityThread not actually used in Service
- mServiceClass.getName(),
- null, // token not needed when not talking with the activity manager
- getApplication(),
- null // mocked services don't talk with the activity manager
- );
+ MockService.attachForTesting(
+ mService, getContext(), mServiceClass.getName(), getApplication());
assertNotNull(mService);
diff --git a/test-runner/src/android/test/TestSuiteProvider.java b/test-runner/src/android/test/TestSuiteProvider.java
index c74651c..12cfcb7 100644
--- a/test-runner/src/android/test/TestSuiteProvider.java
+++ b/test-runner/src/android/test/TestSuiteProvider.java
@@ -20,6 +20,11 @@
/**
* Implementors will know how to get a test suite.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
@Deprecated
public interface TestSuiteProvider {
diff --git a/test-runner/src/junit/runner/BaseTestRunner.java b/test-runner/src/junit/runner/BaseTestRunner.java
index e7e0431..b2fa16c 100644
--- a/test-runner/src/junit/runner/BaseTestRunner.java
+++ b/test-runner/src/junit/runner/BaseTestRunner.java
@@ -207,6 +207,7 @@
*
* @deprecated not present in JUnit4.10
*/
+ @Deprecated
public TestSuiteLoader getLoader() {
return new StandardTestSuiteLoader();
}
@@ -279,6 +280,7 @@
// BEGIN android-changed - add back this method for API compatibility
/** @deprecated not present in JUnit4.10 */
+ @Deprecated
public static boolean inVAJava() {
return false;
}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk
index 088f322..6e0d58a 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk
@@ -40,6 +40,6 @@
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wno-unused-parameter
-LOCAL_NDK_STL_VARIANT := stlport_static
+LOCAL_NDK_STL_VARIANT := c++_static
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 388f91a..261ea2e 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -66,9 +66,6 @@
super(SurfaceCompositionMeasuringActivity.class);
}
- private void testRestoreContexts() {
- }
-
@SmallTest
public void testSurfaceCompositionPerformance() {
Bundle status = new Bundle();
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 677585c..4907894 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -29,32 +29,35 @@
LOCAL_CERTIFICATE := platform
# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
+LOCAL_JNI_SHARED_LIBRARIES := \
+ android.hidl.token@1.0 \
libbacktrace \
libbase \
libbinder \
libc++ \
+ libcrypto \
libcutils \
+ libdexfile \
+ libframeworksnettestsjni \
+ libhidl-gen-utils \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
liblog \
liblzma \
libnativehelper \
libnetdaidl \
- libui \
- libunwind \
- libutils \
- libvndksupport \
- libcrypto \
- libhidl-gen-utils \
- libhidlbase \
- libhidltransport \
libpackagelistparser \
libpcre2 \
libselinux \
- libtinyxml2 \
+ libui \
+ libunwind \
+ libutils \
libvintf \
- libhwbinder \
+ libvndksupport \
+ libtinyxml2 \
libunwindstack \
- android.hidl.token@1.0
+ libutilscallstack
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/tests/net/OWNERS b/tests/net/OWNERS
index 6f77e04..ce50558 100644
--- a/tests/net/OWNERS
+++ b/tests/net/OWNERS
@@ -1,7 +1,6 @@
set noparent
ek@google.com
-hugobenichi@google.com
jchalard@google.com
lorenzo@google.com
satk@google.com
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index cc792cc..03a617c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -38,6 +38,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
@@ -217,7 +218,8 @@
// callback triggers
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(500).times(1)).onAvailable(any());
+ verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
+ any(NetworkCapabilities.class), any(LinkProperties.class));
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -244,7 +246,8 @@
// callback triggers
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(100).times(1)).onAvailable(any());
+ verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
+ any(NetworkCapabilities.class), any(LinkProperties.class));
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -335,6 +338,10 @@
static Message makeMessage(NetworkRequest req, int messageType) {
Bundle bundle = new Bundle();
bundle.putParcelable(NetworkRequest.class.getSimpleName(), req);
+ // Pass default objects as we don't care which get passed here
+ bundle.putParcelable(Network.class.getSimpleName(), new Network(1));
+ bundle.putParcelable(NetworkCapabilities.class.getSimpleName(), new NetworkCapabilities());
+ bundle.putParcelable(LinkProperties.class.getSimpleName(), new LinkProperties());
Message msg = Message.obtain();
msg.what = messageType;
msg.setData(bundle);
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index efc01f2a..f186ee5 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -17,6 +17,7 @@
package android.net;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -36,55 +37,63 @@
public void testDefaults() throws Exception {
IpSecConfig c = new IpSecConfig();
assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode());
- assertEquals("", c.getLocalAddress());
- assertEquals("", c.getRemoteAddress());
+ assertEquals("", c.getSourceAddress());
+ assertEquals("", c.getDestinationAddress());
assertNull(c.getNetwork());
assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType());
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId());
assertEquals(0, c.getEncapRemotePort());
assertEquals(0, c.getNattKeepaliveInterval());
- for (int direction :
- new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) {
- assertNull(c.getEncryption(direction));
- assertNull(c.getAuthentication(direction));
- assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction));
- }
+ assertNull(c.getEncryption());
+ assertNull(c.getAuthentication());
+ assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
+ }
+
+ private IpSecConfig getSampleConfig() {
+ IpSecConfig c = new IpSecConfig();
+ c.setMode(IpSecTransform.MODE_TUNNEL);
+ c.setSourceAddress("0.0.0.0");
+ c.setDestinationAddress("1.2.3.4");
+ c.setSpiResourceId(1984);
+ c.setEncryption(
+ new IpSecAlgorithm(
+ IpSecAlgorithm.CRYPT_AES_CBC,
+ new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
+ c.setAuthentication(
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_HMAC_MD5,
+ new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
+ c.setAuthenticatedEncryption(
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0, 1, 2, 3, 4
+ },
+ 128));
+ c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
+ c.setEncapSocketResourceId(7);
+ c.setEncapRemotePort(22);
+ c.setNattKeepaliveInterval(42);
+ c.setMarkValue(12);
+ c.setMarkMask(23);
+
+ return c;
+ }
+
+ @Test
+ public void testCopyConstructor() {
+ IpSecConfig original = getSampleConfig();
+ IpSecConfig copy = new IpSecConfig(original);
+
+ assertTrue(IpSecConfig.equals(original, copy));
+ assertFalse(original == copy);
}
@Test
public void testParcelUnparcel() throws Exception {
assertParcelingIsLossless(new IpSecConfig());
- IpSecConfig c = new IpSecConfig();
- c.setMode(IpSecTransform.MODE_TUNNEL);
- c.setLocalAddress("0.0.0.0");
- c.setRemoteAddress("1.2.3.4");
- c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
- c.setEncapSocketResourceId(7);
- c.setEncapRemotePort(22);
- c.setNattKeepaliveInterval(42);
- c.setEncryption(
- IpSecTransform.DIRECTION_OUT,
- new IpSecAlgorithm(
- IpSecAlgorithm.CRYPT_AES_CBC,
- new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
- c.setAuthentication(
- IpSecTransform.DIRECTION_OUT,
- new IpSecAlgorithm(
- IpSecAlgorithm.AUTH_HMAC_MD5,
- new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
- c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984);
- c.setEncryption(
- IpSecTransform.DIRECTION_IN,
- new IpSecAlgorithm(
- IpSecAlgorithm.CRYPT_AES_CBC,
- new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}));
- c.setAuthentication(
- IpSecTransform.DIRECTION_IN,
- new IpSecAlgorithm(
- IpSecAlgorithm.AUTH_HMAC_MD5,
- new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1}));
- c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99);
+ IpSecConfig c = getSampleConfig();
assertParcelingIsLossless(c);
}
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 0f40b45..cc3366f 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -81,15 +81,13 @@
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
when(mMockIpSecService.allocateSecurityParameterIndex(
- eq(IpSecTransform.DIRECTION_IN),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(DROID_SPI),
anyObject()))
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex droidSpi =
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, DROID_SPI);
assertEquals(DROID_SPI, droidSpi.getSpi());
droidSpi.close();
@@ -103,15 +101,13 @@
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
when(mMockIpSecService.allocateSecurityParameterIndex(
- eq(IpSecTransform.DIRECTION_OUT),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
anyObject()))
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex randomSpi =
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
assertEquals(DROID_SPI, randomSpi.getSpi());
@@ -124,16 +120,15 @@
* Throws resource unavailable exception
*/
@Test
- public void testAllocSpiResUnavaiableExeption() throws Exception {
+ public void testAllocSpiResUnavailableException() throws Exception {
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
when(mMockIpSecService.allocateSecurityParameterIndex(
- anyInt(), anyString(), anyInt(), anyObject()))
+ anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -143,15 +138,14 @@
* Throws spi unavailable exception
*/
@Test
- public void testAllocSpiSpiUnavaiableExeption() throws Exception {
+ public void testAllocSpiSpiUnavailableException() throws Exception {
IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
when(mMockIpSecService.allocateSecurityParameterIndex(
- anyInt(), anyString(), anyInt(), anyObject()))
+ anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -163,8 +157,7 @@
@Test
public void testRequestAllocInvalidSpi() throws Exception {
try {
- mIpSecManager.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0);
+ mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, 0);
fail("Able to allocate invalid spi");
} catch (IllegalArgumentException e) {
}
diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/tests/net/java/android/net/IpSecTransformTest.java
new file mode 100644
index 0000000..ffd1f06
--- /dev/null
+++ b/tests/net/java/android/net/IpSecTransformTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IpSecTransform}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class IpSecTransformTest {
+
+ @Test
+ public void testCreateTransformCopiesConfig() {
+ // Create a config with a few parameters to make sure it's not empty
+ IpSecConfig config = new IpSecConfig();
+ config.setSourceAddress("0.0.0.0");
+ config.setDestinationAddress("1.2.3.4");
+ config.setSpiResourceId(1984);
+
+ IpSecTransform preModification = new IpSecTransform(null, config);
+
+ config.setSpiResourceId(1985);
+ IpSecTransform postModification = new IpSecTransform(null, config);
+
+ assertFalse(IpSecTransform.equals(preModification, postModification));
+ }
+
+ @Test
+ public void testCreateTransformsWithSameConfigEqual() {
+ // Create a config with a few parameters to make sure it's not empty
+ IpSecConfig config = new IpSecConfig();
+ config.setSourceAddress("0.0.0.0");
+ config.setDestinationAddress("1.2.3.4");
+ config.setSpiResourceId(1984);
+
+ IpSecTransform config1 = new IpSecTransform(null, config);
+ IpSecTransform config2 = new IpSecTransform(null, config);
+
+ assertTrue(IpSecTransform.equals(config1, config2));
+ }
+}
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 52da79a..f3c22a5 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -79,6 +79,9 @@
assertTrue(source.isIdenticalDnses(target));
assertTrue(target.isIdenticalDnses(source));
+ assertTrue(source.isIdenticalPrivateDns(target));
+ assertTrue(target.isIdenticalPrivateDns(source));
+
assertTrue(source.isIdenticalRoutes(target));
assertTrue(target.isIdenticalRoutes(source));
@@ -91,6 +94,9 @@
assertTrue(source.isIdenticalMtu(target));
assertTrue(target.isIdenticalMtu(source));
+ assertTrue(source.isIdenticalTcpBufferSizes(target));
+ assertTrue(target.isIdenticalTcpBufferSizes(source));
+
// Check result of equals().
assertTrue(source.equals(target));
assertTrue(target.equals(source));
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 473dc538..04266c5 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -67,7 +67,7 @@
assertEquals(msg, t.expectedType, got);
if (got != MacAddress.TYPE_UNKNOWN) {
- assertEquals(got, MacAddress.fromBytes(t.addr).addressType());
+ assertEquals(got, MacAddress.fromBytes(t.addr).getAddressType());
}
}
}
@@ -172,7 +172,7 @@
final int iterations = 1000;
final String expectedAndroidOui = "da:a1:19";
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress();
+ MacAddress mac = MacAddress.createRandomUnicastAddressWithGoogleBase();
String stringRepr = mac.toString();
assertTrue(stringRepr + " expected to be a locally assigned address",
@@ -191,10 +191,19 @@
assertTrue(stringRepr + " expected to be a locally assigned address",
mac.isLocallyAssigned());
- assertEquals(MacAddress.TYPE_UNICAST, mac.addressType());
+ assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType());
assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
stringRepr.startsWith(expectedLocalOui));
}
+
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.createRandomUnicastAddress();
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType());
+ }
}
@Test
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index e6170cb..4c6a644 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -34,12 +34,15 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -189,4 +192,84 @@
assertEquals(20, NetworkCapabilities
.maxBandwidth(10, 20));
}
+
+ @Test
+ public void testSetUids() {
+ final NetworkCapabilities netCap = new NetworkCapabilities();
+ final Set<UidRange> uids = new ArraySet<>();
+ uids.add(new UidRange(50, 100));
+ uids.add(new UidRange(3000, 4000));
+ netCap.setUids(uids);
+ assertTrue(netCap.appliesToUid(50));
+ assertTrue(netCap.appliesToUid(80));
+ assertTrue(netCap.appliesToUid(100));
+ assertTrue(netCap.appliesToUid(3000));
+ assertTrue(netCap.appliesToUid(3001));
+ assertFalse(netCap.appliesToUid(10));
+ assertFalse(netCap.appliesToUid(25));
+ assertFalse(netCap.appliesToUid(49));
+ assertFalse(netCap.appliesToUid(101));
+ assertFalse(netCap.appliesToUid(2000));
+ assertFalse(netCap.appliesToUid(100000));
+
+ assertTrue(netCap.appliesToUidRange(new UidRange(50, 100)));
+ assertTrue(netCap.appliesToUidRange(new UidRange(70, 72)));
+ assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912)));
+ assertFalse(netCap.appliesToUidRange(new UidRange(1, 100)));
+ assertFalse(netCap.appliesToUidRange(new UidRange(49, 100)));
+ assertFalse(netCap.appliesToUidRange(new UidRange(1, 10)));
+ assertFalse(netCap.appliesToUidRange(new UidRange(60, 101)));
+ assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400)));
+
+ NetworkCapabilities netCap2 = new NetworkCapabilities();
+ assertFalse(netCap2.satisfiedByUids(netCap));
+ assertFalse(netCap2.equalsUids(netCap));
+ netCap2.setUids(uids);
+ assertTrue(netCap2.satisfiedByUids(netCap));
+ assertTrue(netCap.equalsUids(netCap2));
+ assertTrue(netCap2.equalsUids(netCap));
+
+ uids.add(new UidRange(600, 700));
+ netCap2.setUids(uids);
+ assertFalse(netCap2.satisfiedByUids(netCap));
+ assertFalse(netCap.appliesToUid(650));
+ assertTrue(netCap2.appliesToUid(650));
+ netCap.combineCapabilities(netCap2);
+ assertTrue(netCap2.satisfiedByUids(netCap));
+ assertTrue(netCap.appliesToUid(650));
+ assertFalse(netCap.appliesToUid(500));
+
+ assertFalse(new NetworkCapabilities().satisfiedByUids(netCap));
+ netCap.combineCapabilities(new NetworkCapabilities());
+ assertTrue(netCap.appliesToUid(500));
+ assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
+ assertFalse(netCap2.appliesToUid(500));
+ assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000)));
+ assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
+ }
+
+ @Test
+ public void testParcelNetworkCapabilities() {
+ final Set<UidRange> uids = new ArraySet<>();
+ uids.add(new UidRange(50, 100));
+ uids.add(new UidRange(3000, 4000));
+ final NetworkCapabilities netCap = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .setUids(uids)
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+ assertEqualsThroughMarshalling(netCap);
+ }
+
+ private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
+ Parcel p = Parcel.obtain();
+ netCap.writeToParcel(p, /* flags */ 0);
+ p.setDataPosition(0);
+ byte[] marshalledData = p.marshall();
+
+ p = Parcel.obtain();
+ p.unmarshall(marshalledData, 0, marshalledData.length);
+ p.setDataPosition(0);
+ assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap);
+ }
}
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 25289ba..035a4cd7 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
@@ -56,71 +59,75 @@
@Test
public void testFindIndex() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
- 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
- 1024L, 8L, 11)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 0L, 0L,
- 1024L, 8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
- 8L, 1024L, 8L, 12)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L,
- 8L, 1024L, 8L, 12);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
- ROAMING_YES));
+ ROAMING_YES, DEFAULT_NETWORK_YES));
assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO));
+ ROAMING_NO, DEFAULT_NETWORK_NO));
assertEquals(2, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES,
- ROAMING_NO));
+ ROAMING_NO, DEFAULT_NETWORK_YES));
assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO));
+ ROAMING_NO, DEFAULT_NETWORK_NO));
assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO));
+ ROAMING_NO, DEFAULT_NETWORK_YES));
assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO));
+ ROAMING_NO, DEFAULT_NETWORK_NO));
+ assertEquals(-1, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO));
}
@Test
public void testFindIndexHinted() {
final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
- 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
- 1024L, 8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
- 8L, 1024L, 8L, 12)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
.addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- 1024L, 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 0L, 0L,
- 1024L, 8L, 11)
- .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 0L, 0L,
- 1024L, 8L, 11)
- .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L,
- 8L, 1024L, 8L, 12)
- .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L,
- 8L, 1024L, 8L, 12);
+ DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
+ .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
+ .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
+ .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
+ .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
// verify that we correctly find across regardless of hinting
for (int hint = 0; hint < stats.size(); hint++) {
assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, hint));
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, hint));
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint));
assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, hint));
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE,
- METERED_NO, ROAMING_NO, hint));
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint));
assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
- METERED_NO, ROAMING_NO, hint));
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
- METERED_YES, ROAMING_NO, hint));
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, hint));
assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, hint));
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
assertEquals(7, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
- METERED_YES, ROAMING_YES, hint));
+ METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, hint));
assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE,
- METERED_NO, ROAMING_NO, hint));
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint));
+ assertEquals(-1, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
+ METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, hint));
}
}
@@ -131,50 +138,50 @@
assertEquals(0, stats.size());
assertEquals(4, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L, 1L,
- 2L, 2L, 3);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 2L, 2L,
- 2L, 2L, 4);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 3L,
- 3L, 2L, 2L, 5);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 3L,
- 3L, 2L, 2L, 5);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
assertEquals(4, stats.size());
assertEquals(4, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4L,
- 40L, 4L, 40L, 7);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5L,
- 50L, 4L, 40L, 8);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 6L,
- 60L, 5L, 50L, 10);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 7L,
- 70L, 5L, 50L, 11);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 7L,
- 70L, 5L, 50L, 11);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
assertEquals(9, stats.size());
assertTrue(stats.internalSize() >= 9);
assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 1L, 1L, 2L, 2L, 3);
+ DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 2L, 2L, 2L, 2L, 4);
+ DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
- 3L, 3L, 2L, 2L, 5);
+ DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES,
- ROAMING_YES, 3L, 3L, 2L, 2L, 5);
+ ROAMING_YES, DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 4L, 40L, 4L, 40L, 7);
+ DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 5L, 50L, 4L, 40L, 8);
+ DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 6L, 60L, 5L, 50L, 10);
+ DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
assertValues(stats, 7, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
- 7L, 70L, 5L, 50L, 11);
+ DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
assertValues(stats, 8, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES,
- ROAMING_YES, 7L, 70L, 5L, 50L, 11);
+ ROAMING_YES, DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
}
@Test
@@ -187,17 +194,17 @@
-128L, -1L, -1);
assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 384L, 3L, 128L, 1L, 9);
- assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO, 128L,
- 1L, 128L, 1L, 2);
+ DEFAULT_NETWORK_NO, 384L, 3L, 128L, 1L, 9);
+ assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 2);
// now try combining that should create row
stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 128L, 1L, 128L, 1L, 3);
+ DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 3);
stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 256L, 2L, 256L, 2L, 6);
+ DEFAULT_NETWORK_NO, 256L, 2L, 256L, 2L, 6);
}
@Test
@@ -213,10 +220,10 @@
final NetworkStats result = after.subtract(before);
// identical data should result in zero delta
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
- 0L, 0L, 0L, 0);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
- 0L, 0L, 0L, 0);
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
}
@Test
@@ -232,10 +239,10 @@
final NetworkStats result = after.subtract(before);
// expect delta between measurements
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L,
- 1L, 2L, 1L, 4);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 3L,
- 1L, 4L, 1L, 8);
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1L, 1L, 2L, 1L, 4);
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 3L, 1L, 4L, 1L, 8);
}
@Test
@@ -252,12 +259,12 @@
final NetworkStats result = after.subtract(before);
// its okay to have new rows
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
- 0L, 0L, 0L, 0);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
- 0L, 0L, 0L, 0);
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0);
assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 1024L, 8L, 1024L, 8L, 20);
+ DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 20);
}
@Test
@@ -274,7 +281,7 @@
// should silently drop omitted rows
assertEquals(1, result.size());
assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, 1L, 2L, 3L, 4L, 0);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 0);
assertEquals(4L, result.getTotalBytes());
}
@@ -301,21 +308,21 @@
assertEquals(64L, uidTag.getTotalBytes());
final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
- 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidMetered.getTotalBytes());
final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 32L, 0L,
- 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidRoaming.getTotalBytes());
}
@@ -331,38 +338,38 @@
@Test
public void testGroupedByIfaceAll() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
- .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, 0L,
- 2L, 20L)
- .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, 128L,
- 8L, 0L, 2L, 20L)
- .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, 128L, 8L, 0L,
- 2L, 20L);
+ .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
+ .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
+ .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
final NetworkStats grouped = uidStats.groupedByIface();
assertEquals(3, uidStats.size());
assertEquals(1, grouped.size());
assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- 384L, 24L, 0L, 6L, 0L);
+ DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 6L, 0L);
}
@Test
public void testGroupedByIface() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
- 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
- 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
- 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 128L, 8L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L,
- 8L, 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -370,59 +377,59 @@
assertEquals(2, grouped.size());
assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- 384L, 24L, 0L, 2L, 0L);
+ DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 2L, 0L);
assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- 1024L, 64L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_ALL, 1024L, 64L, 0L, 0L, 0L);
}
@Test
public void testAddAllValues() {
final NetworkStats first = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 32L,
- 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L,
- 0L, 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
final NetworkStats second = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L,
- 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L,
- 0L, 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
first.combineAllValues(second);
assertEquals(4, first.size());
- assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 64L,
- 0L, 0L, 0L, 0L);
+ assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L);
assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- 32L, 0L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L);
assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
- 64L, 0L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L);
assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 32L, 0L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L);
}
@Test
public void testGetTotal() {
final NetworkStats stats = new NetworkStats(TEST_START, 7)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L,
- 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
- 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L,
- 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 128L,
- 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L, 8L,
- 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L,
- 8L, 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
assertValues(stats.getTotal(null, 100), 1280L, 80L, 0L, 2L, 20L);
@@ -449,9 +456,9 @@
assertEquals(6, before.size());
assertEquals(2, after.size());
assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 128L, 8L, 0L, 0L, 0L);
- assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L,
- 8L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
+ assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
}
@Test
@@ -489,90 +496,90 @@
final String underlyingIface = "wlan0";
final int testTag1 = 8888;
NetworkStats delta = new NetworkStats(TEST_START, 17)
- .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 39605L, 46L,
- 12259L, 55L, 0L)
- .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
- 0L, 0L, 0L)
- .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 72667L, 197L,
- 43909L, 241L, 0L)
- .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 9297L,
- 17L, 4128L, 21L, 0L)
+ .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
+ .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+ .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
+ .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
// VPN package also uses some traffic through unprotected network.
- .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4983L, 10L,
- 1801L, 12L, 0L)
- .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L,
- 0L, 0L, 0L)
+ .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
+ .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
// Tag entries
- .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, 21691L, 41L,
- 13820L, 51L, 0L)
- .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, 1281L, 2L,
- 665L, 2L, 0L)
+ .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
+ .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
// Irrelevant entries
- .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1685L, 5L,
- 2070L, 6L, 0L)
+ .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
// Underlying Iface entries
- .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5178L,
- 8L, 2139L, 11L, 0L)
- .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L,
- 0L, 0L, 0L, 0L)
+ .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
+ .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
.addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 149873L, 287L, 59217L /* smaller than sum(tun0) */,
+ DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
299L /* smaller than sum(tun0) */, 0L)
.addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- 0L, 0L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
- assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
+ assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface));
assertEquals(20, delta.size());
// tunIface and TEST_IFACE entries are not changed.
assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 39605L, 46L, 12259L, 55L, 0L);
+ DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L);
assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- 0L, 0L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 72667L, 197L, 43909L, 241L, 0L);
+ DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L);
assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- 9297L, 17L, 4128L, 21L, 0L);
+ DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L);
assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 4983L, 10L, 1801L, 12L, 0L);
+ DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L);
assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- 0L, 0L, 0L, 0L, 0L);
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
- 21691L, 41L, 13820L, 51L, 0L);
+ DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L);
assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
- 1281L, 2L, 665L, 2L, 0L);
+ DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L);
assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 1685L, 5L, 2070L, 6L, 0L);
+ DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L);
// Existing underlying Iface entries are updated
assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, 44783L, 54L, 14178L, 62L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 44783L, 54L, 14178L, 62L, 0L);
assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
- ROAMING_NO, 0L, 0L, 0L, 0L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
// VPN underlying Iface entries are updated
assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, 28304L, 27L, 1L, 2L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 28304L, 27L, 1L, 2L, 0L);
assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
- ROAMING_NO, 0L, 0L, 0L, 0L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
// New entries are added for new application's underlying Iface traffic
assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, 72667L, 197L, 43123L, 227L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 72667L, 197L, 43123L, 227L, 0L);
assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO,
- ROAMING_NO, 9297L, 17L, 4054, 19L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 9297L, 17L, 4054, 19L, 0L);
assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, METERED_NO,
- ROAMING_NO, 21691L, 41L, 13572L, 48L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 21691L, 41L, 13572L, 48L, 0L);
assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, METERED_NO,
- ROAMING_NO, 1281L, 2L, 653L, 1L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1281L, 2L, 653L, 1L, 0L);
// New entries are added for debug purpose
assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
- ROAMING_NO, 39605L, 46L, 12039, 51, 0);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 39605L, 46L, 12039, 51, 0);
assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
- ROAMING_NO, 81964, 214, 47177, 246, 0);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 81964, 214, 47177, 246, 0);
assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL,
- ROAMING_ALL, 121569, 260, 59216, 297, 0);
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, 121569, 260, 59216, 297, 0);
}
@@ -587,79 +594,80 @@
final String underlyingIface = "wlan0";
NetworkStats delta = new NetworkStats(TEST_START, 9)
// 2 different apps sent/receive data via tun0.
- .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50000L, 25L,
- 100000L, 50L, 0L)
- .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 500L, 2L,
- 200L, 5L, 0L)
+ .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
+ .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
// VPN package resends data through the tunnel (with exaggerated overhead)
- .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 240000,
- 100L, 120000L, 60L, 0L)
+ .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
// 1 app already has some traffic on the underlying interface, the other doesn't yet
- .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1000L,
- 10L, 2000L, 20L, 0L)
+ .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
// Traffic through the underlying interface via the vpn app.
// This test should redistribute this data correctly.
.addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 75500L, 37L, 130000L, 70L, 0L);
+ DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
assertEquals(9, delta.size());
// tunIface entries should not be changed.
assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 50000L, 25L, 100000L, 50L, 0L);
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 500L, 2L, 200L, 5L, 0L);
+ DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L);
assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 240000L, 100L, 120000L, 60L, 0L);
+ DEFAULT_NETWORK_NO, 240000L, 100L, 120000L, 60L, 0L);
// Existing underlying Iface entries are updated
assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, 51000L, 35L, 102000L, 70L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 51000L, 35L, 102000L, 70L, 0L);
// VPN underlying Iface entries are updated
assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, 25000L, 10L, 29800L, 15L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 25000L, 10L, 29800L, 15L, 0L);
// New entries are added for new application's underlying Iface traffic
assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, 500L, 2L, 200L, 5L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L);
// New entries are added for debug purpose
assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
- ROAMING_NO, 50000L, 25L, 100000L, 50L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
- ROAMING_NO, 500, 2L, 200L, 5L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, 500, 2L, 200L, 5L, 0L);
assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL,
- ROAMING_ALL, 50500L, 27L, 100200L, 55, 0);
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0);
}
private static void assertContains(NetworkStats stats, String iface, int uid, int set,
- int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
- long txPackets, long operations) {
- int index = stats.findIndex(iface, uid, set, tag, metered, roaming);
+ int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+ long txBytes, long txPackets, long operations) {
+ int index = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork);
assertTrue(index != -1);
- assertValues(stats, index, iface, uid, set, tag, metered, roaming,
+ assertValues(stats, index, iface, uid, set, tag, metered, roaming, defaultNetwork,
rxBytes, rxPackets, txBytes, txPackets, operations);
}
private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
- int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
- long txPackets, long operations) {
+ int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+ long txBytes, long txPackets, long operations) {
final NetworkStats.Entry entry = stats.getValues(index, null);
- assertValues(entry, iface, uid, set, tag, metered, roaming);
+ assertValues(entry, iface, uid, set, tag, metered, roaming, defaultNetwork);
assertValues(entry, rxBytes, rxPackets, txBytes, txPackets, operations);
}
private static void assertValues(
NetworkStats.Entry entry, String iface, int uid, int set, int tag, int metered,
- int roaming) {
+ int roaming, int defaultNetwork) {
assertEquals(iface, entry.iface);
assertEquals(uid, entry.uid);
assertEquals(set, entry.set);
assertEquals(tag, entry.tag);
assertEquals(metered, entry.metered);
assertEquals(roaming, entry.roaming);
+ assertEquals(defaultNetwork, entry.defaultNetwork);
}
private static void assertValues(NetworkStats.Entry entry, long rxBytes, long rxPackets,
diff --git a/tests/net/java/android/net/NetworkTest.java b/tests/net/java/android/net/NetworkTest.java
index bacf986..94d01e9 100644
--- a/tests/net/java/android/net/NetworkTest.java
+++ b/tests/net/java/android/net/NetworkTest.java
@@ -147,9 +147,9 @@
// Adjust as necessary to test an implementation's specific constants.
// When running with runtest, "adb logcat -s TestRunner" can be useful.
- assertEquals(4311403230L, one.getNetworkHandle());
- assertEquals(8606370526L, two.getNetworkHandle());
- assertEquals(12901337822L, three.getNetworkHandle());
+ assertEquals(7700664333L, one.getNetworkHandle());
+ assertEquals(11995631629L, two.getNetworkHandle());
+ assertEquals(16290598925L, three.getNetworkHandle());
}
private static <T> void assertNotEqual(T t1, T t2) {
diff --git a/core/tests/coretests/src/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/NetworkUtilsTest.java
rename to tests/net/java/android/net/NetworkUtilsTest.java
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/tests/net/java/android/net/RouteInfoTest.java
similarity index 100%
rename from core/tests/coretests/src/android/net/RouteInfoTest.java
rename to tests/net/java/android/net/RouteInfoTest.java
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 725ddb9..9b75a50 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -35,6 +35,7 @@
import android.net.ip.IpManager;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
+import android.net.util.InterfaceParams;
import android.os.ConditionVariable;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -62,7 +63,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
-import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
@@ -635,7 +635,7 @@
public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback,
IpConnectivityLog log) throws Exception {
- super(config, NetworkInterface.getByName("lo"), ipManagerCallback, log);
+ super(config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
index 54776db..e65585f 100644
--- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
+++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;
+import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -54,8 +55,8 @@
}
IpReachabilityMonitor makeMonitor() {
- return new IpReachabilityMonitor(
- "fake0", 1, mHandler, mLog, mCallback, null, mDependencies);
+ final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null);
+ return new IpReachabilityMonitor(ifParams, mHandler, mLog, mCallback, null, mDependencies);
}
@Test
diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
index 38d3d74..f9b7ec8 100644
--- a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
+++ b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.net.MacAddress;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
@@ -36,9 +37,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ConnectivityPacketSummaryTest {
- private static final byte[] MYHWADDR = {
- asByte(0x80), asByte(0x7a), asByte(0xbf), asByte(0x6f), asByte(0x48), asByte(0xf3)
- };
+ private static final MacAddress MYHWADDR = MacAddress.fromString("80:7a:bf:6f:48:f3");
private String getSummary(String hexBytes) {
hexBytes = hexBytes.replaceAll("\\s+", "");
diff --git a/tests/net/java/android/net/util/InterfaceParamsTest.java b/tests/net/java/android/net/util/InterfaceParamsTest.java
new file mode 100644
index 0000000..21728af
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceParamsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceParamsTest {
+ @Test
+ public void testNullInterfaceReturnsNull() {
+ assertNull(InterfaceParams.getByName(null));
+ }
+
+ @Test
+ public void testNonExistentInterfaceReturnsNull() {
+ assertNull(InterfaceParams.getByName("doesnotexist0"));
+ }
+
+ @Test
+ public void testLoopback() {
+ final InterfaceParams ifParams = InterfaceParams.getByName("lo");
+ assertNotNull(ifParams);
+ assertEquals("lo", ifParams.name);
+ assertTrue(ifParams.index > 0);
+ assertNotNull(ifParams.macAddr);
+ assertTrue(ifParams.defaultMtu >= NetworkConstants.ETHER_MTU);
+ }
+}
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index fb2bd79..b14f550 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -16,6 +16,7 @@
package com.android.internal.net;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_ALL;
@@ -66,7 +67,7 @@
IoUtils.deleteContents(mTestProc);
}
- mFactory = new NetworkStatsFactory(mTestProc);
+ mFactory = new NetworkStatsFactory(mTestProc, false);
}
@After
@@ -115,6 +116,20 @@
}
@Test
+ public void testNetworkStatsSummary() throws Exception {
+ stageFile(R.raw.net_dev_typical, file("net/dev"));
+
+ final NetworkStats stats = mFactory.readNetworkStatsIfaceDev();
+ assertEquals(6, stats.size());
+ assertStatsEntry(stats, "lo", UID_ALL, SET_ALL, TAG_NONE, 8308L, 8308L);
+ assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 1507570L, 489339L);
+ assertStatsEntry(stats, "ifb0", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
+ assertStatsEntry(stats, "ifb1", UID_ALL, SET_ALL, TAG_NONE, 52454L, 0L);
+ assertStatsEntry(stats, "sit0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
+ assertStatsEntry(stats, "ip6tnl0", UID_ALL, SET_ALL, TAG_NONE, 0L, 0L);
+ }
+
+ @Test
public void testNetworkStatsSingle() throws Exception {
stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all"));
@@ -240,7 +255,8 @@
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long txBytes) {
- final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
+ final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO);
if (i < 0) {
fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)",
iface, uid, set, tag));
@@ -252,7 +268,8 @@
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO);
+ final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO);
if (i < 0) {
fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)",
iface, uid, set, tag));
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 113cd37..28f8122 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -35,6 +35,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
@@ -44,10 +45,12 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static com.android.internal.util.TestUtils.waitForIdleHandler;
+import static com.android.internal.util.TestUtils.waitForIdleLooper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -55,14 +58,20 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -77,6 +86,7 @@
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
+import android.net.ConnectivityThread;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.IpPrefix;
@@ -95,6 +105,7 @@
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
+import android.net.UidRange;
import android.net.metrics.IpConnectivityLog;
import android.net.util.MultinetworkPolicyTracker;
import android.os.ConditionVariable;
@@ -116,14 +127,17 @@
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.DefaultNetworkMetrics;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -132,6 +146,7 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -174,8 +189,11 @@
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+ @Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
+ private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
+
// This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
// do not go through ConnectivityService but talk to netd directly, so they don't automatically
// reflect the state of our test ConnectivityService.
@@ -263,6 +281,7 @@
waitForIdle(mWiFiNetworkAgent, timeoutMs);
waitForIdle(mEthernetNetworkAgent, timeoutMs);
waitForIdleHandler(mService.mHandlerThread, timeoutMs);
+ waitForIdleLooper(ConnectivityThread.getInstanceLooper(), timeoutMs);
}
public void waitForIdle(MockNetworkAgent agent, long timeoutMs) {
@@ -303,6 +322,7 @@
// This test has an inherent race condition in it, and cannot be enabled for continuous testing
// or presubmit tests. It is kept for manual runs and documentation purposes.
+ @Ignore
public void verifyThatNotWaitingForIdleCausesRaceConditions() {
// Bring up a network that we can use to send messages to ConnectivityService.
ConditionVariable cv = waitForConnectivityBroadcasts(1);
@@ -349,7 +369,7 @@
MockNetworkAgent(int transport, LinkProperties linkProperties) {
final int type = transportToLegacyType(transport);
- final String typeName = ConnectivityManager.getNetworkTypeName(type);
+ final String typeName = ConnectivityManager.getNetworkTypeName(transport);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(transport);
@@ -366,6 +386,9 @@
case TRANSPORT_WIFI_AWARE:
mScore = 20;
break;
+ case TRANSPORT_VPN:
+ mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
+ break;
default:
throw new UnsupportedOperationException("unimplemented network type");
}
@@ -427,6 +450,11 @@
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
+ public void setUids(Set<UidRange> uids) {
+ mNetworkCapabilities.setUids(uids);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
public void setSignalStrength(int signalStrength) {
mNetworkCapabilities.setSignalStrength(signalStrength);
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
@@ -504,6 +532,11 @@
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
}
+ public void resume() {
+ mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
public void disconnect() {
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -545,6 +578,10 @@
assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS));
return mRedirectUrl;
}
+
+ public NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
}
/**
@@ -872,7 +909,7 @@
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
mService = new WrappedConnectivityService(mServiceContext,
- mock(INetworkManagementService.class),
+ mNetworkManagementService,
mStatsService,
mock(INetworkPolicyManager.class),
mock(IpConnectivityLog.class));
@@ -1249,6 +1286,7 @@
NETWORK_CAPABILITIES,
LINK_PROPERTIES,
SUSPENDED,
+ RESUMED,
LOSING,
LOST,
UNAVAILABLE
@@ -1320,6 +1358,11 @@
}
@Override
+ public void onNetworkResumed(Network network) {
+ setLastCallback(CallbackState.RESUMED, network, null);
+ }
+
+ @Override
public void onLosing(Network network, int maxMsToLive) {
setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
}
@@ -1381,39 +1424,90 @@
return null;
}
- void expectAvailableCallbacks(
- MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+ // Expects onAvailable and the callbacks that follow it. These are:
+ // - onSuspended, iff the network was suspended when the callbacks fire.
+ // - onCapabilitiesChanged.
+ // - onLinkPropertiesChanged.
+ //
+ // @param agent the network to expect the callbacks on.
+ // @param expectSuspended whether to expect a SUSPENDED callback.
+ // @param expectValidated the expected value of the VALIDATED capability in the
+ // onCapabilitiesChanged callback.
+ // @param timeoutMs how long to wait for the callbacks.
+ void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
+ boolean expectValidated, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
}
- expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+ if (expectValidated) {
+ expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
+ } else {
+ expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
+ }
expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
}
- void expectAvailableCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ // Expects the available callbacks (validated), plus onSuspended.
+ void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
+ expectAvailableCallbacks(agent, true, expectValidated, TIMEOUT_MS);
}
- void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+ void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, true, TIMEOUT_MS);
}
- void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, false, TIMEOUT_MS);
+ }
+
+ // Expects the available callbacks (where the onCapabilitiesChanged must contain the
+ // VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
+ // one we just sent.
+ // TODO: this is likely a bug. Fix it and remove this method.
+ void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
+ expectCallback(CallbackState.AVAILABLE, agent, TIMEOUT_MS);
+ NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ expectCallback(CallbackState.LINK_PROPERTIES, agent, TIMEOUT_MS);
+ NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ assertEquals(nc1, nc2);
+ }
+
+ // Expects the available callbacks where the onCapabilitiesChanged must not have validated,
+ // then expects another onCapabilitiesChanged that has the validated bit set. This is used
+ // when a network connects and satisfies a callback, and then immediately validates.
+ void expectAvailableThenValidatedCallbacks(MockNetworkAgent agent) {
+ expectAvailableCallbacksUnvalidated(agent);
expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
}
- void expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
- CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
- NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
- assertTrue(nc.hasCapability(capability));
+ NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
+ return expectCapabilitiesWith(capability, agent, TIMEOUT_MS);
}
- void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
- CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+ NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent,
+ int timeoutMs) {
+ CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+ NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
+ assertTrue(nc.hasCapability(capability));
+ return nc;
+ }
+
+ NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+ return expectCapabilitiesWithout(capability, agent, TIMEOUT_MS);
+ }
+
+ NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent,
+ int timeoutMs) {
+ CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
assertFalse(nc.hasCapability(capability));
+ return nc;
+ }
+
+ void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) {
+ CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+ assertTrue(fn.test((NetworkCapabilities) cbi.arg));
}
void assertNoCallback() {
@@ -1450,8 +1544,8 @@
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- genericNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
- cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1465,8 +1559,8 @@
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- wifiNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1489,8 +1583,8 @@
// Test validated networks
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- genericNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1502,10 +1596,10 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- wifiNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1541,32 +1635,32 @@
mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.connect(true);
// We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
// We then get LOSING when wifi validates and cell is outscored.
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
for (int i = 0; i < 4; i++) {
MockNetworkAgent oldNetwork, newNetwork;
@@ -1583,7 +1677,7 @@
callback.expectCallback(CallbackState.LOSING, oldNetwork);
// TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
// longer lingering?
- defaultCallback.expectAvailableCallbacks(newNetwork);
+ defaultCallback.expectAvailableCallbacksValidated(newNetwork);
assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
}
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1603,7 +1697,7 @@
// Disconnect our test networks.
mWiFiNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1619,22 +1713,22 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false); // Score: 10
- callback.expectAvailableCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false); // Score: 20
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
@@ -1642,33 +1736,33 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1676,12 +1770,12 @@
// If a network is lingering, and we add and remove a request from it, resume lingering.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
@@ -1700,7 +1794,7 @@
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Cell is now the default network. Pin it with a cell-specific request.
noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1709,8 +1803,8 @@
// Now connect wifi, and expect it to become the default network.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// The default request is lingering on cell, but nothing happens to cell, and we send no
// callbacks for it, because it's kept up by cellRequest.
callback.assertNoCallback();
@@ -1726,14 +1820,14 @@
// Register a TRACK_DEFAULT request and check that it does not affect lingering.
TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(trackDefaultCallback);
- trackDefaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
- trackDefaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
// Let linger run its course.
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
@@ -1750,6 +1844,51 @@
}
@Test
+ public void testNetworkGoesIntoBackgroundAfterLinger() {
+ setMobileDataAlwaysOn(true);
+ NetworkRequest request = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .build();
+ TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ // Wifi comes up and cell lingers.
+ mWiFiNetworkAgent.connect(true);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+ // File a request for cellular, then release it.
+ NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ NetworkCallback noopCallback = new NetworkCallback();
+ mCm.requestNetwork(cellRequest, noopCallback);
+ mCm.unregisterNetworkCallback(noopCallback);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+
+ // Let linger run its course.
+ callback.assertNoCallback();
+ final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
+ callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent,
+ lingerTimeoutMs);
+
+ // Clean up.
+ mCm.unregisterNetworkCallback(defaultCallback);
+ mCm.unregisterNetworkCallback(callback);
+ }
+
+ @Test
public void testExplicitlySelected() {
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
@@ -1760,13 +1899,13 @@
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up unvalidated wifi with explicitlySelected=true.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// Cell Remains the default.
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1789,7 +1928,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the
// network to disconnect.
@@ -1800,7 +1939,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false);
mWiFiNetworkAgent.connect(true);
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1809,7 +1948,7 @@
// TODO: fix this.
mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
@@ -1982,7 +2121,7 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mCellNetworkAgent.connectWithoutInternet();
- networkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
@@ -2011,7 +2150,7 @@
MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
- networkCallback.expectAvailableCallbacks(mmsNetworkAgent);
+ networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
@@ -2038,7 +2177,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
@@ -2051,7 +2190,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
@@ -2061,9 +2200,7 @@
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- // TODO: Investigate only sending available callbacks.
- validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -2087,7 +2224,7 @@
// Bring up wifi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- validatedCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Check that calling startCaptivePortalApp does nothing.
@@ -2098,7 +2235,7 @@
// Turn into a captive portal.
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 302;
mCm.reportNetworkConnectivity(wifiNetwork, false);
- captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Check that startCaptivePortalApp sends the expected intent.
@@ -2111,7 +2248,7 @@
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204;
CaptivePortal c = (CaptivePortal) intent.getExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
c.reportCaptivePortalDismissed();
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(validatedCallback);
@@ -2154,7 +2291,7 @@
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
// But there should be no CaptivePortal callback.
captivePortalCallback.assertNoCallback();
}
@@ -2192,14 +2329,14 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- cEmpty1.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty2.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty3.expectAvailableCallbacks(mWiFiNetworkAgent);
- cEmpty4.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty3.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertNoCallbacks(cFoo, cBar);
mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo"));
- cFoo.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
@@ -2208,7 +2345,7 @@
mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar"));
cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- cBar.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
@@ -2337,14 +2474,14 @@
// Bring up cell and expect CALLBACK_AVAILABLE.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up wifi and expect CALLBACK_AVAILABLE.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
- defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
@@ -2354,7 +2491,7 @@
// Bring up cell. Expect no default network callback, since it won't be the default.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
// Bring down wifi. Expect the default network callback to notified of LOST wifi
@@ -2362,7 +2499,7 @@
mWiFiNetworkAgent.disconnect();
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -2383,7 +2520,7 @@
// We should get onAvailable(), onCapabilitiesChanged(), and
// onLinkPropertiesChanged() in rapid succession. Additionally, we
// should get onCapabilitiesChanged() when the mobile network validates.
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Update LinkProperties.
@@ -2396,15 +2533,30 @@
// Suspend the network.
mCellNetworkAgent.suspend();
+ cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED,
+ mCellNetworkAgent);
cellNetworkCallback.expectCallback(CallbackState.SUSPENDED, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Register a garden variety default network request.
- final TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
+ TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
// We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
// as well as onNetworkSuspended() in rapid succession.
- dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent);
+ dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true);
+ dfltNetworkCallback.assertNoCallback();
+ mCm.unregisterNetworkCallback(dfltNetworkCallback);
+
+ mCellNetworkAgent.resume();
+ cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED,
+ mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.RESUMED, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
+ dfltNetworkCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
+ // This time onNetworkSuspended should not be called.
+ dfltNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
dfltNetworkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(dfltNetworkCallback);
@@ -2444,18 +2596,18 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- fgCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
// When wifi connects, cell lingers.
- callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- fgCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
@@ -2479,8 +2631,8 @@
// is currently delivered before the onAvailable() callbacks.
// TODO: Fix this.
cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
- cellCallback.expectAvailableCallbacks(mCellNetworkAgent);
- fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Expect a network capabilities update with FOREGROUND, because the most recent
// request causes its state to change.
callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
@@ -2500,7 +2652,7 @@
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mCm.unregisterNetworkCallback(callback);
@@ -2640,7 +2792,7 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
testFactory.expectAddRequests(2); // Because the cell request changes score twice.
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
testFactory.waitForNetworkRequests(2);
assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us.
@@ -2731,16 +2883,15 @@
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
- defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
Network cellNetwork = mCellNetworkAgent.getNetwork();
// Bring up validated wifi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
@@ -2761,18 +2912,18 @@
// that we switch back to cell.
tracker.configRestrictsAvoidBadWifi = false;
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// Switch back to a restrictive carrier.
tracker.configRestrictsAvoidBadWifi = true;
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
// Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
mCm.setAvoidUnvalidated(wifiNetwork);
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2783,9 +2934,8 @@
mWiFiNetworkAgent.disconnect();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
- validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
@@ -2799,7 +2949,7 @@
tracker.reevaluate();
// We now switch to cell.
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2810,17 +2960,17 @@
// We switch to wifi and then to cell.
Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
tracker.reevaluate();
- defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedWifiCallback.assertNoCallback();
mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2862,7 +3012,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, timeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, timeoutMs);
// pass timeout and validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
@@ -2883,7 +3033,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
final int assertTimeoutMs = 100;
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, assertTimeoutMs);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3033,6 +3183,9 @@
InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888");
+ final int validKaInterval = 15;
+ final int invalidKaInterval = 9;
+
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("wlan12");
lp.addLinkAddress(new LinkAddress(myIPv6, 64));
@@ -3047,36 +3200,37 @@
PacketKeepalive ka;
// Attempt to start keepalives with invalid parameters and check for errors.
- ka = mCm.startNattKeepalive(notMyNet, 25, callback, myIPv4, 1234, dstIPv4);
+ ka = mCm.startNattKeepalive(notMyNet, validKaInterval, callback, myIPv4, 1234, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
- ka = mCm.startNattKeepalive(myNet, 19, callback, notMyIPv4, 1234, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, invalidKaInterval, callback, myIPv4, 1234, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_INVALID_INTERVAL);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 1234, dstIPv6);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 1234, dstIPv6);
callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv6);
- callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); // NAT-T is IPv4-only.
+ // NAT-T is only supported for IPv4.
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv6);
+ callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
// Check that a started keepalive can be stopped.
mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS);
ka.stop();
@@ -3084,7 +3238,7 @@
// Check that deleting the IP address stops the keepalive.
LinkProperties bogusLp = new LinkProperties(lp);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25));
bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25));
@@ -3093,7 +3247,7 @@
mWiFiNetworkAgent.sendLinkProperties(lp);
// Check that a started keepalive is stopped correctly when the network disconnects.
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
mWiFiNetworkAgent.disconnect();
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
@@ -3110,7 +3264,7 @@
mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
// Check things work as expected when the keepalive is stopped and the network disconnects.
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
ka.stop();
mWiFiNetworkAgent.disconnect();
@@ -3124,13 +3278,14 @@
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
- ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4);
+ ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
// The second one gets slot 2.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
TestKeepaliveCallback callback2 = new TestKeepaliveCallback();
- PacketKeepalive ka2 = mCm.startNattKeepalive(myNet, 25, callback2, myIPv4, 6789, dstIPv4);
+ PacketKeepalive ka2 = mCm.startNattKeepalive(
+ myNet, validKaInterval, callback2, myIPv4, 6789, dstIPv4);
callback2.expectStarted();
// Now stop the first one and create a third. This also gets slot 1.
@@ -3139,7 +3294,8 @@
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
TestKeepaliveCallback callback3 = new TestKeepaliveCallback();
- PacketKeepalive ka3 = mCm.startNattKeepalive(myNet, 25, callback3, myIPv4, 9876, dstIPv4);
+ PacketKeepalive ka3 = mCm.startNattKeepalive(
+ myNet, validKaInterval, callback3, myIPv4, 9876, dstIPv4);
callback3.expectStarted();
ka2.stop();
@@ -3370,7 +3526,7 @@
// Bring up wifi aware network.
wifiAware.connect(false, false);
- callback.expectAvailableCallbacks(wifiAware);
+ callback.expectAvailableCallbacksUnvalidated(wifiAware);
assertNull(mCm.getActiveNetworkInfo());
assertNull(mCm.getActiveNetwork());
@@ -3458,37 +3614,91 @@
@Test
public void testStatsIfacesChanged() throws Exception {
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+
+ Network[] onlyCell = new Network[]{mCellNetworkAgent.getNetwork()};
+ Network[] onlyWifi = new Network[]{mWiFiNetworkAgent.getNetwork()};
// Simple connection should have updated ifaces
mCellNetworkAgent.connect(false);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
+ reset(mStatsService);
+
+ // Default network switch should update ifaces.
+ mWiFiNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyWifi);
+ reset(mStatsService);
+
+ // Disconnect should update ifaces.
+ mWiFiNetworkAgent.disconnect();
+ waitForIdle();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
reset(mStatsService);
// Captive portal change shouldn't update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
waitForIdle();
- verify(mStatsService, never()).forceUpdateIfaces();
+ verify(mStatsService, never()).forceUpdateIfaces(onlyCell);
reset(mStatsService);
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
reset(mStatsService);
}
+ @Test
+ public void testBasicDnsConfigurationPushed() throws Exception {
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ waitForIdle();
+ verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
+ anyInt(), any(), any(), any(), anyBoolean(), anyString());
+
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName("test_rmnet_data0");
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ mCellNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+ // CS tells netd about the empty DNS config for this network.
+ assertEmpty(mStringArrayCaptor.getValue());
+ reset(mNetworkManagementService);
+
+ cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+ assertEquals(1, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
+ reset(mNetworkManagementService);
+
+ cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+ assertEquals(2, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+ new String[]{"2001:db8::1", "192.0.2.1"}));
+ reset(mNetworkManagementService);
+ }
+
private void checkDirectlyConnectedRoutes(Object callbackObj,
Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
assertTrue(callbackObj instanceof LinkProperties);
@@ -3528,4 +3738,75 @@
return;
}
}
+
+ @Test
+ public void testVpnNetworkActive() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+ final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+ final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest genericRequest = new NetworkRequest.Builder().build();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_VPN).build();
+ mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+ mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ vpnNetworkCallback.assertNoCallback();
+
+ // TODO : check callbacks agree with the return value of mCm.getActiveNetwork().
+ // Right now this is not possible because establish() is not adequately instrumented
+ // in this test.
+
+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ vpnNetworkAgent.setUids(ranges);
+ vpnNetworkAgent.connect(false);
+
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ wifiNetworkCallback.assertNoCallback();
+ vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+
+ genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+ vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent);
+
+ ranges.clear();
+ vpnNetworkAgent.setUids(ranges);
+
+ genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ wifiNetworkCallback.assertNoCallback();
+ vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+
+ ranges.add(new UidRange(uid, uid));
+ vpnNetworkAgent.setUids(ranges);
+
+ genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ wifiNetworkCallback.assertNoCallback();
+ vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+
+ mWiFiNetworkAgent.disconnect();
+
+ genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ vpnNetworkCallback.assertNoCallback();
+
+ vpnNetworkAgent.disconnect();
+
+ genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ wifiNetworkCallback.assertNoCallback();
+ vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+
+ mCm.unregisterNetworkCallback(genericNetworkCallback);
+ mCm.unregisterNetworkCallback(wifiNetworkCallback);
+ mCm.unregisterNetworkCallback(vpnNetworkCallback);
+ }
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 80e42a3..66e0955 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -32,7 +31,6 @@
import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecSpiResponse;
-import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.NetworkUtils;
import android.os.Binder;
@@ -54,14 +52,14 @@
@RunWith(Parameterized.class)
public class IpSecServiceParameterizedTest {
- private static final int TEST_SPI_OUT = 0xD1201D;
- private static final int TEST_SPI_IN = TEST_SPI_OUT + 1;
+ private static final int TEST_SPI = 0xD1201D;
- private final String mRemoteAddr;
+ private final String mDestinationAddr;
+ private final String mSourceAddr;
@Parameterized.Parameters
public static Collection ipSecConfigs() {
- return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
+ return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}});
}
private static final byte[] AEAD_KEY = {
@@ -96,11 +94,9 @@
private static final IpSecAlgorithm AEAD_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
- private static final int[] DIRECTIONS =
- new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
-
- public IpSecServiceParameterizedTest(String remoteAddr) {
- mRemoteAddr = remoteAddr;
+ public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) {
+ mSourceAddr = sourceAddr;
+ mDestinationAddr = destAddr;
}
@Before
@@ -116,44 +112,35 @@
@Test
public void testIpSecServiceReserveSpi() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
assertEquals(IpSecManager.Status.OK, spiResp.status);
- assertEquals(TEST_SPI_OUT, spiResp.spi);
+ assertEquals(TEST_SPI, spiResp.spi);
}
@Test
public void testReleaseSecurityParameterIndex() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(spiResp.resourceId),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI),
anyInt(),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
+ anyInt());
// Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
@@ -169,17 +156,12 @@
@Test
public void testSecurityParameterIndexBinderDeath() throws Exception {
- when(mMockNetd.ipSecAllocateSpi(
- anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
- anyString(),
- eq(mRemoteAddr),
- eq(TEST_SPI_OUT)))
- .thenReturn(TEST_SPI_OUT);
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
+ .thenReturn(TEST_SPI);
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+ mDestinationAddr, TEST_SPI, new Binder());
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -191,10 +173,11 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(spiResp.resourceId),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI),
anyInt(),
- anyString(),
- anyString(),
- eq(TEST_SPI_OUT));
+ anyInt());
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
@@ -206,14 +189,12 @@
}
}
- private int getNewSpiResourceId(int direction, String remoteAddress, int returnSpi)
- throws Exception {
- when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt()))
+ private int getNewSpiResourceId(String remoteAddress, int returnSpi) throws Exception {
+ when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), anyString(), anyInt()))
.thenReturn(returnSpi);
IpSecSpiResponse spi =
mIpSecService.allocateSecurityParameterIndex(
- direction,
NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(),
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
new Binder());
@@ -221,62 +202,36 @@
}
private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception {
- config.setSpiResourceId(
- IpSecTransform.DIRECTION_OUT,
- getNewSpiResourceId(IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT));
- config.setSpiResourceId(
- IpSecTransform.DIRECTION_IN,
- getNewSpiResourceId(IpSecTransform.DIRECTION_IN, mRemoteAddr, TEST_SPI_IN));
- config.setRemoteAddress(mRemoteAddr);
+ config.setSpiResourceId(getNewSpiResourceId(mDestinationAddr, TEST_SPI));
+ config.setSourceAddress(mSourceAddr);
+ config.setDestinationAddress(mDestinationAddr);
}
private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception {
- for (int direction : DIRECTIONS) {
- config.setEncryption(direction, CRYPT_ALGO);
- config.setAuthentication(direction, AUTH_ALGO);
- }
+ config.setEncryption(CRYPT_ALGO);
+ config.setAuthentication(AUTH_ALGO);
}
@Test
- public void testCreateTransportModeTransform() throws Exception {
+ public void testCreateTransform() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verify(mMockNetd)
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
- anyLong(),
- eq(TEST_SPI_OUT),
- eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
- eq(AUTH_KEY),
anyInt(),
- eq(IpSecAlgorithm.CRYPT_AES_CBC),
- eq(CRYPT_KEY),
- anyInt(),
- eq(""),
- eq(new byte[] {}),
- eq(0),
+ eq(TEST_SPI),
anyInt(),
anyInt(),
- anyInt());
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- anyLong(),
- eq(TEST_SPI_IN),
eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
eq(AUTH_KEY),
anyInt(),
@@ -292,47 +247,26 @@
}
@Test
- public void testCreateTransportModeTransformAead() throws Exception {
+ public void testCreateTransformAead() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
- ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_OUT, AEAD_ALGO);
- ipSecConfig.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
+ ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verify(mMockNetd)
.ipSecAddSecurityAssociation(
eq(createTransformResp.resourceId),
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
- anyLong(),
- eq(TEST_SPI_OUT),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(""),
- eq(new byte[] {}),
- eq(0),
- eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(AEAD_KEY),
+ anyInt(),
+ eq(TEST_SPI),
anyInt(),
anyInt(),
- anyInt(),
- anyInt());
- verify(mMockNetd)
- .ipSecAddSecurityAssociation(
- eq(createTransformResp.resourceId),
- anyInt(),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- anyLong(),
- eq(TEST_SPI_IN),
eq(""),
eq(new byte[] {}),
eq(0),
@@ -347,88 +281,49 @@
anyInt());
}
- @Test
- public void testCreateInvalidConfigAeadWithAuth() throws Exception {
- IpSecConfig ipSecConfig = new IpSecConfig();
- addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-
- for (int direction : DIRECTIONS) {
- ipSecConfig.setAuthentication(direction, AUTH_ALGO);
- ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
- }
-
- try {
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- fail(
- "IpSecService should have thrown an error on authentication being"
- + " enabled with authenticated encryption");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
- public void testCreateInvalidConfigAeadWithCrypt() throws Exception {
- IpSecConfig ipSecConfig = new IpSecConfig();
- addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-
- for (int direction : DIRECTIONS) {
- ipSecConfig.setEncryption(direction, CRYPT_ALGO);
- ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
- }
-
- try {
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- fail(
- "IpSecService should have thrown an error on encryption being"
- + " enabled with authenticated encryption");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
- public void testCreateInvalidConfigAeadWithAuthAndCrypt() throws Exception {
- IpSecConfig ipSecConfig = new IpSecConfig();
- addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-
- for (int direction : DIRECTIONS) {
- ipSecConfig.setAuthentication(direction, AUTH_ALGO);
- ipSecConfig.setEncryption(direction, CRYPT_ALGO);
- ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
- }
-
- try {
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- fail(
- "IpSecService should have thrown an error on authentication and encryption being"
- + " enabled with authenticated encryption");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
- public void testDeleteTransportModeTransform() throws Exception {
+ public void testCreateTwoTransformsWithSameSpis() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
- mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
+ mIpSecService.createTransform(ipSecConfig, new Binder());
+ assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+ // Attempting to create transform a second time with the same SPIs should throw an error...
+ try {
+ mIpSecService.createTransform(ipSecConfig, new Binder());
+ fail("IpSecService should have thrown an error for reuse of SPI");
+ } catch (IllegalStateException expected) {
+ }
+
+ // ... even if the transform is deleted
+ mIpSecService.deleteTransform(createTransformResp.resourceId);
+ try {
+ mIpSecService.createTransform(ipSecConfig, new Binder());
+ fail("IpSecService should have thrown an error for reuse of SPI");
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ @Test
+ public void testDeleteTransform() throws Exception {
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder());
+ mIpSecService.deleteTransform(createTransformResp.resourceId);
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(TEST_SPI),
+ anyInt(),
+ anyInt());
// Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
@@ -450,7 +345,7 @@
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -463,17 +358,11 @@
verify(mMockNetd)
.ipSecDeleteSecurityAssociation(
eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecDeleteSecurityAssociation(
- eq(createTransformResp.resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(TEST_SPI),
+ anyInt(),
+ anyInt());
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
@@ -493,34 +382,26 @@
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+ mIpSecService.createTransform(ipSecConfig, new Binder());
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
int resourceId = createTransformResp.resourceId;
- mIpSecService.applyTransportModeTransform(pfd, resourceId);
+ mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
verify(mMockNetd)
.ipSecApplyTransportModeTransform(
eq(pfd.getFileDescriptor()),
eq(resourceId),
- eq(IpSecTransform.DIRECTION_OUT),
+ eq(IpSecManager.DIRECTION_OUT),
anyString(),
anyString(),
- eq(TEST_SPI_OUT));
- verify(mMockNetd)
- .ipSecApplyTransportModeTransform(
- eq(pfd.getFileDescriptor()),
- eq(resourceId),
- eq(IpSecTransform.DIRECTION_IN),
- anyString(),
- anyString(),
- eq(TEST_SPI_IN));
+ eq(TEST_SPI));
}
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransform(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 5d1e10e..2c94a60 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -35,6 +35,8 @@
import android.content.Context;
import android.net.INetd;
+import android.net.IpSecAlgorithm;
+import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecSpiResponse;
import android.net.IpSecTransform;
@@ -76,6 +78,33 @@
private static final InetAddress INADDR_ANY;
+ private static final byte[] AEAD_KEY = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x73, 0x61, 0x6C, 0x74
+ };
+ private static final byte[] CRYPT_KEY = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+ };
+ private static final byte[] AUTH_KEY = {
+ 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
+ 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
+ };
+
+ private static final IpSecAlgorithm AUTH_ALGO =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
+ private static final IpSecAlgorithm CRYPT_ALGO =
+ new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ private static final IpSecAlgorithm AEAD_ALGO =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+
static {
try {
INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -137,6 +166,7 @@
mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
udpEncapResp.fileDescriptor.close();
+ // Verify quota and RefcountedResource objects cleaned up
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
@@ -150,10 +180,8 @@
@Test
public void testUdpEncapsulationSocketBinderDeath() throws Exception {
- int localport = findUnusedPort();
-
IpSecUdpEncapResponse udpEncapResp =
- mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
+ mIpSecService.openUdpEncapsulationSocket(0, new Binder());
IpSecService.UserRecord userRecord =
mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -163,6 +191,7 @@
refcountedRecord.binderDied();
+ // Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
try {
userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
@@ -270,9 +299,122 @@
}
@Test
- public void testDeleteInvalidTransportModeTransform() throws Exception {
+ public void testValidateAlgorithmsAuth() {
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthentication(AUTH_ALGO);
+ mIpSecService.validateAlgorithms(config);
+
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setAuthentication(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void testValidateAlgorithmsCrypt() {
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setEncryption(CRYPT_ALGO);
+ mIpSecService.validateAlgorithms(config);
+
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setEncryption(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void testValidateAlgorithmsAead() {
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ mIpSecService.validateAlgorithms(config);
+
+ // Validate that incorrect algorithm types fails
+ for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
+ try {
+ config = new IpSecConfig();
+ config.setAuthenticatedEncryption(algo);
+ mIpSecService.validateAlgorithms(config);
+ fail("Did not throw exception on invalid algorithm type");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void testValidateAlgorithmsAuthCrypt() {
+ // Validate that correct algorithm type succeeds
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthentication(AUTH_ALGO);
+ config.setEncryption(CRYPT_ALGO);
+ mIpSecService.validateAlgorithms(config);
+ }
+
+ @Test
+ public void testValidateAlgorithmsNoAlgorithms() {
+ IpSecConfig config = new IpSecConfig();
try {
- mIpSecService.deleteTransportModeTransform(1);
+ mIpSecService.validateAlgorithms(config);
+ fail("Expected exception; no algorithms specified");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testValidateAlgorithmsAeadWithAuth() {
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setAuthentication(AUTH_ALGO);
+ try {
+ mIpSecService.validateAlgorithms(config);
+ fail("Expected exception; both AEAD and auth algorithm specified");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testValidateAlgorithmsAeadWithCrypt() {
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setEncryption(CRYPT_ALGO);
+ try {
+ mIpSecService.validateAlgorithms(config);
+ fail("Expected exception; both AEAD and crypt algorithm specified");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
+ IpSecConfig config = new IpSecConfig();
+ config.setAuthenticatedEncryption(AEAD_ALGO);
+ config.setAuthentication(AUTH_ALGO);
+ config.setEncryption(CRYPT_ALGO);
+ try {
+ mIpSecService.validateAlgorithms(config);
+ fail("Expected exception; AEAD, auth and crypt algorithm specified");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testDeleteInvalidTransform() throws Exception {
+ try {
+ mIpSecService.deleteTransform(1);
fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) {
}
@@ -281,7 +423,7 @@
@Test
public void testRemoveTransportModeTransform() throws Exception {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
- mIpSecService.removeTransportModeTransform(pfd, 1);
+ mIpSecService.removeTransportModeTransforms(pfd);
verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
}
@@ -294,7 +436,7 @@
try {
IpSecSpiResponse spiResp =
mIpSecService.allocateSecurityParameterIndex(
- IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
+ address, DROID_SPI, new Binder());
fail("Invalid address was passed through IpSecService validation: " + address);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
@@ -366,7 +508,6 @@
// tracks the resource ID.
when(mMockNetd.ipSecAllocateSpi(
anyInt(),
- eq(IpSecTransform.DIRECTION_OUT),
anyString(),
eq(InetAddress.getLoopbackAddress().getHostAddress()),
anyInt()))
@@ -375,7 +516,6 @@
for (int i = 0; i < MAX_NUM_SPIS; i++) {
IpSecSpiResponse newSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + i,
new Binder());
@@ -391,7 +531,6 @@
// Try to reserve one more SPI, and should fail.
IpSecSpiResponse extraSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
new Binder());
@@ -405,7 +544,6 @@
// Should successfully reserve one more spi.
extraSpi =
mIpSecService.allocateSecurityParameterIndex(
- 0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
new Binder());
@@ -497,4 +635,25 @@
verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
}
+
+ @Test
+ public void testReserveNetId() {
+ int start = mIpSecService.TUN_INTF_NETID_START;
+ for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
+ assertEquals(start + i, mIpSecService.reserveNetId());
+ }
+
+ // Check that resource exhaustion triggers an exception
+ try {
+ mIpSecService.reserveNetId();
+ fail("Did not throw error for all netIds reserved");
+ } catch (IllegalStateException expected) {
+ }
+
+ // Now release one and try again
+ int releasedNetId =
+ mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
+ mIpSecService.releaseNetId(releasedNetId);
+ assertEquals(releasedNetId, mIpSecService.reserveNetId());
+ }
}
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index 2ac73db..56a075b 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -99,8 +99,12 @@
@After
public void tearDown() throws Exception {
- if (mSocket != null) mSocket.close();
- if (mServerSocket != null) mServerSocket.close();
+ mNMService.shutdown();
+ // Once NetworkManagementService#shutdown() actually does something and shutdowns
+ // the underlying NativeDaemonConnector, the block below should be uncommented.
+ // if (mOutputStream != null) mOutputStream.close();
+ // if (mSocket != null) mSocket.close();
+ // if (mServerSocket != null) mServerSocket.close();
}
/**
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 10d6deb..8359fe2 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -632,6 +632,7 @@
when(nai.getCurrentScore()).thenReturn(score);
nai.linkProperties = new LinkProperties();
nai.networkCapabilities = new NetworkCapabilities();
+ nai.lastValidated = true;
for (int t : BitUtils.unpackBits(transports)) {
nai.networkCapabilities.addTransportType(t);
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index c29363c..1dbf9b2 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -22,6 +22,7 @@
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
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;
@@ -436,11 +437,13 @@
.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));
setMockedNetworks(networks);
@@ -454,6 +457,7 @@
assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -463,6 +467,7 @@
assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
assertTrue(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);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -472,6 +477,7 @@
assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
@@ -481,6 +487,7 @@
assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
}
/**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 9c10264..da0a48a 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -195,7 +195,7 @@
final NetworkStats.Entry entry = new NetworkStats.Entry();
final NetworkIdentitySet identSet = new NetworkIdentitySet();
identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TEST_IMSI, null, false, true));
+ TEST_IMSI, null, false, true, true));
int myUid = Process.myUid();
int otherUidInSameUser = Process.myUid() + 1;
@@ -447,7 +447,7 @@
final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS);
final NetworkIdentitySet ident = new NetworkIdentitySet();
ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null,
- false, true));
+ false, true, true));
large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B,
new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0));
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index 2be5dae..185c3eb 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -18,6 +18,8 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_DEFAULT;
@@ -224,6 +226,15 @@
Mockito.verifyZeroInteractions(mockBinder);
}
+ private NetworkIdentitySet makeTestIdentSet() {
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */, true /* metered */,
+ true /* defaultNetwork */));
+ return identSet;
+ }
+
@Test
public void testUpdateStats_initialSample_doesNotNotify() throws Exception {
DataUsageRequest inputRequest = new DataUsageRequest(
@@ -235,10 +246,7 @@
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- NetworkIdentitySet identSet = new NetworkIdentitySet();
- identSet.add(new NetworkIdentity(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+ NetworkIdentitySet identSet = makeTestIdentSet();
mActiveIfaces.put(TEST_IFACE, identSet);
// Baseline
@@ -263,10 +271,7 @@
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- NetworkIdentitySet identSet = new NetworkIdentitySet();
- identSet.add(new NetworkIdentity(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+ NetworkIdentitySet identSet = makeTestIdentSet();
mActiveIfaces.put(TEST_IFACE, identSet);
// Baseline
@@ -298,10 +303,7 @@
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- NetworkIdentitySet identSet = new NetworkIdentitySet();
- identSet.add(new NetworkIdentity(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+ NetworkIdentitySet identSet = makeTestIdentSet();
mActiveIfaces.put(TEST_IFACE, identSet);
// Baseline
@@ -334,17 +336,14 @@
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- NetworkIdentitySet identSet = new NetworkIdentitySet();
- identSet.add(new NetworkIdentity(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+ NetworkIdentitySet identSet = makeTestIdentSet();
mActiveUidIfaces.put(TEST_IFACE, identSet);
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
@@ -352,7 +351,8 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
@@ -371,17 +371,14 @@
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- NetworkIdentitySet identSet = new NetworkIdentitySet();
- identSet.add(new NetworkIdentity(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+ NetworkIdentitySet identSet = makeTestIdentSet();
mActiveUidIfaces.put(TEST_IFACE, identSet);
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
@@ -389,7 +386,8 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
@@ -407,17 +405,14 @@
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- NetworkIdentitySet identSet = new NetworkIdentitySet();
- identSet.add(new NetworkIdentity(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+ NetworkIdentitySet identSet = makeTestIdentSet();
mActiveUidIfaces.put(TEST_IFACE, identSet);
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
@@ -425,7 +420,8 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
@@ -444,17 +440,14 @@
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
- NetworkIdentitySet identSet = new NetworkIdentitySet();
- identSet.add(new NetworkIdentity(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
+ NetworkIdentitySet identSet = makeTestIdentSet();
mActiveUidIfaces.put(TEST_IFACE, identSet);
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
@@ -462,8 +455,8 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
.addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
- ROAMING_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES,
- 2L, 0L);
+ ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index ecc9932..47c3455 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -21,6 +21,9 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_NO;
@@ -67,6 +70,7 @@
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsSession;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -136,6 +140,12 @@
private static final int UID_BLUE = 1002;
private static final int UID_GREEN = 1003;
+
+ private static final Network WIFI_NETWORK = new Network(100);
+ private static final Network MOBILE_NETWORK = new Network(101);
+ private static final Network[] NETWORKS_WIFI = new Network[]{ WIFI_NETWORK };
+ private static final Network[] NETWORKS_MOBILE = new Network[]{ MOBILE_NETWORK };
+
private static final long WAIT_TIMEOUT = 2 * 1000; // 2 secs
private static final int INVALID_TYPE = -1;
@@ -231,7 +241,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -278,7 +288,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -306,10 +316,10 @@
// verify service recorded history
assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
- assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L,
- 2L, 4);
- assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L,
- 256L, 2L, 6);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6);
assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
@@ -331,10 +341,10 @@
// after systemReady(), we should have historical stats loaded again
assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
- assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L,
- 2L, 4);
- assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L,
- 256L, 2L, 6);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6);
assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
}
@@ -356,7 +366,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// modify some number on wifi, and trigger poll event
@@ -401,7 +411,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_MOBILE);
// create some traffic on first network
@@ -439,7 +449,7 @@
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_MOBILE);
forcePollAndWaitForIdle();
@@ -481,7 +491,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// create some traffic
@@ -543,7 +553,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_MOBILE);
// create some traffic
@@ -573,7 +583,7 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_MOBILE);
forcePollAndWaitForIdle();
@@ -605,7 +615,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// create some traffic for two apps
@@ -641,12 +651,12 @@
NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(3, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50L,
- 5L, 50L, 5L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 10L,
- 1L, 10L, 1L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 50L, 5L, 50L, 5L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 10L, 1L, 10L, 1L, 1);
assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 2048L, 16L, 1024L, 8L, 0);
+ DEFAULT_NETWORK_YES, 2048L, 16L, 1024L, 8L, 0);
// now verify that recent history only contains one uid
final long currentTime = currentTimeMillis();
@@ -654,7 +664,7 @@
sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
assertEquals(1, stats.size());
assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- 1024L, 8L, 512L, 4L, 0);
+ DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0);
}
@Test
@@ -667,7 +677,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// create some initial traffic
@@ -708,14 +718,14 @@
final NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(4, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L,
- 2L, 128L, 2L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L,
- 1L, 64L, 1L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1);
assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- 32L, 2L, 32L, 2L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO, 1L,
- 1L, 1L, 1L, 1);
+ DEFAULT_NETWORK_YES, 32L, 2L, 32L, 2L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 1L, 1L, 1L, 1L, 1);
}
@Test
@@ -728,7 +738,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// create some initial traffic
@@ -736,14 +746,14 @@
expectCurrentTime();
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
- // Note that all traffic from NetworkManagementService is tagged as METERED_NO and
- // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
- // on top by inspecting the iface properties.
+ // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO
+ // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
+ // We layer them on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L,
- 2L, 128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L,
- 1L, 64L, 1L, 0L));
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
forcePollAndWaitForIdle();
@@ -755,9 +765,9 @@
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(2, stats.size());
assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
- 128L, 2L, 128L, 2L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 64L,
- 1L, 64L, 1L, 1);
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1);
}
@Test
@@ -770,7 +780,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_MOBILE);
// Create some traffic
@@ -783,9 +793,9 @@
// on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
- 128L, 2L, 128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, 64L,
- 1L, 64L, 1L, 0L));
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
forcePollAndWaitForIdle();
// verify service recorded history
@@ -796,9 +806,9 @@
sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(2, stats.size());
assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_YES,
- 128L, 2L, 128L, 2L, 0);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES, 64L,
- 1L, 64L, 1L, 0);
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0);
}
@Test
@@ -811,7 +821,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_MOBILE);
// create some tethering traffic
@@ -856,7 +866,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces();
+ mService.forceUpdateIfaces(NETWORKS_WIFI);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -977,18 +987,18 @@
// verify summary API
final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end);
assertValues(stats, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- rxBytes, rxPackets, txBytes, txPackets, operations);
+ DEFAULT_NETWORK_ALL, rxBytes, rxPackets, txBytes, txPackets, operations);
}
private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets,
long txBytes, long txPackets, int operations) throws Exception {
- assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, rxBytes, rxPackets,
- txBytes, txPackets, operations);
+ assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
+ rxBytes, rxPackets, txBytes, txPackets, operations);
}
private void assertUidTotal(NetworkTemplate template, int uid, int set, int metered,
- int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations)
- throws Exception {
+ int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes,
+ long txPackets, int operations) throws Exception {
// verify history API
final NetworkStatsHistory history = mSession.getHistoryForUid(
template, uid, set, TAG_NONE, FIELD_ALL);
@@ -998,8 +1008,8 @@
// verify summary API
final NetworkStats stats = mSession.getSummaryForAllUid(
template, Long.MIN_VALUE, Long.MAX_VALUE, false);
- assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, rxBytes, rxPackets,
- txBytes, txPackets, operations);
+ assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, defaultNetwork,
+ rxBytes, rxPackets, txBytes, txPackets, operations);
}
private void expectSystemReady() throws Exception {
@@ -1097,8 +1107,8 @@
}
private static void assertValues(NetworkStats stats, String iface, int uid, int set,
- int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes,
- long txPackets, int operations) {
+ int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+ long txBytes, long txPackets, int operations) {
final NetworkStats.Entry entry = new NetworkStats.Entry();
final int[] sets;
if (set == SET_ALL) {
@@ -1121,12 +1131,22 @@
meterings = new int[] { metered };
}
+ final int[] defaultNetworks;
+ if (defaultNetwork == DEFAULT_NETWORK_ALL) {
+ defaultNetworks = new int[] { DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES,
+ DEFAULT_NETWORK_NO };
+ } else {
+ defaultNetworks = new int[] { defaultNetwork };
+ }
+
for (int s : sets) {
for (int r : roamings) {
for (int m : meterings) {
- final int i = stats.findIndex(iface, uid, s, tag, m, r);
- if (i != -1) {
- entry.add(stats.getValues(i, null));
+ for (int d : defaultNetworks) {
+ final int i = stats.findIndex(iface, uid, s, tag, m, r, d);
+ if (i != -1) {
+ entry.add(stats.getValues(i, null));
+ }
}
}
}
@@ -1161,7 +1181,7 @@
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
- return new NetworkState(info, prop, capabilities, null, null, TEST_SSID);
+ return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
}
private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1178,7 +1198,7 @@
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
- return new NetworkState(info, prop, capabilities, null, subscriberId, null);
+ return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
}
private static NetworkState buildMobile4gState(String iface) {
@@ -1189,7 +1209,7 @@
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
- return new NetworkState(info, prop, capabilities, null, null, null);
+ return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null);
}
private NetworkStats buildEmptyStats() {
diff --git a/tests/net/res/raw/net_dev_typical b/tests/net/res/raw/net_dev_typical
new file mode 100644
index 0000000..290bf03
--- /dev/null
+++ b/tests/net/res/raw/net_dev_typical
@@ -0,0 +1,8 @@
+Inter-| Receive | Transmit
+ face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
+ lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0
+rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0
+ ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0
+ ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0
+ sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0
+ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0
diff --git a/tools/aapt2/filter/AbiFilter.h b/tools/aapt2/filter/AbiFilter.h
index d875cb2..2832711 100644
--- a/tools/aapt2/filter/AbiFilter.h
+++ b/tools/aapt2/filter/AbiFilter.h
@@ -33,6 +33,8 @@
*/
class AbiFilter : public IPathFilter {
public:
+ virtual ~AbiFilter() = default;
+
/** Factory method to create a filter from a list of configuration::Abi. */
static std::unique_ptr<AbiFilter> FromAbiList(const std::vector<configuration::Abi>& abi_list);
diff --git a/tools/aapt2/filter/Filter.h b/tools/aapt2/filter/Filter.h
index d737dc9..f932f9c 100644
--- a/tools/aapt2/filter/Filter.h
+++ b/tools/aapt2/filter/Filter.h
@@ -27,7 +27,7 @@
/** A filter to be applied to a path segment. */
class IPathFilter {
public:
- ~IPathFilter() = default;
+ virtual ~IPathFilter() = default;
/** Returns true if the path should be kept. */
virtual bool Keep(const std::string& path) = 0;
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
index ee0e36c..81a0773 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
@@ -18,7 +18,6 @@
import java.util.LinkedList;
import java.util.List;
import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.TryCatchBlockSorter;
@@ -101,7 +100,7 @@
try {
a.analyze(owner, mn);
} catch (AnalyzerException e) {
- e.printStackTrace();
+ throw new RuntimeException("Locked region code injection: " + e.getMessage(), e);
}
InsnList instructions = mn.instructions;
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
new file mode 100644
index 0000000..00fb8aa
--- /dev/null
+++ b/tools/sdkparcelables/Android.bp
@@ -0,0 +1,22 @@
+java_binary_host {
+ name: "sdkparcelables",
+ manifest: "manifest.txt",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "asm-6.0",
+ ],
+}
+
+java_library_host {
+ name: "sdkparcelables_test",
+ manifest: "manifest.txt",
+ srcs: [
+ "tests/**/*.kt",
+ ],
+ static_libs: [
+ "sdkparcelables",
+ "junit",
+ ],
+}
diff --git a/tools/sdkparcelables/manifest.txt b/tools/sdkparcelables/manifest.txt
new file mode 100644
index 0000000..cd5420c
--- /dev/null
+++ b/tools/sdkparcelables/manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.sdkparcelables.MainKt
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
new file mode 100644
index 0000000..d75aea5
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.sdkparcelables
+
+import org.objectweb.asm.ClassVisitor
+import java.util.*
+
+data class Ancestors(val superName: String?, val interfaces: List<String>?)
+
+/** A class that implements an ASM ClassVisitor that collects super class and
+ * implemented interfaces for each class that it visits.
+ */
+class AncestorCollector(api: Int, dest: ClassVisitor?) : ClassVisitor(api, dest) {
+ private val _ancestors = LinkedHashMap<String, Ancestors>()
+
+ val ancestors: Map<String, Ancestors>
+ get() = _ancestors
+
+ override fun visit(version: Int, access: Int, name: String?, signature: String?,
+ superName: String?, interfaces: Array<out String>?) {
+ name!!
+
+ val old = _ancestors.put(name, Ancestors(superName, interfaces?.toList()))
+ if (old != null) {
+ throw RuntimeException("class $name already found")
+ }
+
+ super.visit(version, access, name, signature, superName, interfaces)
+ }
+}
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
new file mode 100644
index 0000000..22e8d78
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.sdkparcelables
+
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.Opcodes
+import java.io.File
+import java.io.IOException
+import java.util.zip.ZipFile
+
+fun main(args: Array<String>) {
+ if (args.size != 2) {
+ usage()
+ }
+
+ val zipFileName = args[0]
+ val aidlFileName = args[1]
+
+ val zipFile: ZipFile
+
+ try {
+ zipFile = ZipFile(zipFileName)
+ } catch (e: IOException) {
+ System.err.println("error reading input jar: ${e.message}")
+ kotlin.system.exitProcess(2)
+ }
+
+ val ancestorCollector = AncestorCollector(Opcodes.ASM6, null)
+
+ for (entry in zipFile.entries()) {
+ if (entry.name.endsWith(".class")) {
+ val reader = ClassReader(zipFile.getInputStream(entry))
+ reader.accept(ancestorCollector,
+ ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
+ }
+ }
+
+ val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorCollector.ancestors)
+
+ try {
+ val outFile = File(aidlFileName)
+ val outWriter = outFile.bufferedWriter()
+ for (parcelable in parcelables) {
+ outWriter.write("parcelable ")
+ outWriter.write(parcelable.replace('/', '.').replace('$', '.'))
+ outWriter.write(";\n")
+ }
+ outWriter.flush()
+ } catch (e: IOException) {
+ System.err.println("error writing output aidl: ${e.message}")
+ kotlin.system.exitProcess(2)
+ }
+}
+
+fun usage() {
+ System.err.println("Usage: <input jar> <output aidl>")
+ kotlin.system.exitProcess(1)
+}
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
new file mode 100644
index 0000000..45027b5
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.sdkparcelables
+
+/** A class that uses an ancestor map to find all classes that
+ * implement android.os.Parcelable, including indirectly through
+ * super classes or super interfaces.
+ */
+class ParcelableDetector {
+ companion object {
+ fun ancestorsToParcelables(ancestors: Map<String, Ancestors>): List<String> {
+ val impl = Impl(ancestors)
+ impl.build()
+ return impl.parcelables
+ }
+
+ const val PARCELABLE_CLASS = "android/os/Parcelable"
+ }
+
+ private class Impl(val ancestors: Map<String, Ancestors>) {
+ val isParcelableCache = HashMap<String, Boolean>()
+ val parcelables = ArrayList<String>()
+
+ fun build() {
+ val classList = ancestors.keys
+ classList.filterTo(parcelables, { (it != PARCELABLE_CLASS) && isParcelable(it) })
+ parcelables.sort()
+ }
+
+ private fun isParcelable(c: String?): Boolean {
+ if (c == null) {
+ return false
+ }
+
+ if (c == PARCELABLE_CLASS) {
+ return true
+ }
+
+ val old = isParcelableCache[c]
+ if (old != null) {
+ return old
+ }
+
+ val cAncestors = ancestors[c] ?:
+ throw RuntimeException("class $c missing ancestor information")
+
+ val seq = (cAncestors.interfaces?.asSequence() ?: emptySequence()) +
+ cAncestors.superName
+
+ val ancestorIsParcelable = seq.any(this::isParcelable)
+
+ isParcelableCache[c] = ancestorIsParcelable
+ return ancestorIsParcelable
+ }
+ }
+}
diff --git a/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
new file mode 100644
index 0000000..f08173d
--- /dev/null
+++ b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.sdkparcelables
+
+import junit.framework.TestCase.assertEquals
+import org.junit.Test
+
+class ParcelableDetectorTest {
+ @Test
+ fun `detect implements`() {
+ val ancestorMap = mapOf(
+ testAncestors("android/test/Parcelable",null, "android/os/Parcelable"),
+ testAncestors("android/os/Parcelable", null))
+
+ val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+ assertEquals(parcelables, listOf("android/test/Parcelable"))
+ }
+
+ @Test
+ fun `detect implements in reverse order`() {
+ val ancestorMap = mapOf(
+ testAncestors("android/os/Parcelable", null),
+ testAncestors("android/test/Parcelable",null, "android/os/Parcelable"))
+
+ val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+ assertEquals(parcelables, listOf("android/test/Parcelable"))
+ }
+
+ @Test
+ fun `detect super implements`() {
+ val ancestorMap = mapOf(
+ testAncestors("android/test/SuperParcelable",null, "android/os/Parcelable"),
+ testAncestors("android/test/Parcelable","android/test/SuperParcelable"),
+ testAncestors("android/os/Parcelable", null))
+
+ val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+ assertEquals(parcelables, listOf("android/test/Parcelable", "android/test/SuperParcelable"))
+ }
+
+ @Test
+ fun `detect super interface`() {
+ val ancestorMap = mapOf(
+ testAncestors("android/test/IParcelable",null, "android/os/Parcelable"),
+ testAncestors("android/test/Parcelable",null, "android/test/IParcelable"),
+ testAncestors("android/os/Parcelable", null))
+
+ val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+ assertEquals(parcelables, listOf("android/test/IParcelable", "android/test/Parcelable"))
+ }
+
+}
+
+private fun testAncestors(name: String, superName: String?, vararg interfaces: String): Pair<String, Ancestors> {
+ return Pair(name, Ancestors(superName, interfaces.toList()))
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index b235ccc7..8ac659f 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -599,6 +599,7 @@
/**
* Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl.
*/
+ @Test
public void testWatchLocalOnlyHotspot() throws Exception {
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();