Merge "Added APIs for Connection-oriented channels"
diff --git a/Android.bp b/Android.bp
index eb5240e..4d781125 100644
--- a/Android.bp
+++ b/Android.bp
@@ -247,6 +247,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,6 +455,8 @@
         "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/data/IDataService.aidl",
+	"telephony/java/android/telephony/data/IDataServiceCallback.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",
@@ -460,8 +464,6 @@
         "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",
@@ -481,6 +483,8 @@
         "telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl",
         "telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl",
+        "telephony/java/com/android/ims/internal/IImsRegistration.aidl",
+        "telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsRcsFeature.aidl",
         "telephony/java/com/android/ims/internal/IImsService.aidl",
         "telephony/java/com/android/ims/internal/IImsServiceController.aidl",
@@ -508,7 +512,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 +621,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..7d5afcd 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) \
   )
 
@@ -487,8 +289,6 @@
 
 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)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 
@@ -524,8 +324,6 @@
 
 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)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 
@@ -562,8 +360,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 +389,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
@@ -906,3 +699,4 @@
 endif
 
 endif # ANDROID_BUILD_EMBEDDED
+
diff --git a/api/current.txt b/api/current.txt
index e8d0ee2..40452b763 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19873,6 +19873,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 +19964,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 final android.icu.util.LocaleData getInstance(android.icu.util.ULocale);
+    method public static final android.icu.util.LocaleData getInstance();
+    method public static final android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale);
+    method public boolean getNoSubstitute();
+    method public static final 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();
@@ -25781,12 +25814,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 +25848,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 +25876,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 +25941,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 +25954,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 {
@@ -35516,7 +35553,7 @@
   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 +35566,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";
@@ -37410,6 +37448,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();
@@ -39428,12 +39467,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);
@@ -39833,6 +39875,113 @@
 
 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 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);
@@ -39881,6 +40030,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 +40130,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 +40148,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 +40165,7 @@
     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 getCi();
     method public int getEarfcn();
     method public deprecated int getMcc();
@@ -40028,8 +40181,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();
@@ -40330,6 +40492,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 +40504,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,111 +40517,6 @@
     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);
@@ -40465,11 +40524,13 @@
     method protected void copyFrom(android.telephony.ServiceState);
     method public int describeContents();
     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);
@@ -40482,6 +40543,7 @@
     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 {
@@ -40712,6 +40774,8 @@
     method public int getSimState();
     method public int getSimState(int);
     method public java.lang.String getSubscriberId();
+    method public int getSubscriptionCarrierId();
+    method public java.lang.String getSubscriptionCarrierName();
     method public java.lang.String getVisualVoicemailPackageName();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
@@ -40726,11 +40790,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 +40805,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 +40820,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 +40841,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 +40853,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 +40889,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 +40977,78 @@
 
 }
 
+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 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 setId(int);
+    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 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.gsm {
 
   public class GsmCellLocation extends android.telephony.CellLocation {
diff --git a/api/system-current.txt b/api/system-current.txt
index 3a71189..282dfaa 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -615,6 +615,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 +623,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
   }
 
 }
@@ -4029,7 +4047,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);
@@ -4058,11 +4076,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,15 +4123,36 @@
     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);
-    method public int describeContents();
-    method public java.net.InetAddress getAddress();
-    method public int getNetworkPrefixLength();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.data.InterfaceAddress> CREATOR;
+  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";
+  }
+
+  public class DataService.DataServiceProvider {
+    ctor public DataService.DataServiceProvider(int);
+    method public void deactivateDataCall(int, boolean, boolean, 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, boolean, 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
   }
 
 }
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/core/java/Android.bp b/core/java/Android.bp
index 42b0f6b..fb27f742 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/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..2ecd312 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -366,7 +366,7 @@
 
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
-        LoadedApk packageInfo;
+        LoadedApk loadedApk;
 
         List<ResultInfo> pendingResults;
         List<ReferrerIntent> pendingIntents;
@@ -542,7 +542,7 @@
     }
 
     static final class AppBindData {
-        LoadedApk info;
+        LoadedApk loadedApk;
         String processName;
         ApplicationInfo appInfo;
         List<ProviderInfo> providers;
@@ -1584,7 +1584,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 +1832,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 +1973,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 +1992,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 +2006,7 @@
                             + mBoundApplication.processName
                             + "/" + mBoundApplication.appInfo.uid);
                 }
-                return packageInfo;
+                return loadedApk;
             }
         }
 
@@ -2019,13 +2021,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 +2049,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 +2070,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 +2085,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 +2647,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 +2685,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 +2811,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 +2819,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 +3121,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 +3159,7 @@
 
         String component = data.intent.getComponent().getClassName();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
+        LoadedApk loadedApk = getLoadedApkNoCheck(
                 data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManager.getService();
@@ -3154,7 +3168,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 +3192,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 +3239,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 +3266,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 +3306,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 +3327,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 +3344,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 +3957,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 +4252,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 +4753,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 +5223,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 +5234,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 +5261,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 +5289,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 +5304,7 @@
                 break;
             }
         }
-        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
+        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
     }
 
     final void handleLowMemory() {
@@ -5441,12 +5455,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 +5512,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 +5584,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 +5603,7 @@
                 }
 
             } else {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
+                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
                       + " can be debugged on port 8100...");
             }
         }
@@ -5629,14 +5651,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 +5688,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 +5734,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 +5788,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 +6383,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/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..4d47d82 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1379,7 +1379,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;
         }
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/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f6d9710..236a42c 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -31,6 +31,7 @@
 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;
@@ -48,15 +49,13 @@
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.LogPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
-
 import com.android.internal.util.ArrayUtils;
-
 import dalvik.system.VMRuntime;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -948,14 +947,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..044b780 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,7 @@
         autoStopProfiler = in.autoStopProfiler;
         streamingOutput = in.streamingOutput;
         agent = in.agent;
+        attachAgentDuringBind = in.attachAgentDuringBind;
     }
 
     /**
@@ -109,6 +120,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 +143,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/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 7841b83..35a21a4 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.
      *
@@ -425,6 +444,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.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4775bde..6c8fe2e 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1680,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
@@ -2442,6 +2463,8 @@
      *
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enableNoAutoConnect() {
         if (isEnabled()) {
             if (DBG) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index f4dda80..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) {
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/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index df2028a..0e2263f 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,19 @@
     public static final int HID_DEVICE = 19;
 
     /**
+     * Object Push Profile (OPP)
+     *
+     * @hide
+     */
+    public static final int OPP = 20;
+
+    /**
      * 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;
+    public static final int MAX_PROFILE_ID = 20;
 
     /**
      * Default priority for devices that we try to auto-connect to and
@@ -178,6 +186,7 @@
      *
      * @hide
      **/
+    @SystemApi
     public static final int PRIORITY_ON = 100;
 
     /**
@@ -186,6 +195,7 @@
      *
      * @hide
      **/
+    @SystemApi
     public static final int PRIORITY_OFF = 0;
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7e2ac63..70087da 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3589,8 +3589,18 @@
     public static final String EUICC_SERVICE = "euicc_service";
 
     /**
-     * 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
+     * TODO(b/35851809): Make this a SystemApi.
+     * @hide
+     */
+    public static final String EUICC_CARD_SERVICE = "euicc_card_service";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.content.ClipboardManager} for accessing and modifying
      * the contents of the global clipboard.
      *
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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8071e8b..11d338d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1794,7 +1794,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;
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index d9b57db..790c80b 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -31,7 +31,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);
 
@@ -43,7 +43,7 @@
 
     void deleteTransportModeTransform(int transformId);
 
-    void applyTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+    void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
 
-    void removeTransportModeTransform(in ParcelFileDescriptor socket, int transformId);
+    void removeTransportModeTransforms(in ParcelFileDescriptor socket);
 }
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..80b0af3 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
@@ -100,42 +70,37 @@
         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;
     }
@@ -161,28 +126,28 @@
         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() {
@@ -215,17 +180,13 @@
     @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);
@@ -237,22 +198,15 @@
 
     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();
@@ -266,10 +220,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 +234,14 @@
                 .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("}");
 
         return strBuilder.toString();
@@ -305,17 +263,18 @@
     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));
     }
 }
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 6a4b891..2cda58c 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -33,6 +34,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 +56,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 +89,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 +145,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 +166,7 @@
         public void close() {
             try {
                 mService.releaseSecurityParameterIndex(mResourceId);
+                mResourceId = INVALID_RESOURCE_ID;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -163,14 +184,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 +236,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 +260,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 +288,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 +306,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 +321,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 +339,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 +353,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 +371,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 +405,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 +492,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 +545,7 @@
         public void close() throws IOException {
             try {
                 mService.closeUdpEncapsulationSocket(mResourceId);
+                mResourceId = INVALID_RESOURCE_ID;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 7cd742b..7b9b483 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -38,13 +38,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 +50,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;
 
@@ -170,7 +151,7 @@
      *
      * <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.
@@ -272,82 +253,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());
+        public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
+            Preconditions.checkNotNull(algo);
+            mConfig.setAuthenticatedEncryption(algo);
             return this;
         }
 
@@ -360,7 +308,8 @@
          * @hide
          */
         @SystemApi
-        public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
+        public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) {
+            Preconditions.checkNotNull(net);
             mConfig.setNetwork(net);
             return this;
         }
@@ -379,8 +328,12 @@
          *     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;
@@ -413,21 +366,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,22 +401,33 @@
          * 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
          */
         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);
+            mConfig.setSourceAddress(sourceAddress.getHostAddress());
+            mConfig.setSpiResourceId(spi.getResourceId());
             return new IpSecTransform(mContext, mConfig);
         }
 
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..287bdc8 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;
 
@@ -60,7 +61,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 +93,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 +121,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 +134,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 +198,7 @@
         if (!isMacAddress(addr)) {
             return TYPE_UNKNOWN;
         }
-        return MacAddress.fromBytes(addr).addressType();
+        return MacAddress.fromBytes(addr).getAddressType();
     }
 
     /**
@@ -211,7 +212,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 +240,7 @@
      *
      * @hide
      */
-    public static String stringAddrFromByteAddr(byte[] addr) {
+    public static @NonNull String stringAddrFromByteAddr(byte[] addr) {
         if (!isMacAddress(addr)) {
             return null;
         }
@@ -291,7 +292,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 +311,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,7 +323,7 @@
      * @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));
     }
 
@@ -336,7 +337,7 @@
      *
      * @hide
      */
-    public static MacAddress createRandomUnicastAddress() {
+    public static @NonNull MacAddress createRandomUnicastAddress() {
         return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
     }
 
@@ -352,7 +353,7 @@
      *
      * @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;
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/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7c897de..be85583 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -107,6 +107,7 @@
             NET_CAPABILITY_CAPTIVE_PORTAL,
             NET_CAPABILITY_NOT_ROAMING,
             NET_CAPABILITY_FOREGROUND,
+            NET_CAPABILITY_NOT_CONGESTED,
     })
     public @interface NetCapability { }
 
@@ -234,8 +235,17 @@
      */
     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;
+
     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_CONGESTED;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -248,7 +258,8 @@
             (1 << NET_CAPABILITY_VALIDATED) |
             (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
             (1 << NET_CAPABILITY_NOT_ROAMING) |
-            (1 << NET_CAPABILITY_FOREGROUND);
+            (1 << NET_CAPABILITY_FOREGROUND) |
+            (1 << NET_CAPABILITY_NOT_CONGESTED);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -386,12 +397,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";
@@ -1056,6 +1064,7 @@
             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";
             default:                            return Integer.toString(capability);
         }
     }
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/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 5c4a40e..336e1b4 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -792,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) {
@@ -803,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) {
@@ -895,17 +913,31 @@
                 Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
                         + " total = " + totalSize);
                 mWarnBucketSize += WARN_INCREMENT;
-                if (Build.IS_DEBUGGABLE && totalSize > CRASH_AT_SIZE) {
-                    diagnosticCrash();
+                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, then throw an assertion error. Used to diagnose
-         * abnormally large proxy maps.
+         * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
          */
-        private void diagnosticCrash() {
+        private void dumpProxyInterfaceCounts() {
             Map<String, Integer> counts = new HashMap<>();
             for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
                 if (a != null) {
@@ -940,11 +972,6 @@
                 Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
                         + sorted[i].getValue());
             }
-
-            // Now throw an assertion.
-            final int totalSize = size();
-            throw new AssertionError("Binder ProxyMap has too many entries: " + totalSize
-                    + ". BinderProxy leak?");
         }
 
         // Corresponding ArrayLists in the following two arrays always have the same size.
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/VintfObject.java b/core/java/android/os/VintfObject.java
index 340f3fb..12a495b 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -76,8 +76,8 @@
     /**
      * @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();
 }
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..ed68276 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;
@@ -10077,8 +10078,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 +10091,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 +10102,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 +10113,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 +10124,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 +10136,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
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/android/telephony/data/InterfaceAddress.aidl b/core/java/android/service/euicc/EuiccProfileInfo.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to core/java/android/service/euicc/EuiccProfileInfo.aidl
index d750363..321021b 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/core/java/android/service/euicc/EuiccProfileInfo.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.
  */
+package android.service.euicc;
 
-/** @hide */
-package android.telephony.data;
-
-parcelable InterfaceAddress;
+parcelable EuiccProfileInfo;
diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/core/java/android/service/euicc/EuiccProfileInfo.java
index ba6c9a2..8e752d1 100644
--- a/core/java/android/service/euicc/EuiccProfileInfo.java
+++ b/core/java/android/service/euicc/EuiccProfileInfo.java
@@ -15,12 +15,19 @@
  */
 package android.service.euicc;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 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.Objects;
+
 /**
  * Information about an embedded profile (subscription) on an eUICC.
  *
@@ -30,18 +37,90 @@
  */
 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
+    })
+    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
+    })
+    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
+    })
+    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;
 
+    /** An optional nickname for the subscription. */
+    public final @Nullable String nickname;
+
+    /** The service provider name for the subscription. */
+    public final String serviceProviderName;
+
+    /** The profile name for the subscription. */
+    public final String profileName;
+
+    /** Profile class for the subscription. */
+    @ProfileClass public final int profileClass;
+
+    /** The profile state of the subscription. */
+    @ProfileState public final int state;
+
+    /** The operator Id of the subscription. */
+    public final CarrierIdentifier carrierIdentifier;
+
+    /** The policy rules of the subscription. */
+    @PolicyRule public final int policyRules;
+
     /**
      * 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;
-
     public static final Creator<EuiccProfileInfo> CREATOR = new Creator<EuiccProfileInfo>() {
         @Override
         public EuiccProfileInfo createFromParcel(Parcel in) {
@@ -54,6 +133,12 @@
         }
     };
 
+    // 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)) {
@@ -62,23 +147,290 @@
         this.iccid = iccid;
         this.accessRules = accessRules;
         this.nickname = nickname;
+
+        this.serviceProviderName = null;
+        this.profileName = null;
+        this.profileClass = PROFILE_CLASS_UNSET;
+        this.state = PROFILE_CLASS_UNSET;
+        this.carrierIdentifier = null;
+        this.policyRules = 0;
     }
 
     private EuiccProfileInfo(Parcel in) {
         iccid = in.readString();
-        accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
         nickname = in.readString();
+        serviceProviderName = in.readString();
+        profileName = in.readString();
+        profileClass = in.readInt();
+        state = in.readInt();
+        byte exist = in.readByte();
+        if (exist == (byte) 1) {
+            carrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in);
+        } else {
+            carrierIdentifier = null;
+        }
+        policyRules = in.readInt();
+        accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(iccid);
-        dest.writeTypedArray(accessRules, flags);
         dest.writeString(nickname);
+        dest.writeString(serviceProviderName);
+        dest.writeString(profileName);
+        dest.writeInt(profileClass);
+        dest.writeInt(state);
+        if (carrierIdentifier != null) {
+            dest.writeByte((byte) 1);
+            carrierIdentifier.writeToParcel(dest, flags);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        dest.writeInt(policyRules);
+        dest.writeTypedArray(accessRules, flags);
     }
 
     @Override
     public int describeContents() {
         return 0;
     }
+
+    /** The builder to build a new {@link EuiccProfileInfo} instance. */
+    public static final class Builder {
+        public String iccid;
+        public UiccAccessRule[] accessRules;
+        public String nickname;
+        public String serviceProviderName;
+        public String profileName;
+        @ProfileClass public int profileClass;
+        @ProfileState public int state;
+        public CarrierIdentifier carrierIdentifier;
+        @PolicyRule public int policyRules;
+
+        public Builder() {}
+
+        public Builder(EuiccProfileInfo baseProfile) {
+            iccid = baseProfile.iccid;
+            nickname = baseProfile.nickname;
+            serviceProviderName = baseProfile.serviceProviderName;
+            profileName = baseProfile.profileName;
+            profileClass = baseProfile.profileClass;
+            state = baseProfile.state;
+            carrierIdentifier = baseProfile.carrierIdentifier;
+            policyRules = baseProfile.policyRules;
+            accessRules = baseProfile.accessRules;
+        }
+
+        /** Builds the profile instance. */
+        public EuiccProfileInfo build() {
+            if (iccid == null) {
+                throw new IllegalStateException("ICCID must be set for a profile.");
+            }
+            return new EuiccProfileInfo(
+                    iccid,
+                    nickname,
+                    serviceProviderName,
+                    profileName,
+                    profileClass,
+                    state,
+                    carrierIdentifier,
+                    policyRules,
+                    accessRules);
+        }
+
+        /** Sets the iccId of the subscription. */
+        public Builder setIccid(String value) {
+            if (!TextUtils.isDigitsOnly(value)) {
+                throw new IllegalArgumentException("iccid contains invalid characters: " + value);
+            }
+            iccid = value;
+            return this;
+        }
+
+        /** Sets the nickname of the subscription. */
+        public Builder setNickname(String value) {
+            nickname = value;
+            return this;
+        }
+
+        /** Sets the service provider name of the subscription. */
+        public Builder setServiceProviderName(String value) {
+            serviceProviderName = value;
+            return this;
+        }
+
+        /** Sets the profile name of the subscription. */
+        public Builder setProfileName(String value) {
+            profileName = value;
+            return this;
+        }
+
+        /** Sets the profile class of the subscription. */
+        public Builder setProfileClass(@ProfileClass int value) {
+            profileClass = value;
+            return this;
+        }
+
+        /** Sets the state of the subscription. */
+        public Builder setState(@ProfileState int value) {
+            state = value;
+            return this;
+        }
+
+        /** Sets the carrier identifier of the subscription. */
+        public Builder setCarrierIdentifier(CarrierIdentifier value) {
+            carrierIdentifier = value;
+            return this;
+        }
+
+        /** Sets the policy rules of the subscription. */
+        public Builder setPolicyRules(@PolicyRule int value) {
+            policyRules = value;
+            return this;
+        }
+
+        /** Sets the access rules of the subscription. */
+        public Builder setUiccAccessRule(@Nullable UiccAccessRule[] value) {
+            accessRules = 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 UiccAccessRule[] accessRules) {
+        this.iccid = iccid;
+        this.nickname = nickname;
+        this.serviceProviderName = serviceProviderName;
+        this.profileName = profileName;
+        this.profileClass = profileClass;
+        this.state = state;
+        this.carrierIdentifier = carrierIdentifier;
+        this.policyRules = policyRules;
+        this.accessRules = accessRules;
+    }
+
+    /** Gets the ICCID string. */
+    public String getIccid() {
+        return iccid;
+    }
+
+    /** Gets the access rules. */
+    @Nullable
+    public UiccAccessRule[] getUiccAccessRules() {
+        return accessRules;
+    }
+
+    /** Gets the nickname. */
+    public String getNickname() {
+        return nickname;
+    }
+
+    /** Gets the service provider name. */
+    public String getServiceProviderName() {
+        return serviceProviderName;
+    }
+
+    /** Gets the profile name. */
+    public String getProfileName() {
+        return profileName;
+    }
+
+    /** Gets the profile class. */
+    @ProfileClass
+    public int getProfileClass() {
+        return profileClass;
+    }
+
+    /** Gets the state of the subscription. */
+    @ProfileState
+    public int getState() {
+        return state;
+    }
+
+    /** Gets the carrier identifier. */
+    public CarrierIdentifier getCarrierIdentifier() {
+        return carrierIdentifier;
+    }
+
+    /** Gets the policy rules. */
+    @PolicyRule
+    public int getPolicyRules() {
+        return policyRules;
+    }
+
+    /** Returns whether any policy rule exists. */
+    public boolean hasPolicyRules() {
+        return policyRules != 0;
+    }
+
+    /** Checks whether a certain policy rule exists. */
+    public boolean hasPolicyRule(@PolicyRule int policy) {
+        return (policyRules & 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(iccid, that.iccid)
+                && Objects.equals(nickname, that.nickname)
+                && Objects.equals(serviceProviderName, that.serviceProviderName)
+                && Objects.equals(profileName, that.profileName)
+                && profileClass == that.profileClass
+                && state == that.state
+                && Objects.equals(carrierIdentifier, that.carrierIdentifier)
+                && policyRules == that.policyRules
+                && Arrays.equals(accessRules, that.accessRules);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + Objects.hashCode(iccid);
+        result = 31 * result + Objects.hashCode(nickname);
+        result = 31 * result + Objects.hashCode(serviceProviderName);
+        result = 31 * result + Objects.hashCode(profileName);
+        result = 31 * result + profileClass;
+        result = 31 * result + state;
+        result = 31 * result + Objects.hashCode(carrierIdentifier);
+        result = 31 * result + policyRules;
+        result = 31 * result + Arrays.hashCode(accessRules);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "EuiccProfileInfo (nickname="
+                + nickname
+                + ", serviceProviderName="
+                + serviceProviderName
+                + ", profileName="
+                + profileName
+                + ", profileClass="
+                + profileClass
+                + ", state="
+                + state
+                + ", CarrierIdentifier="
+                + carrierIdentifier.toString()
+                + ", policyRules="
+                + policyRules
+                + ", accessRules="
+                + Arrays.toString(accessRules)
+                + ")";
+    }
 }
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index df0842f..be85800 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -23,6 +23,7 @@
 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;
@@ -192,6 +193,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 interface OtaStatusChangedCallback {
+        /** Called when OTA status is changed. */
+        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,6 +216,26 @@
     public abstract String onGetEid(int slotId);
 
     /**
+     * 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. This is currently not populated
@@ -385,6 +418,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/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%
copy from core/java/android/os/Seccomp.java
copy 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&lt;tz&gt; 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/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/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/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 3ee8b47..9167076 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -69,6 +69,9 @@
 
     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
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 2be6212..21f1fb6 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,12 @@
                 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);
                 } 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: "
@@ -779,12 +780,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) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 551d54a..bc98716 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",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 047fa84..35ab56a 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);
@@ -1420,7 +1419,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_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1eeea51..1659168 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -146,8 +146,8 @@
         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()));
     }
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
deleted file mode 100644
index 06e2a167..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/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_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 32ef3dc..63dba43 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,
@@ -229,6 +232,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) {
@@ -541,6 +558,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);
@@ -698,6 +720,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();
 }
@@ -832,6 +860,8 @@
 }
 
 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",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9072960..fdda55b 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" />
@@ -173,6 +175,8 @@
     <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 +563,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                           -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 15fd34d..e18265b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1753,9 +1753,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>
@@ -2560,6 +2557,13 @@
 
     <bool name="config_sms_force_7bit_encoding">false</bool>
 
+    <!-- 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>
+
     <!--Thresholds for LTE dbm in status bar-->
     <integer-array translatable="false" name="config_lteDbmThresholds">
         <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1703ef0..f1070de 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -272,7 +272,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 +445,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" />
 
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..1e3ddf3
--- /dev/null
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EuiccProfileInfoTest {
+    @Test
+    public void testWriteToParcel() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder()
+                        .setIccid("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(
+                                new UiccAccessRule[] {
+                                        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()
+                        .setIccid("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(
+                                new UiccAccessRule[] {
+                                        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()
+                        .setIccid("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(
+                                new UiccAccessRule[] {
+                                        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());
+    }
+
+    @Test
+    public void testBuilder_BasedOnAnotherProfile() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder()
+                        .setIccid("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(
+                                new UiccAccessRule[] {
+                                        new UiccAccessRule(new byte[0], null, 0)
+                                })
+                        .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()
+                        .setIccid("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(
+                                new UiccAccessRule[] {
+                                        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_NoIccid() {
+        new EuiccProfileInfo.Builder().build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuilderSetOperatorMccMnc_Illegal() {
+        new EuiccProfileInfo.Builder()
+                .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/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/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/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index d7695ef..5785b0c 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -30,13 +30,6 @@
         "libandroid_runtime",
         "libskia",
     ],
-
-    arch: {
-        arm: {
-            // TODO: This is to work around b/24465209. Remove after root cause is fixed
-            ldflags: ["-Wl,--hash-style=both"],
-        },
-    },
 }
 
 // The headers module is in frameworks/native/Android.bp.
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8a4a774..6601451 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -488,8 +488,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 +580,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/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/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d9713a5..337406d 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);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5228498..56d7e7b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -130,6 +130,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;
@@ -224,7 +226,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 +238,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 +400,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 +414,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;
@@ -857,6 +865,9 @@
         mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
                 mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
         mMultinetworkPolicyTracker.start();
+
+        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+        registerPrivateDnsSettingsCallbacks();
     }
 
     private Tethering makeTethering() {
@@ -917,6 +928,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 +989,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 +1079,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 +1247,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();
@@ -1555,9 +1574,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 +1824,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(
@@ -2094,36 +2097,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 +2189,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 +2239,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 +2430,7 @@
                 } catch (Exception e) {
                     loge("Exception removing network: " + e);
                 }
+                mDnsManager.removeNetwork(nai.network);
             }
             synchronized (mNetworkForNetId) {
                 mNetIdInUse.delete(nai.network.netId);
@@ -2488,6 +2587,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 +2600,7 @@
                 mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
             }
         }
+
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
         if (nri.request.isRequest()) {
             boolean wasKept = false;
@@ -2876,6 +2977,9 @@
                     handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2));
                     break;
                 }
+                case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
+                    handlePrivateDnsSettingsChanged();
+                    break;
             }
         }
     }
@@ -3422,9 +3526,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 +3572,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 +3585,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 +3617,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 +3686,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 +3722,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 +3737,7 @@
         }
     }
 
+    @GuardedBy("mVpns")
     private void throwIfLockdownEnabled() {
         if (mLockdownEnabled) {
             throw new IllegalStateException("Unavailable in lockdown mode");
@@ -3679,12 +3785,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 +3966,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 +4005,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);
+            }
         }
     }
 
@@ -4558,41 +4666,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 +4692,67 @@
     }
 
     /**
-     * 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 (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 +4764,10 @@
         final NetworkCapabilities prevNc;
         synchronized (nai) {
             prevNc = nai.networkCapabilities;
-            nai.networkCapabilities = networkCapabilities;
+            nai.networkCapabilities = newNc;
         }
 
-        if (nai.getCurrentScore() == oldScore &&
-                networkCapabilities.equalRequestableCapabilities(prevNc)) {
+        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 +4783,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) {
@@ -4865,7 +4965,7 @@
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork);
-        setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+        mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -5215,11 +5315,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();
+                }
             }
         }
     }
@@ -5449,28 +5551,28 @@
 
     @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) {
@@ -5530,31 +5632,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..46a35ec 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;
@@ -51,6 +53,7 @@
 
 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 +63,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 +83,7 @@
 
     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 int NETD_FETCH_TIMEOUT_MS = 5000; // ms
     private static final int MAX_PORT_BIND_ATTEMPTS = 10;
@@ -101,8 +103,14 @@
     /* 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;
@@ -528,14 +536,14 @@
 
     private final class TransformRecord extends KernelResourceRecord {
         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;
         }
 
@@ -543,29 +551,26 @@
             return mConfig;
         }
 
-        public SpiRecord getSpiRecord(int direction) {
-            return mSpis[direction];
+        public SpiRecord getSpiRecord() {
+            return mSpi;
         }
 
         /** 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);
+            } 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 +594,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("}");
@@ -601,23 +604,16 @@
     }
 
     private final class SpiRecord extends KernelResourceRecord {
-        private final int mDirection;
-        private final String mLocalAddress;
-        private final String mRemoteAddress;
+        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;
         }
 
@@ -638,7 +634,7 @@
                 mSrvConfig
                         .getNetdInstance()
                         .ipSecDeleteSecurityAssociation(
-                                mResourceId, mDirection, mLocalAddress, mRemoteAddress, mSpi);
+                                mResourceId, mSourceAddress, mDestinationAddress, mSpi);
             } catch (ServiceSpecificException e) {
                 // FIXME: get the error code and throw is at an IOException from Errno Exception
             } catch (RemoteException e) {
@@ -654,6 +650,10 @@
             return mSpi;
         }
 
+        public String getDestinationAddress() {
+            return mDestinationAddress;
+        }
+
         public void setOwnedByTransform() {
             if (mOwnedByTransform) {
                 // Programming error
@@ -681,12 +681,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("}");
@@ -764,14 +762,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 +838,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 +848,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 +970,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()) {
@@ -1023,6 +1015,30 @@
         releaseResource(userRecord.mEncapSocketRecords, 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.
@@ -1030,29 +1046,6 @@
     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,21 +1064,36 @@
                 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());
+
+        // 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:
+            case IpSecTransform.MODE_TUNNEL:
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid IpSecTransform.mode: " + config.getMode());
         }
     }
 
@@ -1101,17 +1109,16 @@
             IpSecConfig c, IBinder binder) throws RemoteException {
         checkIpSecConfig(c);
         checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
-        int resourceId = mNextResourceId.getAndIncrement();
+        final int resourceId = mNextResourceId++;
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
-        // Avoid resizing by creating a dependency array of min-size 3 (1 UDP encap + 2 SPIs)
-        List<RefcountedResource> dependencies = new ArrayList<>(3);
+        // Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI)
+        List<RefcountedResource> dependencies = new ArrayList<>(2);
 
         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;
@@ -1127,51 +1134,46 @@
             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);
+        RefcountedResource<SpiRecord> refcountedSpiRecord =
+                userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId());
+        dependencies.add(refcountedSpiRecord);
+        SpiRecord spiRecord = refcountedSpiRecord.getResource();
 
-            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);
-            }
+        try {
+            mSrvConfig
+                    .getNetdInstance()
+                    .ipSecAddSecurityAssociation(
+                            resourceId,
+                            c.getMode(),
+                            c.getSourceAddress(),
+                            c.getDestinationAddress(),
+                            (c.getNetwork() != null) ? c.getNetwork().netId : 0,
+                            spiRecord.getSpi(),
+                            (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);
         }
         // Both SAs were 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);
@@ -1195,9 +1197,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);
 
@@ -1208,30 +1210,32 @@
 
         IpSecConfig c = info.getConfig();
         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
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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 831c9cb..6747be3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -147,6 +147,8 @@
 
     private int[] mDataActivationState;
 
+    private boolean[] mUserMobileDataState;
+
     private SignalStrength[] mSignalStrength;
 
     private boolean[] mMessageWaiting;
@@ -304,6 +306,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 +323,7 @@
             mCallIncomingNumber[i] =  "";
             mServiceState[i] =  new ServiceState();
             mSignalStrength[i] =  new SignalStrength();
+            mUserMobileDataState[i] = false;
             mMessageWaiting[i] =  false;
             mCallForwarding[i] =  false;
             mCellLocation[i] = new Bundle();
@@ -656,6 +660,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 {
@@ -1012,6 +1023,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);
     }
@@ -1374,6 +1412,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]);
@@ -1755,6 +1794,18 @@
             }
         }
 
+        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);
+            }
+        }
+
         if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
             try {
                 if (VDBG) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 8d46d1e..7ce0f43 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -87,6 +87,7 @@
         "/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
     };
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..002381a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -200,6 +200,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;
@@ -1548,6 +1549,8 @@
         }
     }
 
+    final List<ScreenObserver> mScreenObservers = new ArrayList<>();
+
     final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
     ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
 
@@ -1689,6 +1692,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;
 
@@ -2412,11 +2417,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) {
@@ -3260,32 +3271,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;
@@ -7027,15 +7012,22 @@
             }
 
             ProfilerInfo profilerInfo = null;
-            String agent = null;
+            String preBindAgent = 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;
+                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.attachAgentDuringBind) {
+                        preBindAgent = mProfilerInfo.agent;
+                    }
+                }
             } else if (app.instr != null && app.instr.mProfileFile != null) {
                 profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
-                        null);
+                        null, false);
             }
 
             boolean enableTrackAllocation = false;
@@ -7102,8 +7094,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");
@@ -12497,7 +12489,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 +12643,9 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
-        sendNotifyVrManagerOfKeyguardState(showing);
+
+        mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+                .sendToTarget();
     }
 
     @Override
@@ -24258,6 +24253,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..a7ace21 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;
@@ -295,7 +296,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 +396,7 @@
                     }
                 }
                 profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
-                        mStreaming, mAgent);
+                        mStreaming, mAgent, mAttachAgentDuringBind);
             }
 
             pw.println("Starting: " + intent);
@@ -755,7 +770,7 @@
                 return -1;
             }
             profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
-                    null);
+                    null, false);
         }
 
         try {
@@ -2679,6 +2694,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");
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/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 7684030..ed268581b 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;
@@ -215,6 +216,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 +240,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 +277,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 +360,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 +428,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 +456,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 +602,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 +617,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 +1081,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
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d1bab89..7b171b3 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;
 
@@ -867,33 +854,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 +1037,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 +1095,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..aa174e3 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
@@ -183,10 +184,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 +198,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 +249,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 +260,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 +299,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 +322,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 +479,6 @@
     }
 
     private void unregisterPackageChangeReceiverLocked() {
-        // register previous intent filter
         if (mIsPackageIntentReceiverRegistered) {
             mContext.unregisterReceiver(mPackageIntentReceiver);
             mIsPackageIntentReceiverRegistered = false;
@@ -581,7 +579,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 +609,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 +628,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 +637,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.
@@ -860,8 +859,8 @@
 
         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, 0 /* score */, networkMisc) {
                             @Override
                             public void unwanted() {
                                 // We are user controlled, not driven by NetworkRequest.
@@ -935,7 +934,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 +942,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);
         }
@@ -1336,7 +1335,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 +1415,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() {
@@ -1767,7 +1766,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 +1779,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 +1859,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 +1881,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 +1938,7 @@
 
                     // Wait for the daemon to start.
                     while (!SystemService.isRunning(daemon)) {
-                        checkpoint(true);
+                        checkInterruptAndDelay(true);
                     }
 
                     // Create the control socket.
@@ -1958,7 +1954,7 @@
                         } catch (Exception e) {
                             // ignore
                         }
-                        checkpoint(true);
+                        checkInterruptAndDelay(true);
                     }
                     mSockets[i].setSoTimeout(500);
 
@@ -1972,7 +1968,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 +1984,7 @@
                         } catch (Exception e) {
                             // ignore
                         }
-                        checkpoint(true);
+                        checkInterruptAndDelay(true);
                     }
                 }
 
@@ -2001,7 +1997,7 @@
                             throw new IllegalStateException(daemon + " is dead");
                         }
                     }
-                    checkpoint(true);
+                    checkInterruptAndDelay(true);
                 }
 
                 // Now we are connected. Read and parse the new state.
@@ -2057,8 +2053,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 +2077,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/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/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..41cfcbe 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -281,13 +281,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 seInfo, boolean downgrade, int targetSdkVersion)
             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);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 6253857..5dbd3ca 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -260,12 +260,13 @@
             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)
                     throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The version. Right now it's 3.
-                builder.append("3 ");
+                // The version. Right now it's 4.
+                builder.append("4 ");
 
                 builder.append("dexopt");
 
@@ -281,6 +282,7 @@
                 encodeParameter(builder, sharedLibraries);
                 encodeParameter(builder, seInfo);
                 encodeParameter(builder, downgrade);
+                encodeParameter(builder, targetSdkVersion);
 
                 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..2cc5159 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -274,7 +274,7 @@
             // 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);
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -395,7 +395,7 @@
                 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);
             }
 
             return DEX_OPT_PERFORMED;
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/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/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/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 ed78175b..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..d3a97b3 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;
@@ -556,7 +556,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.
@@ -722,7 +722,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 +863,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 +894,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 +1214,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 +1241,7 @@
         try {
             mIpReachabilityMonitor = new IpReachabilityMonitor(
                     mContext,
-                    mInterfaceName,
+                    mInterfaceParams,
                     getHandler(),
                     mLog,
                     new IpReachabilityMonitor.Callback() {
@@ -1447,7 +1443,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 +1511,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/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c668100..4a7072d 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;
@@ -38,6 +42,7 @@
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
 import android.os.BatteryManager;
+import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
@@ -60,6 +65,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;
@@ -75,7 +81,7 @@
 /**
  * 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;
@@ -97,6 +103,12 @@
     private static final String USB_STATE_PROPERTY = "sys.usb.state";
 
     /**
+     * The SharedPreference setting per user that stores the screen unlocked functions between
+     * sessions.
+     */
+    private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d";
+
+    /**
      * ro.bootmode value when phone boots into usual Android.
      */
     private static final String NORMAL_BOOT = "normal";
@@ -128,6 +140,8 @@
     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 AUDIO_MODE_SOURCE = 1;
 
@@ -169,6 +183,7 @@
     private Intent mBroadcastedIntent;
     private boolean mPendingBootBroadcast;
     private static Set<Integer> sBlackListedInterfaces;
+    private SharedPreferences mSettings;
 
     static {
         sBlackListedInterfaces = new HashSet<>();
@@ -217,6 +232,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;
@@ -303,6 +343,8 @@
     public void systemReady() {
         if (DEBUG) Slog.d(TAG, "systemReady");
 
+        LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this);
+
         mNotificationManager = (NotificationManager)
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
 
@@ -407,6 +449,14 @@
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
+    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 final class UsbHandler extends Handler {
 
         // current USB state
@@ -423,17 +473,21 @@
         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;
 
         public UsbHandler(Looper looper) {
             super(looper);
             try {
                 // Restore default functions.
 
+                mCurrentOemFunctions = SystemProperties.get(UsbDeviceManager.getPersistProp(false),
+                        UsbManager.USB_FUNCTION_NONE);
                 if (isNormalBoot()) {
                     mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
                             UsbManager.USB_FUNCTION_NONE);
@@ -447,6 +501,9 @@
                             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.
@@ -651,7 +708,7 @@
         private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
             if (functions == null || applyAdbFunction(functions)
                     .equals(UsbManager.USB_FUNCTION_NONE)) {
-                functions = getDefaultFunctions();
+                functions = getChargingFunctions();
             }
             functions = applyAdbFunction(functions);
 
@@ -662,8 +719,7 @@
             }
 
             if ((!functions.equals(oemFunctions) &&
-                    (mCurrentOemFunctions == null ||
-                            !mCurrentOemFunctions.equals(oemFunctions)))
+                            !mCurrentOemFunctions.equals(oemFunctions))
                     || !mCurrentFunctions.equals(functions)
                     || !mCurrentFunctionsApplied
                     || forceRestart) {
@@ -875,6 +931,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) {
@@ -894,7 +958,13 @@
                     if (mBootCompleted) {
                         if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_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 +1047,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
@@ -1000,7 +1111,13 @@
                         updateUsbStateBroadcastIfNeeded(false);
                         mPendingBootBroadcast = false;
                     }
-                    setEnabledFunctions(null, false, false);
+
+                    if (!mScreenLocked
+                            && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) {
+                        setScreenUnlockedFunctions();
+                    } else {
+                        setEnabledFunctions(null, false, false);
+                    }
                     if (mCurrentAccessory != null) {
                         getCurrentSettings().accessoryAttached(mCurrentAccessory);
                     }
@@ -1010,16 +1127,15 @@
                     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;
                 }
@@ -1071,20 +1187,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,
@@ -1235,7 +1343,7 @@
             }
         }
 
-        private String getDefaultFunctions() {
+        private String getChargingFunctions() {
             String func = SystemProperties.get(getPersistProp(true),
                     UsbManager.USB_FUNCTION_NONE);
             // if ADB is enabled, reset functions to ADB
@@ -1252,6 +1360,8 @@
             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);
@@ -1308,6 +1418,17 @@
         mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
     }
 
+    /**
+     * 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 void readOemUsbOverrideConfig() {
         String[] configList = mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_oemUsbModeOverride);
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/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2091101..8c7d6b3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -1408,7 +1408,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);
     }
 
     /**
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4fd602f..e37aeb4 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -145,6 +145,8 @@
     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,6 +176,8 @@
     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 Connection sNullConnection;
@@ -610,6 +614,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()) {
@@ -1061,6 +1085,12 @@
                     }
                     break;
                 }
+                case MSG_CONNECTION_SERVICE_FOCUS_GAINED:
+                    onConnectionServiceFocusGained();
+                    break;
+                case MSG_CONNECTION_SERVICE_FOCUS_LOST:
+                    onConnectionServiceFocusLost();
+                    break;
                 default:
                     break;
             }
@@ -1930,6 +1960,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 +2219,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 4bc2a9b..658685f 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -286,11 +286,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/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/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..96c6e0a 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
@@ -645,7 +655,6 @@
             mContext = context;
         }
         mTelecomServiceOverride = telecomServiceImpl;
-        android.telecom.Log.initMd5Sum();
     }
 
     /**
@@ -1424,6 +1433,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..02e1ff8 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -103,4 +103,8 @@
 
     void handoverFailed(String callId, in ConnectionRequest request,
             int error, 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..87ccd3e 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -64,7 +64,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/telephony/java/android/telephony/RadioNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
similarity index 95%
rename from telephony/java/android/telephony/RadioNetworkConstants.java
rename to telephony/java/android/telephony/AccessNetworkConstants.java
index 5f5dd82..fc814be 100644
--- a/telephony/java/android/telephony/RadioNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -17,23 +17,23 @@
 package android.telephony;
 
 /**
- * Contains radio access network related constants.
+ * Contains access network related constants.
  */
-public final class RadioNetworkConstants {
+public final class AccessNetworkConstants {
 
-    public static final class RadioAccessNetworks {
+    public static final class AccessNetworkType {
         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;
     }
 
     /**
      * 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 +54,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 +83,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..0c05a02 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -352,6 +352,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.
@@ -939,8 +950,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 +1007,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";
@@ -1737,6 +1756,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);
@@ -2000,6 +2020,7 @@
         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);
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/CellIdentity.aidl
similarity index 90%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy 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%
rename from telephony/java/android/telephony/data/InterfaceAddress.aidl
rename 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..7f20c8a 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,10 +36,6 @@
     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
@@ -52,12 +45,11 @@
      * @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;
         mAlphaLong = null;
         mAlphaShort = null;
     }
@@ -72,7 +64,7 @@
      *
      * @hide
      */
-    public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac) {
+    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);
     }
 
@@ -87,7 +79,7 @@
      *
      * @hide
      */
-    public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
+    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);
     }
 
@@ -104,38 +96,13 @@
      *
      * @hide
      */
-    public CellIdentityLte (int ci, int pci, int tac, int earfcn, String mccStr,
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, 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);
-        }
-
         mAlphaLong = alphal;
         mAlphaShort = alphas;
     }
@@ -248,58 +215,54 @@
         }
 
         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
+                && 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(" 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.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();
+        mAlphaLong = in.readString();
+        mAlphaShort = in.readString();
 
-        if (DBG) log("CellIdentityLte(Parcel): " + toString());
+        if (DBG) log(toString());
     }
 
     /** Implement the Parcelable interface */
@@ -308,7 +271,8 @@
             new Creator<CellIdentityLte>() {
                 @Override
                 public CellIdentityLte createFromParcel(Parcel in) {
-                    return new CellIdentityLte(in);
+                    in.readInt();   // skip;
+                    return createFromParcelBody(in);
                 }
 
                 @Override
@@ -317,10 +281,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/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/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index c7e5131..98ea451 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -244,6 +244,13 @@
      */
     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;
+
      /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -349,6 +356,9 @@
                     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;
@@ -543,6 +553,14 @@
     }
 
     /**
+     * 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 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 +672,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/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..d4b4b88 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -16,12 +16,15 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Contains phone state and service related information.
  *
@@ -105,6 +108,31 @@
     /** @hide */
     public static final int RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED = 14;
 
+    /** @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
@@ -162,6 +190,12 @@
      */
     public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
 
+    /**
+     * Number of radio technologies for GSM, UMTS and CDMA.
+     * @hide
+     */
+    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))
@@ -216,6 +250,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;
@@ -1153,7 +1192,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;
@@ -1212,12 +1252,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 +1348,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).
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c84db95..64cc7c1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -361,6 +361,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>
      *
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index d7b6142..e633053 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -2564,6 +2564,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";
@@ -2693,6 +2722,7 @@
          * but is currently only used for LTE (14) and eHRPD (13).
          * <P>Type: INTEGER</P>
          */
+        @Deprecated
         public static final String BEARER = "bearer";
 
         /**
@@ -2704,9 +2734,19 @@
          * <P>Type: INTEGER</P>
          * @hide
          */
+        @Deprecated
         public static final String BEARER_BITMASK = "bearer_bitmask";
 
         /**
+         * Radio technology (network type) bitmask.
+         * To check what values can be contained, refer to
+         * {@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>
@@ -3342,6 +3382,12 @@
         public static final String APN = "apn";
 
         /**
+         * Prefix of Integrated Circuit Card Identifier.
+         * <P>Type: TEXT </P>
+         */
+        public static final String ICCID_PREFIX = "iccid_prefix";
+
+        /**
          * User facing carrier name.
          * <P>Type: TEXT </P>
          */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7245dac..6a68343 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -54,6 +54,7 @@
 
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ITelecomService;
@@ -957,6 +958,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#getSubscriptionCarrierId()} 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
@@ -4720,6 +4779,25 @@
     }
 
     /**
+     * @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;
+    }
+
+    /**
      * Set IMS registration state
      *
      * @param Registration state
@@ -5676,39 +5754,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 +5802,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 */
@@ -6556,6 +6626,55 @@
     }
 
     /**
+     * Returns carrier id of the current subscription.
+     * <p>To recognize a carrier (including MVNO) as a first class identity, assign each carrier
+     * with a canonical integer a.k.a carrier id.
+     *
+     * @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 getSubscriptionCarrierId() {
+        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 #getSubscriptionCarrierId()},
+     * 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 #getSubscriptionCarrierId()} 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 String getSubscriptionCarrierName() {
+        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
@@ -6898,4 +7017,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..3937201 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -32,6 +32,7 @@
 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
@@ -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/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..2ab8d4f
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -0,0 +1,1370 @@
+/*
+ * 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.net.NetworkUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.MalformedURLException;
+import java.net.UnknownHostException;
+import java.net.URL;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.ArrayList;
+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 mBearer;
+    private final int mBearerBitmask;
+
+    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;
+    }
+
+    /**
+     * Radio Access Technology info.
+     * To check what values can hold, refer to ServiceState.java.
+     * This should be spread to other technologies,
+     * but currently only used for LTE(14) and EHRPD(13).
+     *
+     * @return the bearer info of the APN
+     * @hide
+     */
+    public int getBearer() {
+        return mBearer;
+    }
+
+    /**
+     * Returns the radio access technology bitmask for this APN.
+     *
+     * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
+     * technologies in ServiceState.
+     * This should be spread to other technologies,
+     * but currently only used for LTE(14) and EHRPD(13).
+     *
+     * @return the radio access technology bitmask
+     * @hide
+     */
+    public int getBearerBitmask() {
+        return mBearerBitmask;
+    }
+
+    /**
+     * 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;
+    }
+
+    /** @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.mBearer = builder.mBearer;
+        this.mBearerBitmask = builder.mBearerBitmask;
+        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 bearer,
+            int bearerBitmask, 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)
+                .setBearer(bearer)
+                .setBearerBitmask(bearerBitmask)
+                .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)));
+
+        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,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.BEARER_BITMASK)),
+                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.mBearer, apn.mBearerBitmask, 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("[ApnSettingV3] ")
+                .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(mBearer);
+        sb.append(", ").append(mBearerBitmask);
+        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);
+        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(mBearer, other.mBearer)
+                && Objects.equals(mBearerBitmask, other.mBearerBitmask)
+                && 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);
+    }
+
+    /**
+     * Compare two APN settings
+     *
+     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. 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.mBearerBitmask, other.mBearerBitmask)
+                && 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));
+    }
+
+    // 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);
+    }
+
+    /** @hide */
+    // Called by DPM.
+    public ContentValues toContentValues() {
+        ContentValues apnValue = new ContentValues();
+        if (mOperatorNumeric != null) {
+            apnValue.put(Telephony.Carriers.NUMERIC, mOperatorNumeric);
+        }
+        if (mEntryName != null) {
+            apnValue.put(Telephony.Carriers.NAME, mEntryName);
+        }
+        if (mApnName != null) {
+            apnValue.put(Telephony.Carriers.APN, mApnName);
+        }
+        if (mProxy != null) {
+            apnValue.put(Telephony.Carriers.PROXY, inetAddressToString(mProxy));
+        }
+        apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
+        if (mMmsc != null) {
+            apnValue.put(Telephony.Carriers.MMSC, URLToString(mMmsc));
+        }
+        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
+        if (mMmsProxy != null) {
+            apnValue.put(Telephony.Carriers.MMSPROXY, inetAddressToString(mMmsProxy));
+        }
+        if (mUser != null) {
+            apnValue.put(Telephony.Carriers.USER, mUser);
+        }
+        if (mPassword != null) {
+            apnValue.put(Telephony.Carriers.PASSWORD, mPassword);
+        }
+        apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
+        String apnType = deParseTypes(mTypes);
+        if (apnType != null) {
+            apnValue.put(Telephony.Carriers.TYPE, apnType);
+        }
+        if (mProtocol != null) {
+            apnValue.put(Telephony.Carriers.PROTOCOL, mProtocol);
+        }
+        if (mRoamingProtocol != null) {
+            apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, mRoamingProtocol);
+        }
+        apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
+        // networkTypeBit.
+        apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask);
+        if (mMvnoType != null) {
+            apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType);
+        }
+
+        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;
+        }
+        return TextUtils.isEmpty(inetAddress.getHostName())
+                ? inetAddress.getHostAddress() : inetAddress.getHostName();
+    }
+
+    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);
+    }
+
+    private static ApnSetting readFromParcel(Parcel in) {
+        return makeApnSetting(in.readInt(), in.readString(), in.readString(), in.readString(),
+                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
+                in.readInt(), (URL)in.readValue(URL.class.getClassLoader()),
+                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
+                in.readInt(), in.readString(), in.readString(), in.readInt(),
+                Arrays.asList(in.readStringArray()), in.readString(), in.readString(),
+                in.readInt() > 0, 0, 0, 0, false, 0, 0, 0, 0, in.readString(), 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 boolean mCarrierEnabled;
+        private int mBearer;
+        private int mBearerBitmask;
+        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() {}
+
+        /**
+         * 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 bearer info.
+         *
+         * @param bearer the bearer info to set for the APN
+         * @hide
+         */
+        public Builder setBearer(int bearer) {
+            this.mBearer = bearer;
+            return this;
+        }
+
+        /**
+         * Sets the radio access technology bitmask for this APN.
+         *
+         * @param bearerBitmask the radio access technology bitmask to set for this APN
+         * @hide
+         */
+        public Builder setBearerBitmask(int bearerBitmask) {
+            this.mBearerBitmask = bearerBitmask;
+            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;
+        }
+
+        /**
+         * Sets the unique database id for this entry.
+         *
+         * @param id the unique database id to set for this entry
+         */
+        public Builder setId(int id) {
+            this.mId = id;
+            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 of APN.
+         *
+         * @param carrierEnabled the current status to set for this APN
+         */
+        public Builder setCarrierEnabled(boolean carrierEnabled) {
+            this.mCarrierEnabled = carrierEnabled;
+            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..ef3a183 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.LinkAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -40,7 +41,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 +72,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 +97,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 +141,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
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
new file mode 100644
index 0000000..ea08175
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -0,0 +1,540 @@
+/*
+ * 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.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.telephony.SubscriptionManager;
+import android.util.SparseArray;
+
+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";
+
+    private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE          = 1;
+    private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 2;
+    private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 3;
+    private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN               = 4;
+    private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE                     = 5;
+    private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST                   = 6;
+    private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED      = 7;
+    private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED    = 8;
+    private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 9;
+
+    private final HandlerThread mHandlerThread;
+
+    private final DataServiceHandler mHandler;
+
+    private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
+
+    private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>();
+
+    /**
+     * 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 isHandover True if the request is for IWLAN handover.
+         * @param linkProperties If {@code isHandover} is true, this is the link properties of the
+         * existing data connection, otherwise null.
+         * @param callback The result callback for this request.
+         */
+        public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                                  boolean allowRoaming, boolean isHandover,
+                                  LinkProperties linkProperties, 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, boolean, LinkProperties, DataServiceCallback)}.
+         * @param reasonRadioShutDown True if the deactivate request reason is device shut down.
+         * @param isHandover True if the request is for IWLAN handover.
+         * @param callback The result callback for this request.
+         */
+        public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+                                       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.
+         */
+        public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+                                        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.
+         */
+        public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+                                   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(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 boolean isHandover;
+        public final LinkProperties linkProperties;
+        public final IDataServiceCallback callback;
+        SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                             boolean allowRoaming, boolean isHandover,
+                             LinkProperties linkProperties, IDataServiceCallback callback) {
+            this.accessNetworkType = accessNetworkType;
+            this.dataProfile = dataProfile;
+            this.isRoaming = isRoaming;
+            this.allowRoaming = allowRoaming;
+            this.linkProperties = linkProperties;
+            this.isHandover = isHandover;
+            this.callback = callback;
+        }
+    }
+
+    private static final class DeactivateDataCallRequest {
+        public final int cid;
+        public final boolean reasonRadioShutDown;
+        public final boolean isHandover;
+        public final IDataServiceCallback callback;
+        DeactivateDataCallRequest(int cid, boolean reasonRadioShutDown, boolean isHandover,
+                                  IDataServiceCallback callback) {
+            this.cid = cid;
+            this.reasonRadioShutDown = reasonRadioShutDown;
+            this.isHandover = isHandover;
+            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 service;
+
+            synchronized (mServiceMap) {
+                service = mServiceMap.get(slotId);
+            }
+
+            switch (message.what) {
+                case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
+                    service = createDataServiceProvider(message.arg1);
+                    if (service != null) {
+                        mServiceMap.put(slotId, service);
+                    }
+                    break;
+                case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
+                    if (service == null) break;
+                    SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
+                    service.setupDataCall(setupDataCallRequest.accessNetworkType,
+                            setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
+                            setupDataCallRequest.allowRoaming, setupDataCallRequest.isHandover,
+                            setupDataCallRequest.linkProperties,
+                            new DataServiceCallback(setupDataCallRequest.callback));
+
+                    break;
+                case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
+                    if (service == null) break;
+                    DeactivateDataCallRequest deactivateDataCallRequest =
+                            (DeactivateDataCallRequest) message.obj;
+                    service.deactivateDataCall(deactivateDataCallRequest.cid,
+                            deactivateDataCallRequest.reasonRadioShutDown,
+                            deactivateDataCallRequest.isHandover,
+                            new DataServiceCallback(deactivateDataCallRequest.callback));
+                    break;
+                case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
+                    if (service == null) break;
+                    SetInitialAttachApnRequest setInitialAttachApnRequest =
+                            (SetInitialAttachApnRequest) message.obj;
+                    service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+                            setInitialAttachApnRequest.isRoaming,
+                            new DataServiceCallback(setInitialAttachApnRequest.callback));
+                    break;
+                case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
+                    if (service == null) break;
+                    SetDataProfileRequest setDataProfileRequest =
+                            (SetDataProfileRequest) message.obj;
+                    service.setDataProfile(setDataProfileRequest.dps,
+                            setDataProfileRequest.isRoaming,
+                            new DataServiceCallback(setDataProfileRequest.callback));
+                    break;
+                case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
+                    if (service == null) break;
+
+                    service.getDataCallList(new DataServiceCallback(
+                            (IDataServiceCallback) message.obj));
+                    break;
+                case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
+                    if (service == null) break;
+                    service.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+                    break;
+                case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
+                    if (service == null) break;
+                    callback = (IDataServiceCallback) message.obj;
+                    service.unregisterForDataCallListChanged(callback);
+                    break;
+                case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
+                    if (service == null) break;
+                    DataCallListChangedIndication indication =
+                            (DataCallListChangedIndication) message.obj;
+                    try {
+                        indication.callback.onDataCallListChanged(indication.dataCallList);
+                    } catch (RemoteException e) {
+                        loge("Failed to call onDataCallListChanged. " + e);
+                    }
+                    break;
+            }
+        }
+    }
+
+    private 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;
+        }
+
+        int slotId = intent.getIntExtra(
+                DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+
+        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+            loge("Invalid slot id " + slotId);
+            return null;
+        }
+
+        log("onBind: slot id=" + slotId);
+
+        IDataServiceWrapper binder = mBinderMap.get(slotId);
+        if (binder == null) {
+            Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
+            msg.arg1 = slotId;
+            msg.sendToTarget();
+
+            binder = new IDataServiceWrapper(slotId);
+            mBinderMap.put(slotId, binder);
+        }
+
+        return binder;
+    }
+
+    /** @hide */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID,
+                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+        if (mBinderMap.get(slotId) != null) {
+            DataServiceProvider serviceImpl;
+            synchronized (mServiceMap) {
+                serviceImpl = mServiceMap.get(slotId);
+            }
+            if (serviceImpl != null) {
+                serviceImpl.onDestroy();
+            }
+            mBinderMap.remove(slotId);
+        }
+
+        // If all clients unbinds, quit the handler thread
+        if (mBinderMap.size() == 0) {
+            mHandlerThread.quit();
+        }
+
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public void onDestroy() {
+        synchronized (mServiceMap) {
+            for (int i = 0; i < mServiceMap.size(); i++) {
+                DataServiceProvider serviceImpl = mServiceMap.get(i);
+                if (serviceImpl != null) {
+                    serviceImpl.onDestroy();
+                }
+            }
+            mServiceMap.clear();
+        }
+
+        mHandlerThread.quit();
+    }
+
+    /**
+     * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
+     */
+    private class IDataServiceWrapper extends IDataService.Stub {
+
+        private final int mSlotId;
+
+        IDataServiceWrapper(int slotId) {
+            mSlotId = slotId;
+        }
+
+        @Override
+        public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+                                  boolean isRoaming, boolean allowRoaming, boolean isHandover,
+                                  LinkProperties linkProperties, IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0,
+                    new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
+                            allowRoaming, isHandover, linkProperties, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+                                       IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0,
+                    new DeactivateDataCallRequest(cid, reasonRadioShutDown, isHandover, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+                                        IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0,
+                    new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+                                   IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0,
+                    new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
+        }
+
+        @Override
+        public void getDataCallList(IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0,
+                    callback).sendToTarget();
+        }
+
+        @Override
+        public void registerForDataCallListChanged(IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("Callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+                    0, callback).sendToTarget();
+        }
+
+        @Override
+        public void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("Callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+                    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..b6a81f9
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -0,0 +1,172 @@
+/*
+ * 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.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 mTag = 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 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;
+
+    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, boolean, DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link Result}.
+     * @param response Setup data call response.
+     */
+    public void onSetupDataCallComplete(@Result int result, DataCallResponse response) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onSetupDataCallComplete(result, response);
+            } catch (RemoteException e) {
+                Rlog.e(mTag, "Failed to onSetupDataCallComplete on the remote");
+            }
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
+     * boolean, boolean, DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link Result}.
+     */
+    public void onDeactivateDataCallComplete(@Result int result) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onDeactivateDataCallComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(mTag, "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 Result}.
+     */
+    public void onSetInitialAttachApnComplete(@Result int result) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onSetInitialAttachApnComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(mTag, "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 Result}.
+     */
+    @SystemApi
+    public void onSetDataProfileComplete(@Result int result) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onSetDataProfileComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(mTag, "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 Result}.
+     * @param dataCallList List of the current active data connection.
+     */
+    public void onGetDataCallListComplete(@Result int result, List<DataCallResponse> dataCallList) {
+        IDataServiceCallback callback = mCallback.get();
+        if (callback != null) {
+            try {
+                callback.onGetDataCallListComplete(result, dataCallList);
+            } catch (RemoteException e) {
+                Rlog.e(mTag, "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(mTag, "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..4eaaa252
--- /dev/null
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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 setupDataCall(int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
+                       boolean allowRoaming, boolean isHandover, in LinkProperties linkProperties,
+                       IDataServiceCallback callback);
+    void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+                            IDataServiceCallback callback);
+    void setInitialAttachApn(in DataProfile dataProfile, boolean isRoaming,
+                             IDataServiceCallback callback);
+    void setDataProfile(in List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback);
+    void getDataCallList(IDataServiceCallback callback);
+    void registerForDataCallListChanged(IDataServiceCallback callback);
+    void unregisterForDataCallListChanged(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/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
new file mode 100644
index 0000000..b9ed005
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -0,0 +1,663 @@
+/*
+ * 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.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;
+
+/**
+ * EuiccCardManager is the application interface to an eSIM card.
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a 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
+    })
+    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
+    })
+    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;
+
+    /**
+     * 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"));
+    }
+
+    /**
+     * Gets all the profiles on eUicc.
+     *
+     * @param callback The callback to get the result code and all the profiles.
+     */
+    public void getAllProfiles(ResultCallback<EuiccProfileInfo[]> callback) {
+        try {
+            getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(),
+                    new IGetAllProfilesCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
+                            callback.onComplete(resultCode, profiles);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getAllProfiles", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the profile of the given iccid.
+     *
+     * @param iccid The iccid of the profile.
+     * @param callback The callback to get the result code and profile.
+     */
+    public void getProfile(String iccid, ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().getProfile(mContext.getOpPackageName(), iccid,
+                    new IGetProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            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 iccid The iccid of the profile.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param callback The callback to get the result code.
+     */
+    public void disableProfile(String iccid, boolean refresh, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().disableProfile(mContext.getOpPackageName(), iccid, refresh,
+                    new IDisableProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            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 iccid The iccid of the profile to switch to.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     */
+    public void switchToProfile(String iccid, boolean refresh,
+            ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), iccid, refresh,
+                    new ISwitchToProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            callback.onComplete(resultCode, profile);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling switchToProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the EID of the eUICC.
+     *
+     * @return The EID.
+     */
+    public String getEid() {
+        try {
+            return getIEuiccCardController().getEid();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEid", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the nickname of the profile of the given iccid.
+     *
+     * @param iccid The iccid of the profile.
+     * @param nickname The nickname of the profile.
+     * @param callback The callback to get the result code.
+     */
+    public void setNickname(String iccid, String nickname, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().setNickname(mContext.getOpPackageName(), iccid, nickname,
+                    new ISetNicknameCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            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 iccid The iccid of the profile.
+     * @param callback The callback to get the result code.
+     */
+    public void deleteProfile(String iccid, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), iccid,
+                    new IDeleteProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling deleteProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resets the eUICC memory.
+     *
+     * @param options Bits of the options of resetting which parts of the eUICC memory. See
+     *     EuiccCard for details.
+     * @param callback The callback to get the result code.
+     */
+    public void resetMemory(@ResetOption int options, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().resetMemory(mContext.getOpPackageName(), options,
+                    new IResetMemoryCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling resetMemory", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the default SM-DP+ address from eUICC.
+     *
+     * @param callback The callback to get the result code and the default SM-DP+ address.
+     */
+    public void getDefaultSmdpAddress(ResultCallback<String> callback) {
+        try {
+            getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(),
+                    new IGetDefaultSmdpAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, String address) {
+                            callback.onComplete(resultCode, address);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getDefaultSmdpAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the SM-DS address from eUICC.
+     *
+     * @param callback The callback to get the result code and the SM-DS address.
+     */
+    public void getSmdsAddress(ResultCallback<String> callback) {
+        try {
+            getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(),
+                    new IGetSmdsAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, String address) {
+                            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 defaultSmdpAddress The default SM-DP+ address to set.
+     * @param callback The callback to get the result code.
+     */
+    public void setDefaultSmdpAddress(String defaultSmdpAddress, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(),
+                    defaultSmdpAddress,
+                    new ISetDefaultSmdpAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            callback.onComplete(resultCode, null);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling setDefaultSmdpAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets Rules Authorisation Table.
+     *
+     * @param callback the callback to get the result code and the rule authorisation table.
+     */
+    public void getRulesAuthTable(ResultCallback<EuiccRulesAuthTable> callback) {
+        try {
+            getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(),
+                    new IGetRulesAuthTableCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
+                            callback.onComplete(resultCode, rat);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getRulesAuthTable", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the eUICC challenge for new profile downloading.
+     *
+     * @param callback the callback to get the result code and the challenge.
+     */
+    public void getEuiccChallenge(ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(),
+                    new IGetEuiccChallengeCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] challenge) {
+                            callback.onComplete(resultCode, challenge);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEuiccChallenge", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+     *
+     * @param callback the callback to get the result code and the info1.
+     */
+    public void getEuiccInfo1(ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(),
+                    new IGetEuiccInfo1Callback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] info) {
+                            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 callback the callback to get the result code and the info2.
+     */
+    public void getEuiccInfo2(ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(),
+                    new IGetEuiccInfo2Callback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] info) {
+                            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 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 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 matchingId, byte[] serverSigned1,
+            byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().authenticateServer(
+                    mContext.getOpPackageName(),
+                    matchingId,
+                    serverSigned1,
+                    serverSignature1,
+                    euiccCiPkIdToBeUsed,
+                    serverCertificate,
+                    new IAuthenticateServerCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            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 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 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(@Nullable byte[] hashCc, byte[] smdpSigned2,
+            byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().prepareDownload(
+                    mContext.getOpPackageName(),
+                    hashCc,
+                    smdpSigned2,
+                    smdpSignature2,
+                    smdpCertificate,
+                    new IPrepareDownloadCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            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 boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
+     * @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(byte[] boundProfilePackage,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().loadBoundProfilePackage(
+                    mContext.getOpPackageName(),
+                    boundProfilePackage,
+                    new ILoadBoundProfilePackageCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            callback.onComplete(resultCode, response);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling loadBoundProfilePackage", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Cancels the current profile download session.
+     *
+     * @param transactionId the transaction ID returned by SM-DP+ server.
+     * @param reason the cancel reason.
+     * @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(byte[] transactionId, @CancelReason int reason,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().cancelSession(
+                    mContext.getOpPackageName(),
+                    transactionId,
+                    reason,
+                    new ICancelSessionCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            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 events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param callback the callback to get the result code and the list of notifications.
+     */
+    public void listNotifications(@EuiccNotification.Event int events,
+            ResultCallback<EuiccNotification[]> callback) {
+        try {
+            getIEuiccCardController().listNotifications(mContext.getOpPackageName(), events,
+                    new IListNotificationsCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification[] notifications) {
+                            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 events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param callback the callback to get the result code and the list of notifications.
+     */
+    public void retrieveNotificationList(@EuiccNotification.Event int events,
+            ResultCallback<EuiccNotification[]> callback) {
+        try {
+            getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), events,
+                    new IRetrieveNotificationListCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification[] notifications) {
+                            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 seqNumber the sequence number of the notification.
+     * @param callback the callback to get the result code and the notification.
+     */
+    public void retrieveNotification(int seqNumber, ResultCallback<EuiccNotification> callback) {
+        try {
+            getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), seqNumber,
+                    new IRetrieveNotificationCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification notification) {
+                            callback.onComplete(resultCode, notification);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling retrieveNotification", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes a notification from eUICC.
+     *
+     * @param seqNumber the sequence number of the notification.
+     * @param callback the callback to get the result code.
+     */
+    public void removeNotificationFromList(int seqNumber, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().removeNotificationFromList(
+                    mContext.getOpPackageName(),
+                    seqNumber,
+                    new IRemoveNotificationFromListCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            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/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 84d0087..3dfadf5 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -15,8 +15,10 @@
  */
 package android.telephony.euicc;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -29,6 +31,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.
  *
@@ -55,6 +60,20 @@
     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.
+     * TODO(b/35851809): Make this a SystemApi.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_OTA_STATUS_CHANGED
+            = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+
     /**
      * Intent action to provision an embedded subscription.
      *
@@ -167,6 +186,35 @@
      */
     public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
 
+    /**
+     * Euicc OTA update status which can be got by {@link #getOtaStatus}
+     * @hide
+     */
+    @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.
+     */
+    public static final int EUICC_OTA_IN_PROGRESS = 1;
+    /** The OTA update failed. */
+    public static final int EUICC_OTA_FAILED = 2;
+    /** The OTA update finished successfully. */
+    public static final int EUICC_OTA_SUCCEEDED = 3;
+    /** The OTA update not needed since current eUICC OS is latest. */
+    public static final int EUICC_OTA_NOT_NEEDED = 4;
+    /** The OTA status is unavailable since eUICC service is unavailable. */
+    public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
+
     private final Context mContext;
 
     /** @hide */
@@ -211,6 +259,26 @@
     }
 
     /**
+     * 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.
+     */
+    @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,
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/euicc/EuiccNotification.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/InterfaceAddress.aidl
copy to telephony/java/android/telephony/euicc/EuiccNotification.aidl
index d750363..dad770d 100644
--- a/telephony/java/android/telephony/data/InterfaceAddress.aidl
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.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,6 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.telephony.euicc;
 
-parcelable InterfaceAddress;
+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..ef3c1ce
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccNotification.java
@@ -0,0 +1,179 @@
+/*
+ * 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.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
+ *
+ * TODO(b/35851809): Make this a @SystemApi.
+ */
+public class EuiccNotification implements Parcelable {
+    /** Event */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+            EVENT_INSTALL,
+            EVENT_ENABLE,
+            EVENT_DISABLE,
+            EVENT_DELETE
+    })
+    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 above events */
+    @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/core/java/android/os/Seccomp.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
similarity index 83%
rename from core/java/android/os/Seccomp.java
rename to telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
index f14e93f..9785a45 100644
--- a/core/java/android/os/Seccomp.java
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.telephony.euicc;
 
-/**
- * @hide
- */
-public final class Seccomp {
-    public static final native void setPolicy();
-}
+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..7efe043
--- /dev/null
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -0,0 +1,260 @@
+/*
+ * 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.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;
+
+/**
+ * This represents the RAT (Rules Authorisation Table) stored on eUICC.
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a @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
+    })
+    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, CarrierIdentifier[] carrierId, int policyRuleFlags) {
+            if (mPosition >= mPolicyRules.length) {
+                throw new ArrayIndexOutOfBoundsException(mPosition);
+            }
+            mPolicyRules[mPosition] = policyRules;
+            mCarrierIds[mPosition] = carrierId;
+            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/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8230eaf..aaa0f08 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -26,12 +26,14 @@
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MMTelFeature;
 import android.telephony.ims.feature.RcsFeature;
+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.IImsRegistration;
 import com.android.ims.internal.IImsServiceController;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -113,6 +115,12 @@
                 throws RemoteException {
             ImsService.this.removeImsFeature(slotId, featureType, c);
         }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId) throws RemoteException {
+            ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+            return r != null ? r.getBinder() : null;
+        }
     };
 
     /**
@@ -174,6 +182,8 @@
         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) {
@@ -236,4 +246,13 @@
     public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
         return null;
     }
+
+    /**
+     * @param slotId The slot that is associated with the IMS Registration.
+     * @return the ImsRegistration implementation associated with the slot.
+     * @hide
+     */
+    public ImsRegistrationImplBase getRegistration(int slotId) {
+        return new ImsRegistrationImplBase();
+    }
 }
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index ca4a210..d47cea30 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -96,7 +96,7 @@
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
     private @ImsState int mState = STATE_NOT_AVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    private Context mContext;
+    protected Context mContext;
 
     public void setContext(Context context) {
         mContext = context;
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
index b7c8ca0..afaf332 100644
--- a/telephony/java/android/telephony/ims/internal/ImsService.java
+++ b/telephony/java/android/telephony/ims/internal/ImsService.java
@@ -24,7 +24,6 @@
 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;
@@ -32,11 +31,12 @@
 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.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
index 8332bc0..43f5098 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
@@ -24,4 +24,5 @@
  */
 oneway interface IImsMmTelListener {
     void onIncomingCall(IImsCallSession c);
+    void onVoiceMessageCountUpdate(int count);
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
index 8afb955..82a8525 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
@@ -18,12 +18,12 @@
 
 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 com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsRegistration;
 
 /**
  * See ImsService and MmTelFeature for more information.
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
index 4d18873..5dbf077 100644
--- a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
@@ -18,7 +18,7 @@
 
 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;
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
index 2f350c8..057c9a86 100644
--- a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
@@ -28,8 +28,8 @@
 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.ImsEcbmImplBase;
 import android.telephony.ims.stub.ImsMultiEndpointImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
@@ -261,6 +261,15 @@
         }
 
         /**
+         * 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) {
+
+        }
+
+        /**
          * Called when the IMS provider receives an incoming call.
          * @param c The {@link ImsCallSession} associated with the new call.
          */
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
similarity index 83%
rename from telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
rename to telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 558b009..42af083 100644
--- a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -14,16 +14,19 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.net.Uri;
+import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsRegistration;
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
 import com.android.ims.ImsReasonInfo;
+import com.android.ims.internal.IImsRegistration;
+import com.android.ims.internal.IImsRegistrationCallback;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -62,23 +65,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 +93,6 @@
          * @param imsRadioTech the radio access technology. Valid values are defined in
          * {@link ImsRegistrationTech}.
          */
-        @Override
         public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         }
 
@@ -97,7 +101,6 @@
          *
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
-        @Override
         public void onDeregistered(ImsReasonInfo info) {
         }
 
@@ -108,10 +111,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,9 +151,9 @@
     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();
 
     public final IImsRegistration getBinder() {
         return mBinder;
@@ -221,6 +233,17 @@
         });
     }
 
+    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 +264,8 @@
         }
     }
 
-    private @ImsRegistrationTech int getConnectionType() {
+    @VisibleForTesting
+    public final @ImsRegistrationTech int getConnectionType() {
         synchronized (mLock) {
             return mConnectionType;
         }
@@ -271,6 +295,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/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 489c208..693aaff 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -351,7 +351,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>() {
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
similarity index 82%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
rename to telephony/java/com/android/ims/internal/IImsRegistration.aidl
index 687b7ca..6de264e 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistration.aidl
@@ -15,10 +15,9 @@
  */
 
 
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
 
-import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import com.android.ims.internal.IImsRegistrationCallback;
 
 /**
  * See ImsRegistration for more information.
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
similarity index 89%
rename from telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
rename to telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
index a50575b..5f21167 100644
--- a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationCallback.aidl
@@ -15,8 +15,9 @@
  */
 
 
-package android.telephony.ims.internal.aidl;
+package com.android.ims.internal;
 
+import android.net.Uri;
 import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
 
 import com.android.ims.ImsReasonInfo;
@@ -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/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 857089f..7ac25ac 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -18,6 +18,7 @@
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsRcsFeature;
 
 /**
@@ -29,4 +30,5 @@
     IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
     IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
     void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+    IImsRegistration getRegistration(int slotId);
 }
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/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 64083e3..fba82ee 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -40,6 +40,7 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
@@ -808,6 +809,11 @@
     IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
 
     /**
+    * Returns the IImsRegistration associated with the slot and feature specified.
+    */
+    IImsRegistration getImsRegistration(int slotId, int feature);
+
+    /**
      * Set the network selection mode to automatic.
      *
      * @param subId the id of the subscription to update.
@@ -870,14 +876,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.
@@ -1298,6 +1323,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
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/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index e2d25b8..f804cb0 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -417,6 +417,8 @@
     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_RESPONSE_ACKNOWLEDGEMENT = 800;
 
@@ -471,4 +473,5 @@
     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;
 }
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..abc55c7
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -0,0 +1,82 @@
+/*
+ * 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, in IGetAllProfilesCallback callback);
+    oneway void getProfile(String callingPackage, String iccid, in IGetProfileCallback callback);
+    oneway void disableProfile(String callingPackage, String iccid, boolean refresh,
+        in IDisableProfileCallback callback);
+    oneway void switchToProfile(String callingPackage, String iccid, boolean refresh,
+        in ISwitchToProfileCallback callback);
+    String getEid();
+    oneway void setNickname(String callingPackage, String iccid, String nickname,
+        in ISetNicknameCallback callback);
+    oneway void deleteProfile(String callingPackage, String iccid,
+        in IDeleteProfileCallback callback);
+    oneway void resetMemory(String callingPackage, int options, in IResetMemoryCallback callback);
+    oneway void getDefaultSmdpAddress(String callingPackage,
+        in IGetDefaultSmdpAddressCallback callback);
+    oneway void getSmdsAddress(String callingPackage, in IGetSmdsAddressCallback callback);
+    oneway void setDefaultSmdpAddress(String callingPackage, String address,
+        in ISetDefaultSmdpAddressCallback callback);
+    oneway void getRulesAuthTable(String callingPackage, in IGetRulesAuthTableCallback callback);
+    oneway void getEuiccChallenge(String callingPackage, in IGetEuiccChallengeCallback callback);
+    oneway void getEuiccInfo1(String callingPackage, in IGetEuiccInfo1Callback callback);
+    oneway void getEuiccInfo2(String callingPackage, in IGetEuiccInfo2Callback callback);
+    oneway void authenticateServer(String callingPackage, String matchingId,
+        in byte[] serverSigned1, in byte[] serverSignature1, in byte[] euiccCiPkIdToBeUsed,
+        in byte[] serverCertificatein, in IAuthenticateServerCallback callback);
+    oneway void prepareDownload(String callingPackage, in byte[] hashCc, in byte[] smdpSigned2,
+        in byte[] smdpSignature2, in byte[] smdpCertificate, in IPrepareDownloadCallback callback);
+    oneway void loadBoundProfilePackage(String callingPackage, in byte[] boundProfilePackage,
+        in ILoadBoundProfilePackageCallback callback);
+    oneway void cancelSession(String callingPackage, in byte[] transactionId, int reason,
+        in ICancelSessionCallback callback);
+    oneway void listNotifications(String callingPackage, int events,
+        in IListNotificationsCallback callback);
+    oneway void retrieveNotificationList(String callingPackage, int events,
+        in IRetrieveNotificationListCallback callback);
+    oneway void retrieveNotification(String callingPackage, int seqNumber,
+        in IRetrieveNotificationCallback callback);
+    oneway void removeNotificationFromList(String callingPackage, 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/legacy-test/Android.bp b/test-base/Android.bp
similarity index 66%
rename from legacy-test/Android.bp
rename to test-base/Android.bp
index 1173bc6..a42dc5a 100644
--- a/legacy-test/Android.bp
+++ b/test-base/Android.bp
@@ -14,6 +14,24 @@
 // 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"],
+
+    no_framework_libs: true,
+    hostdex: true,
+    libs: [
+        "framework",
+    ],
+
+}
+
 // Build the legacy-test library
 // =============================
 // This contains the junit.framework and android.test classes that were in
@@ -21,23 +39,27 @@
 // Also contains the com.android.internal.util.Predicate[s] classes.
 java_library {
     name: "legacy-test",
-
-    srcs: ["src/**/*.java"],
+    static_libs: ["android.test.base"],
 
     no_framework_libs: true,
     libs: [
         "framework",
     ],
-
 }
 
-// Build the repackaged-legacy-test library
-// ========================================
-// This contains repackaged versions of the classes from legacy-test.
+// Build the repackaged.android.test.base library
+// ==============================================
+// This contains repackaged versions of the classes from
+// android.test.base.
 java_library_static {
-    name: "repackaged-legacy-test",
+    name: "repackaged.android.test.base",
 
-    static_libs: ["legacy-test"],
+    static_libs: ["android.test.base"],
+
+    no_framework_libs: true,
+    libs: [
+        "framework",
+    ],
 
     jarjar_rules: "jarjar-rules.txt",
 }
@@ -56,7 +78,7 @@
     ],
 
     static_libs: [
-        "android.test.runner",
+        "android.test.runner-minus-junit",
         "android.test.mock",
     ],
 
diff --git a/test-base/Android.mk b/test-base/Android.mk
new file mode 100644
index 0000000..25c3d76
--- /dev/null
+++ b/test-base/Android.mk
@@ -0,0 +1,120 @@
+#
+# 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
+
+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)
+
+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 100%
rename from legacy-test/src/android/test/PerformanceTestCase.java
rename to test-base/src/android/test/PerformanceTestCase.java
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-mock/Android.bp b/test-mock/Android.bp
index 8eddec4..b1ae40e 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -24,7 +24,6 @@
     no_framework_libs: true,
     libs: [
         "framework",
-        "legacy-test",
     ],
 }
 
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-runner/Android.bp b/test-runner/Android.bp
index 104ae82..dfaeed5 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -24,11 +24,28 @@
     no_framework_libs: true,
     libs: [
         "framework",
-        "legacy-test",
+        "android.test.base",
         "android.test.mock",
     ],
 }
 
+// Build the android.test.runner-minus-junit library
+// =================================================
+// This is provided solely for use by the legacy-android-test module.
+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 {
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 6cf2d56..cdc7756 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -26,7 +26,7 @@
     core-oj \
     core-libart \
     framework \
-    legacy-test \
+    android.test.base \
     android.test.mock \
 
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
@@ -65,7 +65,7 @@
 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
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/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/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index efc01f2a..f6c5532 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -36,19 +36,16 @@
     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());
     }
 
     @Test
@@ -57,34 +54,21 @@
 
         IpSecConfig c = new IpSecConfig();
         c.setMode(IpSecTransform.MODE_TUNNEL);
-        c.setLocalAddress("0.0.0.0");
-        c.setRemoteAddress("1.2.3.4");
+        c.setSourceAddress("0.0.0.0");
+        c.setDestinationAddress("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);
+        c.setSpiResourceId(1984);
         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/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..9aad413 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());
             }
         }
     }
@@ -191,7 +191,7 @@
 
             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));
         }
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/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 113cd37..b8e37f3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -55,14 +55,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;
@@ -116,6 +122,7 @@
 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;
@@ -132,6 +139,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 +182,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.
@@ -872,7 +883,7 @@
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
         mService = new WrappedConnectivityService(mServiceContext,
-                mock(INetworkManagementService.class),
+                mNetworkManagementService,
                 mStatsService,
                 mock(INetworkPolicyManager.class),
                 mock(IpConnectivityLog.class));
@@ -1381,39 +1392,75 @@
             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);
+            } else {
+                expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent);
+            }
             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) {
+        NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
             CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
             NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
             assertTrue(nc.hasCapability(capability));
+            return nc;
         }
 
-        void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+        NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
             CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
             NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
             assertFalse(nc.hasCapability(capability));
+            return nc;
         }
 
         void assertNoCallback() {
@@ -1450,8 +1497,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 +1512,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 +1536,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 +1549,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 +1588,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 +1630,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 +1650,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 +1666,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 +1689,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 +1723,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 +1747,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 +1756,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 +1773,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);
@@ -1760,13 +1807,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 +1836,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 +1847,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 +1856,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 +2029,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 +2058,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 +2085,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 +2098,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 +2108,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 +2132,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 +2143,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 +2156,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 +2199,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 +2237,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 +2253,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 +2382,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 +2399,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 +2407,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 +2428,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.
@@ -2404,7 +2449,7 @@
         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);
@@ -2444,18 +2489,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 +2524,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 +2545,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 +2685,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 +2776,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 +2805,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 +2827,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 +2842,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 +2853,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 +2905,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 +2926,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);
 
@@ -3370,7 +3413,7 @@
 
         // Bring up wifi aware network.
         wifiAware.connect(false, false);
-        callback.expectAvailableCallbacks(wifiAware);
+        callback.expectAvailableCallbacksUnvalidated(wifiAware);
 
         assertNull(mCm.getActiveNetworkInfo());
         assertNull(mCm.getActiveNetwork());
@@ -3489,6 +3532,44 @@
         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);
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 80e42a3..4fbb228 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -32,7 +32,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 +53,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 +95,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 +113,30 @@
 
     @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),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        eq(TEST_SPI_OUT));
+                        eq(spiResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
 
         // Verify quota and RefcountedResource objects cleaned up
         IpSecService.UserRecord userRecord =
@@ -169,17 +152,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());
@@ -190,11 +168,7 @@
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(spiResp.resourceId),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        eq(TEST_SPI_OUT));
+                        eq(spiResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
@@ -206,14 +180,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,20 +193,14 @@
     }
 
     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
@@ -251,32 +217,10 @@
                 .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),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        eq(IpSecTransform.DIRECTION_IN),
-                        anyString(),
-                        anyString(),
-                        anyLong(),
-                        eq(TEST_SPI_IN),
+                        eq(TEST_SPI),
                         eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
                         eq(AUTH_KEY),
                         anyInt(),
@@ -296,8 +240,7 @@
         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());
@@ -307,11 +250,10 @@
                 .ipSecAddSecurityAssociation(
                         eq(createTransformResp.resourceId),
                         anyInt(),
-                        eq(IpSecTransform.DIRECTION_OUT),
                         anyString(),
                         anyString(),
                         anyLong(),
-                        eq(TEST_SPI_OUT),
+                        eq(TEST_SPI),
                         eq(""),
                         eq(new byte[] {}),
                         eq(0),
@@ -324,85 +266,6 @@
                         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),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(AEAD_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        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
@@ -417,18 +280,7 @@
 
         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(createTransformResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
 
         // Verify quota and RefcountedResource objects cleaned up
         IpSecService.UserRecord userRecord =
@@ -462,18 +314,7 @@
 
         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(createTransformResp.resourceId), anyString(), anyString(), eq(TEST_SPI));
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
@@ -497,30 +338,22 @@
         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..3eba881 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});
@@ -270,6 +299,119 @@
     }
 
     @Test
+    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.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 testDeleteInvalidTransportModeTransform() throws Exception {
         try {
             mIpSecService.deleteTransportModeTransform(1);
@@ -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());
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/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/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..f278aec
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
@@ -0,0 +1,28 @@
+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)
+    }
+}
\ No newline at end of file
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..3e9d92c
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -0,0 +1,56 @@
+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)
+}
\ No newline at end of file
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..620f798
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
@@ -0,0 +1,52 @@
+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
+        }
+    }
+
+    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, this::isParcelable)
+            parcelables.sort()
+        }
+
+        private fun isParcelable(c: String?): Boolean {
+            if (c == null) {
+                return false
+            }
+
+            if (c == "android/os/Parcelable") {
+                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..edfc825
--- /dev/null
+++ b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
@@ -0,0 +1,57 @@
+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/os/Parcelable", "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/os/Parcelable", "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/os/Parcelable", "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/os/Parcelable", "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()))
+}