Merge "AAPT2: Allow output artifacts to be filtered."
diff --git a/Android.bp b/Android.bp
index 2ea4894..487bf7a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,6 +12,649 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Build the master framework library.
+
+// READ ME: ########################################################
+//
+// When updating this list of aidl files, consider if that aidl is
+// part of the SDK API.  If it is, also add it to the list in Android.mk
+// that is preprocessed and distributed with the SDK.  This list should
+// not contain any aidl files for parcelables, but the one below should
+// if you intend for 3rd parties to be able to send those objects
+// across process boundaries.
+//
+// READ ME: ########################################################
+
+java_library {
+    name: "framework",
+
+    srcs: [
+        // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
+        "core/java/**/*.java",
+        "graphics/java/**/*.java",
+        "location/java/**/*.java",
+        "lowpan/java/**/*.java",
+        "media/java/**/*.java",
+        "media/mca/effect/java/**/*.java",
+        "media/mca/filterfw/java/**/*.java",
+        "media/mca/filterpacks/java/**/*.java",
+        "drm/java/**/*.java",
+        "opengl/java/**/*.java",
+        "sax/java/**/*.java",
+        "telecomm/java/**/*.java",
+        "telephony/java/**/*.java",
+        "wifi/java/**/*.java",
+        "keystore/java/**/*.java",
+        "rs/java/**/*.java",
+
+        ":framework-javastream-protos",
+
+        "core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl",
+        "core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl",
+        "core/java/android/accounts/IAccountManager.aidl",
+        "core/java/android/accounts/IAccountManagerResponse.aidl",
+        "core/java/android/accounts/IAccountAuthenticator.aidl",
+        "core/java/android/accounts/IAccountAuthenticatorResponse.aidl",
+        "core/java/android/app/IActivityController.aidl",
+        "core/java/android/app/IActivityManager.aidl",
+        "core/java/android/app/IActivityPendingResult.aidl",
+        "core/java/android/app/IAlarmCompleteListener.aidl",
+        "core/java/android/app/IAlarmListener.aidl",
+        "core/java/android/app/IAlarmManager.aidl",
+        "core/java/android/app/IAppTask.aidl",
+        "core/java/android/app/IApplicationThread.aidl",
+        "core/java/android/app/IAssistDataReceiver.aidl",
+        "core/java/android/app/ITaskStackListener.aidl",
+        "core/java/android/app/IBackupAgent.aidl",
+        "core/java/android/app/IEphemeralResolver.aidl",
+        "core/java/android/app/IInputForwarder.aidl",
+        "core/java/android/app/IInstantAppResolver.aidl",
+        "core/java/android/app/IInstrumentationWatcher.aidl",
+        "core/java/android/app/INotificationManager.aidl",
+        "core/java/android/app/IProcessObserver.aidl",
+        "core/java/android/app/ISearchManager.aidl",
+        "core/java/android/app/ISearchManagerCallback.aidl",
+        "core/java/android/app/IServiceConnection.aidl",
+        "core/java/android/app/IStopUserCallback.aidl",
+        "core/java/android/app/job/IJobCallback.aidl",
+        "core/java/android/app/job/IJobScheduler.aidl",
+        "core/java/android/app/job/IJobService.aidl",
+        "core/java/android/app/ITransientNotification.aidl",
+        "core/java/android/app/IUidObserver.aidl",
+        "core/java/android/app/IUiAutomationConnection.aidl",
+        "core/java/android/app/IUiModeManager.aidl",
+        "core/java/android/app/IUserSwitchObserver.aidl",
+        "core/java/android/app/IWallpaperManager.aidl",
+        "core/java/android/app/IWallpaperManagerCallback.aidl",
+        "core/java/android/app/admin/IDeviceAdminService.aidl",
+        "core/java/android/app/admin/IDevicePolicyManager.aidl",
+        "core/java/android/app/trust/IStrongAuthTracker.aidl",
+        "core/java/android/app/trust/ITrustManager.aidl",
+        "core/java/android/app/trust/ITrustListener.aidl",
+        "core/java/android/app/backup/IBackupManager.aidl",
+        "core/java/android/app/backup/IBackupObserver.aidl",
+        "core/java/android/app/backup/IBackupManagerMonitor.aidl",
+        "core/java/android/app/backup/IFullBackupRestoreObserver.aidl",
+        "core/java/android/app/backup/IRestoreObserver.aidl",
+        "core/java/android/app/backup/IRestoreSession.aidl",
+        "core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+        "core/java/android/app/timezone/ICallback.aidl",
+        "core/java/android/app/timezone/IRulesManager.aidl",
+        "core/java/android/app/usage/ICacheQuotaService.aidl",
+        "core/java/android/app/usage/IStorageStatsManager.aidl",
+        "core/java/android/app/usage/IUsageStatsManager.aidl",
+        ":libbluetooth-binder-aidl",
+        "core/java/android/content/IClipboard.aidl",
+        "core/java/android/content/IContentService.aidl",
+        "core/java/android/content/IIntentReceiver.aidl",
+        "core/java/android/content/IIntentSender.aidl",
+        "core/java/android/content/IOnPrimaryClipChangedListener.aidl",
+        "core/java/android/content/IRestrictionsManager.aidl",
+        "core/java/android/content/ISyncAdapter.aidl",
+        "core/java/android/content/ISyncContext.aidl",
+        "core/java/android/content/ISyncServiceAdapter.aidl",
+        "core/java/android/content/ISyncStatusObserver.aidl",
+        "core/java/android/content/om/IOverlayManager.aidl",
+        "core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl",
+        "core/java/android/content/pm/IDexModuleRegisterCallback.aidl",
+        "core/java/android/content/pm/ILauncherApps.aidl",
+        "core/java/android/content/pm/IOnAppsChangedListener.aidl",
+        "core/java/android/content/pm/IOnPermissionsChangeListener.aidl",
+        "core/java/android/content/pm/IOtaDexopt.aidl",
+        "core/java/android/content/pm/IPackageDataObserver.aidl",
+        "core/java/android/content/pm/IPackageDeleteObserver.aidl",
+        "core/java/android/content/pm/IPackageDeleteObserver2.aidl",
+        "core/java/android/content/pm/IPackageInstallObserver2.aidl",
+        "core/java/android/content/pm/IPackageInstaller.aidl",
+        "core/java/android/content/pm/IPackageInstallerCallback.aidl",
+        "core/java/android/content/pm/IPackageInstallerSession.aidl",
+        "core/java/android/content/pm/IPackageManager.aidl",
+        ":libbinder_aidl",
+        "core/java/android/content/pm/IPackageMoveObserver.aidl",
+        "core/java/android/content/pm/IPackageStatsObserver.aidl",
+        "core/java/android/content/pm/IPinItemRequest.aidl",
+        "core/java/android/content/pm/IShortcutService.aidl",
+        "core/java/android/content/pm/dex/IArtManager.aidl",
+        "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
+        "core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
+        "core/java/android/database/IContentObserver.aidl",
+        ":libcamera_client_aidl",
+        ":libcamera_client_framework_aidl",
+        "core/java/android/hardware/IConsumerIrService.aidl",
+        "core/java/android/hardware/ISerialManager.aidl",
+        "core/java/android/hardware/display/IDisplayManager.aidl",
+        "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
+        "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
+        "core/java/android/hardware/fingerprint/IFingerprintService.aidl",
+        "core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
+        "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
+        "core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
+        "core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
+        "core/java/android/hardware/hdmi/IHdmiControlService.aidl",
+        "core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl",
+        "core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl",
+        "core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl",
+        "core/java/android/hardware/hdmi/IHdmiMhlVendorCommandListener.aidl",
+        "core/java/android/hardware/hdmi/IHdmiRecordListener.aidl",
+        "core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl",
+        "core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl",
+        "core/java/android/hardware/input/IInputManager.aidl",
+        "core/java/android/hardware/input/IInputDevicesChangedListener.aidl",
+        "core/java/android/hardware/input/ITabletModeChangedListener.aidl",
+        "core/java/android/hardware/location/IActivityRecognitionHardware.aidl",
+        "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl",
+        "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl",
+        "core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl",
+        "core/java/android/hardware/location/IFusedLocationHardware.aidl",
+        "core/java/android/hardware/location/IFusedLocationHardwareSink.aidl",
+        "core/java/android/hardware/location/IGeofenceHardware.aidl",
+        "core/java/android/hardware/location/IGeofenceHardwareCallback.aidl",
+        "core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl",
+        "core/java/android/hardware/location/IContextHubCallback.aidl",
+        "core/java/android/hardware/location/IContextHubClient.aidl",
+        "core/java/android/hardware/location/IContextHubClientCallback.aidl",
+        "core/java/android/hardware/location/IContextHubService.aidl",
+        "core/java/android/hardware/location/IContextHubTransactionCallback.aidl",
+        "core/java/android/hardware/radio/IRadioService.aidl",
+        "core/java/android/hardware/radio/ITuner.aidl",
+        "core/java/android/hardware/radio/ITunerCallback.aidl",
+        "core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
+        "core/java/android/hardware/usb/IUsbManager.aidl",
+        "core/java/android/net/ICaptivePortal.aidl",
+        "core/java/android/net/IConnectivityManager.aidl",
+        "core/java/android/net/IIpConnectivityMetrics.aidl",
+        "core/java/android/net/IEthernetManager.aidl",
+        "core/java/android/net/IEthernetServiceListener.aidl",
+        "core/java/android/net/INetdEventCallback.aidl",
+        "core/java/android/net/IIpSecService.aidl",
+        "core/java/android/net/INetworkManagementEventObserver.aidl",
+        "core/java/android/net/INetworkPolicyListener.aidl",
+        "core/java/android/net/INetworkPolicyManager.aidl",
+        "core/java/android/net/INetworkRecommendationProvider.aidl",
+        "core/java/android/net/INetworkScoreCache.aidl",
+        "core/java/android/net/INetworkScoreService.aidl",
+        "core/java/android/net/INetworkStatsService.aidl",
+        "core/java/android/net/INetworkStatsSession.aidl",
+        "core/java/android/net/ITetheringStatsProvider.aidl",
+        "core/java/android/net/nsd/INsdManager.aidl",
+        "core/java/android/nfc/IAppCallback.aidl",
+        "core/java/android/nfc/INfcAdapter.aidl",
+        "core/java/android/nfc/INfcAdapterExtras.aidl",
+        "core/java/android/nfc/INfcTag.aidl",
+        "core/java/android/nfc/INfcCardEmulation.aidl",
+        "core/java/android/nfc/INfcFCardEmulation.aidl",
+        "core/java/android/nfc/INfcUnlockHandler.aidl",
+        "core/java/android/nfc/INfcDta.aidl",
+        "core/java/android/nfc/ITagRemovedCallback.aidl",
+        "core/java/android/os/IBatteryPropertiesListener.aidl",
+        "core/java/android/os/IBatteryPropertiesRegistrar.aidl",
+        "core/java/android/os/ICancellationSignal.aidl",
+        "core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
+        "core/java/android/os/IDeviceIdleController.aidl",
+        "core/java/android/os/IHardwarePropertiesManager.aidl",
+        "core/java/android/os/IIncidentManager.aidl",
+        "core/java/android/os/IIncidentReportCompletedListener.aidl",
+        "core/java/android/os/IIncidentReportStatusListener.aidl",
+        "core/java/android/os/IMaintenanceActivityListener.aidl",
+        "core/java/android/os/IMessenger.aidl",
+        "core/java/android/os/INetworkActivityListener.aidl",
+        "core/java/android/os/INetworkManagementService.aidl",
+        "core/java/android/os/IPermissionController.aidl",
+        "core/java/android/os/IProcessInfoService.aidl",
+        "core/java/android/os/IProgressListener.aidl",
+        "core/java/android/os/IPowerManager.aidl",
+        "core/java/android/os/IRecoverySystem.aidl",
+        "core/java/android/os/IRecoverySystemProgressListener.aidl",
+        "core/java/android/os/IRemoteCallback.aidl",
+        "core/java/android/os/ISchedulingPolicyService.aidl",
+        "core/java/android/os/IStatsCompanionService.aidl",
+        "core/java/android/os/IStatsManager.aidl",
+        "core/java/android/os/IThermalEventListener.aidl",
+        "core/java/android/os/IThermalService.aidl",
+        "core/java/android/os/IUpdateLock.aidl",
+        "core/java/android/os/IUserManager.aidl",
+        "core/java/android/os/IVibratorService.aidl",
+        "core/java/android/os/storage/IStorageManager.aidl",
+        "core/java/android/os/storage/IStorageEventListener.aidl",
+        "core/java/android/os/storage/IStorageShutdownObserver.aidl",
+        "core/java/android/os/storage/IObbActionListener.aidl",
+        "core/java/android/security/IKeystoreService.aidl",
+        "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
+        "core/java/android/service/autofill/IAutoFillService.aidl",
+        "core/java/android/service/autofill/IFillCallback.aidl",
+        "core/java/android/service/autofill/ISaveCallback.aidl",
+        "core/java/android/service/carrier/ICarrierService.aidl",
+        "core/java/android/service/carrier/ICarrierMessagingCallback.aidl",
+        "core/java/android/service/carrier/ICarrierMessagingService.aidl",
+        "core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl",
+        "core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl",
+        "core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl",
+        "core/java/android/service/euicc/IEuiccService.aidl",
+        "core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl",
+        "core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl",
+        "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/IRetainSubscriptionsForFactoryResetCallback.aidl",
+        "core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
+        "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
+        "core/java/android/service/gatekeeper/IGateKeeperService.aidl",
+        "core/java/android/service/notification/INotificationListener.aidl",
+        "core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
+        "core/java/android/service/notification/IConditionListener.aidl",
+        "core/java/android/service/notification/IConditionProvider.aidl",
+        "core/java/android/service/settings/suggestions/ISuggestionService.aidl",
+        "core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
+        "core/java/android/service/vr/IVrListener.aidl",
+        "core/java/android/service/vr/IVrManager.aidl",
+        "core/java/android/service/vr/IVrStateCallbacks.aidl",
+        "core/java/android/print/ILayoutResultCallback.aidl",
+        "core/java/android/print/IPrinterDiscoveryObserver.aidl",
+        "core/java/android/print/IPrintDocumentAdapter.aidl",
+        "core/java/android/print/IPrintDocumentAdapterObserver.aidl",
+        "core/java/android/print/IPrintJobStateChangeListener.aidl",
+        "core/java/android/print/IPrintServicesChangeListener.aidl",
+        "core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl",
+        "core/java/android/print/IPrintManager.aidl",
+        "core/java/android/print/IPrintSpooler.aidl",
+        "core/java/android/print/IPrintSpoolerCallbacks.aidl",
+        "core/java/android/print/IPrintSpoolerClient.aidl",
+        "core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl",
+        "core/java/android/printservice/recommendation/IRecommendationService.aidl",
+        "core/java/android/print/IWriteResultCallback.aidl",
+        "core/java/android/printservice/IPrintService.aidl",
+        "core/java/android/printservice/IPrintServiceClient.aidl",
+        "core/java/android/companion/ICompanionDeviceManager.aidl",
+        "core/java/android/companion/ICompanionDeviceDiscoveryService.aidl",
+        "core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl",
+        "core/java/android/companion/IFindDeviceCallback.aidl",
+        "core/java/android/service/dreams/IDreamManager.aidl",
+        "core/java/android/service/dreams/IDreamService.aidl",
+        "core/java/android/service/oemlock/IOemLockService.aidl",
+        "core/java/android/service/persistentdata/IPersistentDataBlockService.aidl",
+        "core/java/android/service/trust/ITrustAgentService.aidl",
+        "core/java/android/service/trust/ITrustAgentServiceCallback.aidl",
+        "core/java/android/service/voice/IVoiceInteractionService.aidl",
+        "core/java/android/service/voice/IVoiceInteractionSession.aidl",
+        "core/java/android/service/voice/IVoiceInteractionSessionService.aidl",
+        "core/java/android/service/wallpaper/IWallpaperConnection.aidl",
+        "core/java/android/service/wallpaper/IWallpaperEngine.aidl",
+        "core/java/android/service/wallpaper/IWallpaperService.aidl",
+        "core/java/android/service/chooser/IChooserTargetService.aidl",
+        "core/java/android/service/chooser/IChooserTargetResult.aidl",
+        "core/java/android/service/resolver/IResolverRankerService.aidl",
+        "core/java/android/service/resolver/IResolverRankerResult.aidl",
+        "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
+        "core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl",
+        "core/java/android/view/accessibility/IAccessibilityManager.aidl",
+        "core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
+        "core/java/android/view/autofill/IAutoFillManager.aidl",
+        "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
+        "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
+        "core/java/android/view/IApplicationToken.aidl",
+        "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
+        "core/java/android/view/IDockedStackListener.aidl",
+        "core/java/android/view/IGraphicsStats.aidl",
+        "core/java/android/view/IGraphicsStatsCallback.aidl",
+        "core/java/android/view/IInputFilter.aidl",
+        "core/java/android/view/IInputFilterHost.aidl",
+        "core/java/android/view/IOnKeyguardExitResult.aidl",
+        "core/java/android/view/IPinnedStackController.aidl",
+        "core/java/android/view/IPinnedStackListener.aidl",
+        "core/java/android/view/IRotationWatcher.aidl",
+        "core/java/android/view/IWallpaperVisibilityListener.aidl",
+        "core/java/android/view/IWindow.aidl",
+        "core/java/android/view/IWindowFocusObserver.aidl",
+        "core/java/android/view/IWindowId.aidl",
+        "core/java/android/view/IWindowManager.aidl",
+        "core/java/android/view/IWindowSession.aidl",
+        "core/java/android/view/IWindowSessionCallback.aidl",
+        "core/java/android/webkit/IWebViewUpdateService.aidl",
+        "core/java/android/speech/IRecognitionListener.aidl",
+        "core/java/android/speech/IRecognitionService.aidl",
+        "core/java/android/speech/tts/ITextToSpeechCallback.aidl",
+        "core/java/android/speech/tts/ITextToSpeechService.aidl",
+        "core/java/com/android/internal/app/IAppOpsCallback.aidl",
+        "core/java/com/android/internal/app/IAppOpsService.aidl",
+        "core/java/com/android/internal/app/IBatteryStats.aidl",
+        "core/java/com/android/internal/app/ISoundTriggerService.aidl",
+        "core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
+        "core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
+        "core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
+        "core/java/com/android/internal/app/IVoiceInteractor.aidl",
+        "core/java/com/android/internal/app/IVoiceInteractorCallback.aidl",
+        "core/java/com/android/internal/app/IVoiceInteractorRequest.aidl",
+        "core/java/com/android/internal/app/IMediaContainerService.aidl",
+        "core/java/com/android/internal/app/procstats/IProcessStats.aidl",
+        "core/java/com/android/internal/appwidget/IAppWidgetService.aidl",
+        "core/java/com/android/internal/appwidget/IAppWidgetHost.aidl",
+        "core/java/com/android/internal/backup/IBackupTransport.aidl",
+        "core/java/com/android/internal/backup/IObbBackupService.aidl",
+        "core/java/com/android/internal/car/ICarServiceHelper.aidl",
+        "core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl",
+        "core/java/com/android/internal/net/INetworkWatchlistManager.aidl",
+        "core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl",
+        "core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl",
+        "core/java/com/android/internal/policy/IKeyguardExitCallback.aidl",
+        "core/java/com/android/internal/policy/IKeyguardService.aidl",
+        "core/java/com/android/internal/policy/IKeyguardStateCallback.aidl",
+        "core/java/com/android/internal/policy/IShortcutService.aidl",
+        "core/java/com/android/internal/os/IDropBoxManagerService.aidl",
+        "core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl",
+        "core/java/com/android/internal/os/IResultReceiver.aidl",
+        "core/java/com/android/internal/os/IShellCallback.aidl",
+        "core/java/com/android/internal/statusbar/IStatusBar.aidl",
+        "core/java/com/android/internal/statusbar/IStatusBarService.aidl",
+        "core/java/com/android/internal/textservice/ISpellCheckerService.aidl",
+        "core/java/com/android/internal/textservice/ISpellCheckerServiceCallback.aidl",
+        "core/java/com/android/internal/textservice/ISpellCheckerSession.aidl",
+        "core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl",
+        "core/java/com/android/internal/textservice/ITextServicesManager.aidl",
+        "core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl",
+        "core/java/com/android/internal/view/IDragAndDropPermissions.aidl",
+        "core/java/com/android/internal/view/IInputContext.aidl",
+        "core/java/com/android/internal/view/IInputContextCallback.aidl",
+        "core/java/com/android/internal/view/IInputMethod.aidl",
+        "core/java/com/android/internal/view/IInputMethodClient.aidl",
+        "core/java/com/android/internal/view/IInputMethodManager.aidl",
+        "core/java/com/android/internal/view/IInputMethodSession.aidl",
+        "core/java/com/android/internal/view/IInputSessionCallback.aidl",
+        "core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl",
+        "core/java/com/android/internal/widget/ILockSettings.aidl",
+        "core/java/com/android/internal/widget/IRemoteViewsFactory.aidl",
+        "keystore/java/android/security/IKeyChainAliasCallback.aidl",
+        "keystore/java/android/security/IKeyChainService.aidl",
+        "location/java/android/location/IBatchedLocationCallback.aidl",
+        "location/java/android/location/ICountryDetector.aidl",
+        "location/java/android/location/ICountryListener.aidl",
+        "location/java/android/location/IFusedProvider.aidl",
+        "location/java/android/location/IGeocodeProvider.aidl",
+        "location/java/android/location/IGeofenceProvider.aidl",
+        "location/java/android/location/IGnssStatusListener.aidl",
+        "location/java/android/location/IGnssStatusProvider.aidl",
+        "location/java/android/location/IGnssMeasurementsListener.aidl",
+        "location/java/android/location/IGnssNavigationMessageListener.aidl",
+        "location/java/android/location/ILocationListener.aidl",
+        "location/java/android/location/ILocationManager.aidl",
+        "location/java/android/location/IFusedGeofenceHardware.aidl",
+        "location/java/android/location/IGpsGeofenceHardware.aidl",
+        "location/java/android/location/INetInitiatedListener.aidl",
+        "location/java/com/android/internal/location/ILocationProvider.aidl",
+        "media/java/android/media/IAudioService.aidl",
+        "media/java/android/media/IAudioFocusDispatcher.aidl",
+        "media/java/android/media/IAudioRoutesObserver.aidl",
+        "media/java/android/media/IMediaHTTPConnection.aidl",
+        "media/java/android/media/IMediaHTTPService.aidl",
+        "media/java/android/media/IMediaResourceMonitor.aidl",
+        "media/java/android/media/IMediaRouterClient.aidl",
+        "media/java/android/media/IMediaRouterService.aidl",
+        "media/java/android/media/IMediaScannerListener.aidl",
+        "media/java/android/media/IMediaScannerService.aidl",
+        "media/java/android/media/IPlaybackConfigDispatcher.aidl",
+        ":libaudioclient_aidl",
+        "media/java/android/media/IRecordingConfigDispatcher.aidl",
+        "media/java/android/media/IRemoteDisplayCallback.aidl",
+        "media/java/android/media/IRemoteDisplayProvider.aidl",
+        "media/java/android/media/IRemoteVolumeController.aidl",
+        "media/java/android/media/IRemoteVolumeObserver.aidl",
+        "media/java/android/media/IRingtonePlayer.aidl",
+        "media/java/android/media/IVolumeController.aidl",
+        "media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl",
+        "media/java/android/media/midi/IBluetoothMidiService.aidl",
+        "media/java/android/media/midi/IMidiDeviceListener.aidl",
+        "media/java/android/media/midi/IMidiDeviceOpenCallback.aidl",
+        "media/java/android/media/midi/IMidiDeviceServer.aidl",
+        "media/java/android/media/midi/IMidiManager.aidl",
+        "media/java/android/media/projection/IMediaProjection.aidl",
+        "media/java/android/media/projection/IMediaProjectionCallback.aidl",
+        "media/java/android/media/projection/IMediaProjectionManager.aidl",
+        "media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl",
+        "media/java/android/media/session/IActiveSessionsListener.aidl",
+        "media/java/android/media/session/ICallback.aidl",
+        "media/java/android/media/session/IOnMediaKeyListener.aidl",
+        "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
+        "media/java/android/media/session/ISession.aidl",
+        "media/java/android/media/session/ISessionCallback.aidl",
+        "media/java/android/media/session/ISessionController.aidl",
+        "media/java/android/media/session/ISessionControllerCallback.aidl",
+        "media/java/android/media/session/ISessionManager.aidl",
+        "media/java/android/media/tv/ITvInputClient.aidl",
+        "media/java/android/media/tv/ITvInputHardware.aidl",
+        "media/java/android/media/tv/ITvInputHardwareCallback.aidl",
+        "media/java/android/media/tv/ITvInputManager.aidl",
+        "media/java/android/media/tv/ITvInputManagerCallback.aidl",
+        "media/java/android/media/tv/ITvInputService.aidl",
+        "media/java/android/media/tv/ITvInputServiceCallback.aidl",
+        "media/java/android/media/tv/ITvInputSession.aidl",
+        "media/java/android/media/tv/ITvInputSessionCallback.aidl",
+        "media/java/android/media/tv/ITvRemoteProvider.aidl",
+        "media/java/android/media/tv/ITvRemoteServiceInput.aidl",
+        "media/java/android/service/media/IMediaBrowserService.aidl",
+        "media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
+        "telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
+        "telecomm/java/com/android/internal/telecom/ICallScreeningService.aidl",
+        "telecomm/java/com/android/internal/telecom/IVideoCallback.aidl",
+        "telecomm/java/com/android/internal/telecom/IVideoProvider.aidl",
+        "telecomm/java/com/android/internal/telecom/IConnectionService.aidl",
+        "telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl",
+        "telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl",
+        "telecomm/java/com/android/internal/telecom/IInCallService.aidl",
+        "telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
+        "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
+        "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
+        "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
+        "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
+        "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
+        "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
+        "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
+        "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
+        "telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl",
+        "telephony/java/com/android/ims/internal/IImsConfig.aidl",
+        "telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl",
+        "telephony/java/com/android/ims/internal/IImsEcbm.aidl",
+        "telephony/java/com/android/ims/internal/IImsEcbmListener.aidl",
+        "telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl",
+        "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/IImsRcsFeature.aidl",
+        "telephony/java/com/android/ims/internal/IImsService.aidl",
+        "telephony/java/com/android/ims/internal/IImsServiceController.aidl",
+        "telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl",
+        "telephony/java/com/android/ims/internal/IImsSmsFeature.aidl",
+        "telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl",
+        "telephony/java/com/android/ims/internal/IImsUt.aidl",
+        "telephony/java/com/android/ims/internal/IImsUtListener.aidl",
+        "telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl",
+        "telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl",
+        "telephony/java/com/android/ims/internal/ISmsListener.aidl",
+        "telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl",
+        "telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl",
+        "telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl",
+        "telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl",
+        "telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl",
+        "telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl",
+        "telephony/java/com/android/ims/ImsConfigListener.aidl",
+        "telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
+        "telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
+        "telephony/java/com/android/internal/telephony/IMms.aidl",
+        "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
+        "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
+        "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
+        "telephony/java/com/android/internal/telephony/ISms.aidl",
+        "telephony/java/com/android/internal/telephony/ISub.aidl",
+        "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/IEuiccController.aidl",
+        "wifi/java/android/net/wifi/IWifiManager.aidl",
+        "wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
+        "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
+        "wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl",
+        "wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl",
+        "wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl",
+        "wifi/java/android/net/wifi/rtt/IRttCallback.aidl",
+        "wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
+        "wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
+        "wifi/java/android/net/wifi/IWifiScanner.aidl",
+        "wifi/java/android/net/wifi/IRttManager.aidl",
+        "packages/services/PacProcessor/com/android/net/IProxyService.aidl",
+        "packages/services/Proxy/com/android/net/IProxyCallback.aidl",
+        "packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
+        "core/java/android/service/quicksettings/IQSService.aidl",
+        "core/java/android/service/quicksettings/IQSTileService.aidl",
+
+        ":libupdate_engine_aidl",
+
+        ":storaged_aidl",
+        ":netd_aidl",
+        ":vold_aidl",
+        ":installd_aidl",
+
+        "lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl",
+        "lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl",
+        "lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl",
+        "lowpan/java/android/net/lowpan/ILowpanInterface.aidl",
+        "lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl",
+        "lowpan/java/android/net/lowpan/ILowpanManager.aidl",
+
+        "core/java/android/app/admin/SecurityLogTags.logtags",
+        "core/java/android/content/EventLogTags.logtags",
+        "core/java/android/speech/tts/EventLogTags.logtags",
+        "core/java/android/net/EventLogTags.logtags",
+        "core/java/android/webkit/EventLogTags.logtags",
+        "core/java/com/android/internal/logging/EventLogTags.logtags",
+
+        ":framework-statslog-gen",
+    ],
+    logtags: [
+        "core/java/android/app/admin/SecurityLogTags.logtags",
+        "core/java/android/content/EventLogTags.logtags",
+        "core/java/android/speech/tts/EventLogTags.logtags",
+        "core/java/android/net/EventLogTags.logtags",
+        "core/java/android/webkit/EventLogTags.logtags",
+        "core/java/com/android/internal/logging/EventLogTags.logtags",
+    ],
+
+    aidl: {
+        local_include_dirs: [
+            // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
+            "core/java",
+            "graphics/java",
+            "location/java",
+            "lowpan/java",
+            "media/java",
+            "media/mca/effect/java",
+            "media/mca/filterfw/java",
+            "media/mca/filterpacks/java",
+            "drm/java",
+            "opengl/java",
+            "sax/java",
+            "telecomm/java",
+            "telephony/java",
+            "wifi/java",
+            "keystore/java",
+            "rs/java",
+        ],
+
+        include_dirs: [
+            "system/update_engine/binder_bindings",
+            "frameworks/native/aidl/binder",
+            "frameworks/av/camera/aidl",
+            "frameworks/av/media/libaudioclient/aidl",
+            "frameworks/native/aidl/gui",
+            "system/core/storaged/binder",
+            "system/netd/server/binder",
+            "system/vold/binder",
+            "system/bt/binder",
+        ],
+    },
+
+    no_framework_libs: true,
+    libs: [
+        "conscrypt",
+        "okhttp",
+        "bouncycastle",
+        "ext",
+    ],
+
+    static_libs: [
+        "framework-protos",
+        "android.hidl.base-V1.0-java",
+        "android.hardware.cas-V1.0-java",
+        "android.hardware.contexthub-V1.0-java",
+        "android.hardware.health-V1.0-java-constants",
+        "android.hardware.thermal-V1.0-java-constants",
+        "android.hardware.tv.input-V1.0-java-constants",
+        "android.hardware.usb-V1.0-java-constants",
+        "android.hardware.usb-V1.1-java-constants",
+        "android.hardware.vibrator-V1.0-java-constants",
+        "android.hardware.vibrator-V1.1-java-constants",
+        "android.hardware.wifi-V1.0-java-constants",
+    ],
+
+    // Loaded with System.loadLibrary by android.view.textclassifier
+    required: ["libtextclassifier"],
+
+    javac_shard_size: 150,
+
+    dxflags: [
+        "--core-library",
+        "--multi-dex",
+    ],
+}
+
+genrule {
+    name: "framework-statslog-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out)",
+    out: ["android/util/StatsLog.java"],
+}
+
+gensrcs {
+    name: "framework-javastream-protos",
+    depfile: true,
+
+    tools: [
+        "aprotoc",
+        "protoc-gen-javastream",
+        "soong_zip",
+    ],
+
+    cmd: "mkdir -p $(genDir)/$(in) " +
+        "&& $(location aprotoc) " +
+        "  --plugin=$(location protoc-gen-javastream) " +
+        "  --dependency_out=$(depfile) " +
+        "  --javastream_out=$(genDir)/$(in) " +
+        "  -Iexternal/protobuf/src " +
+        "  -I . " +
+        "  $(in) " +
+        "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+
+    srcs: ["core/proto/**/*.proto"],
+    output_extension: "srcjar",
+}
+
 // Build ext.jar
 // ============================================================
 java_library {
@@ -65,6 +708,7 @@
                 "core/proto/android/os/procrank.proto",
                 "core/proto/android/os/system_properties.proto",
                 "core/proto/android/service/graphicsstats.proto",
+                "libs/incident/proto/android/privacy.proto",
                 "tools/streaming_proto/stream.proto",
             ],
             shared: {
diff --git a/Android.mk b/Android.mk
index a69170f..ce504a5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,16 +15,8 @@
 #
 LOCAL_PATH := $(call my-dir)
 
-# We have a special case here where we build the library's resources
-# independently from its code, so we need to find where the resource
-# class source got placed in the course of building the resources.
-# Thus, the magic here.
-# Also, this module cannot depend directly on the R.java file; if it
-# did, the PRIVATE_* vars for R.java wouldn't be guaranteed to be correct.
-# Instead, it depends on the R.stamp file, which lists the corresponding
-# R.java file as a prerequisite.
-# TODO: find a more appropriate way to do this.
-framework_res_source_path := APPS/framework-res_intermediates/src
+# Load framework-specific path mappings used later in the build.
+include $(LOCAL_PATH)/pathmap.mk
 
 # Build the master framework library.
 # The framework contains too many method references (>64K) for poor old DEX.
@@ -35,651 +27,6 @@
 # embedded builds use nothing in frameworks/base
 ifneq ($(ANDROID_BUILD_EMBEDDED),true)
 
-include $(CLEAR_VARS)
-
-# Load framework-specific path mappings used later in the build.
-include $(LOCAL_PATH)/pathmap.mk
-
-# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
-LOCAL_SRC_FILES := \
-        $(call find-other-java-files,$(FRAMEWORKS_BASE_SUBDIRS)) \
-        $(call all-proto-files-under, core/proto)
-
-# EventLogTags files.
-LOCAL_SRC_FILES += \
-       core/java/android/app/admin/SecurityLogTags.logtags \
-       core/java/android/content/EventLogTags.logtags \
-       core/java/android/speech/tts/EventLogTags.logtags \
-       core/java/android/net/EventLogTags.logtags \
-       core/java/android/webkit/EventLogTags.logtags \
-       core/java/com/android/internal/logging/EventLogTags.logtags \
-
-## READ ME: ########################################################
-##
-## When updating this list of aidl files, consider if that aidl is
-## part of the SDK API.  If it is, also add it to the list below that
-## is preprocessed and distributed with the SDK.  This list should
-## not contain any aidl files for parcelables, but the one below should
-## if you intend for 3rd parties to be able to send those objects
-## across process boundaries.
-##
-## READ ME: ########################################################
-LOCAL_SRC_FILES += \
-	core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
-	core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
-	core/java/android/accounts/IAccountManager.aidl \
-	core/java/android/accounts/IAccountManagerResponse.aidl \
-	core/java/android/accounts/IAccountAuthenticator.aidl \
-	core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
-	core/java/android/app/IActivityController.aidl \
-	core/java/android/app/IActivityManager.aidl \
-	core/java/android/app/IActivityPendingResult.aidl \
-	core/java/android/app/IAlarmCompleteListener.aidl \
-	core/java/android/app/IAlarmListener.aidl \
-	core/java/android/app/IAlarmManager.aidl \
-	core/java/android/app/IAppTask.aidl \
-	core/java/android/app/IApplicationThread.aidl \
-	core/java/android/app/IAssistDataReceiver.aidl \
-	core/java/android/app/ITaskStackListener.aidl \
-	core/java/android/app/IBackupAgent.aidl \
-	core/java/android/app/IEphemeralResolver.aidl \
-	core/java/android/app/IInputForwarder.aidl \
-	core/java/android/app/IInstantAppResolver.aidl \
-	core/java/android/app/IInstrumentationWatcher.aidl \
-	core/java/android/app/INotificationManager.aidl \
-	core/java/android/app/IProcessObserver.aidl \
-	core/java/android/app/ISearchManager.aidl \
-	core/java/android/app/ISearchManagerCallback.aidl \
-	core/java/android/app/IServiceConnection.aidl \
-	core/java/android/app/IStopUserCallback.aidl \
-	core/java/android/app/job/IJobCallback.aidl \
-	core/java/android/app/job/IJobScheduler.aidl \
-	core/java/android/app/job/IJobService.aidl \
-	core/java/android/app/ITransientNotification.aidl \
-	core/java/android/app/IUidObserver.aidl \
-	core/java/android/app/IUiAutomationConnection.aidl \
-	core/java/android/app/IUiModeManager.aidl \
-	core/java/android/app/IUserSwitchObserver.aidl \
-	core/java/android/app/IWallpaperManager.aidl \
-	core/java/android/app/IWallpaperManagerCallback.aidl \
-	core/java/android/app/admin/IDeviceAdminService.aidl \
-	core/java/android/app/admin/IDevicePolicyManager.aidl \
-	core/java/android/app/trust/IStrongAuthTracker.aidl \
-	core/java/android/app/trust/ITrustManager.aidl \
-	core/java/android/app/trust/ITrustListener.aidl \
-	core/java/android/app/backup/IBackupManager.aidl \
-	core/java/android/app/backup/IBackupObserver.aidl \
-	core/java/android/app/backup/IBackupManagerMonitor.aidl \
-	core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
-	core/java/android/app/backup/IRestoreObserver.aidl \
-	core/java/android/app/backup/IRestoreSession.aidl \
-	core/java/android/app/backup/ISelectBackupTransportCallback.aidl \
-	core/java/android/app/timezone/ICallback.aidl \
-	core/java/android/app/timezone/IRulesManager.aidl \
-	core/java/android/app/usage/ICacheQuotaService.aidl \
-	core/java/android/app/usage/IStorageStatsManager.aidl \
-	core/java/android/app/usage/IUsageStatsManager.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetooth.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothA2dp.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothA2dpSink.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothAvrcpController.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothCallback.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothProfileServiceConnection.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHeadset.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHeadsetPhone.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHealth.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHealthCallback.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHidHost.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothPan.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothManager.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothManagerCallback.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothMap.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothMapClient.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothPbap.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothPbapClient.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothSocketManager.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothHidDeviceCallback.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothGatt.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothGattCallback.aidl \
-	../../system/bt/binder/android/bluetooth/IBluetoothGattServerCallback.aidl \
-	../../system/bt/binder/android/bluetooth/le/IAdvertisingSetCallback.aidl \
-	../../system/bt/binder/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl \
-	../../system/bt/binder/android/bluetooth/le/IScannerCallback.aidl \
-	core/java/android/content/IClipboard.aidl \
-	core/java/android/content/IContentService.aidl \
-	core/java/android/content/IIntentReceiver.aidl \
-	core/java/android/content/IIntentSender.aidl \
-	core/java/android/content/IOnPrimaryClipChangedListener.aidl \
-	core/java/android/content/IRestrictionsManager.aidl \
-	core/java/android/content/ISyncAdapter.aidl \
-	core/java/android/content/ISyncContext.aidl \
-	core/java/android/content/ISyncServiceAdapter.aidl \
-	core/java/android/content/ISyncStatusObserver.aidl \
-	core/java/android/content/om/IOverlayManager.aidl \
-	core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl \
-	core/java/android/content/pm/IDexModuleRegisterCallback.aidl \
-	core/java/android/content/pm/ILauncherApps.aidl \
-	core/java/android/content/pm/IOnAppsChangedListener.aidl \
-	core/java/android/content/pm/IOnPermissionsChangeListener.aidl \
-	core/java/android/content/pm/IOtaDexopt.aidl \
-	core/java/android/content/pm/IPackageDataObserver.aidl \
-	core/java/android/content/pm/IPackageDeleteObserver.aidl \
-	core/java/android/content/pm/IPackageDeleteObserver2.aidl \
-	core/java/android/content/pm/IPackageInstallObserver2.aidl \
-	core/java/android/content/pm/IPackageInstaller.aidl \
-	core/java/android/content/pm/IPackageInstallerCallback.aidl \
-	core/java/android/content/pm/IPackageInstallerSession.aidl \
-	core/java/android/content/pm/IPackageManager.aidl \
-	../native/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl \
-	core/java/android/content/pm/IPackageMoveObserver.aidl \
-	core/java/android/content/pm/IPackageStatsObserver.aidl \
-	core/java/android/content/pm/IPinItemRequest.aidl \
-	core/java/android/content/pm/IShortcutService.aidl \
-	core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl \
-	core/java/android/database/IContentObserver.aidl \
-	../av/camera/aidl/android/hardware/ICameraService.aidl \
-	../av/camera/aidl/android/hardware/ICameraServiceListener.aidl \
-	../av/camera/aidl/android/hardware/ICameraServiceProxy.aidl \
-	../av/camera/aidl/android/hardware/ICamera.aidl \
-	../av/camera/aidl/android/hardware/ICameraClient.aidl \
-	../av/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl \
-	../av/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl \
-	core/java/android/hardware/IConsumerIrService.aidl \
-	core/java/android/hardware/ISerialManager.aidl \
-	core/java/android/hardware/display/IDisplayManager.aidl \
-	core/java/android/hardware/display/IDisplayManagerCallback.aidl \
-	core/java/android/hardware/display/IVirtualDisplayCallback.aidl \
-	core/java/android/hardware/fingerprint/IFingerprintService.aidl \
-	core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl \
-	core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl \
-	core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl \
-	core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \
-	core/java/android/hardware/hdmi/IHdmiControlService.aidl \
-	core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \
-	core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \
-	core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl \
-	core/java/android/hardware/hdmi/IHdmiMhlVendorCommandListener.aidl \
-	core/java/android/hardware/hdmi/IHdmiRecordListener.aidl \
-	core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl \
-	core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \
-	core/java/android/hardware/input/IInputManager.aidl \
-	core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
-	core/java/android/hardware/input/ITabletModeChangedListener.aidl \
-	core/java/android/hardware/location/IActivityRecognitionHardware.aidl \
-	core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl \
-	core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \
-	core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl \
-	core/java/android/hardware/location/IFusedLocationHardware.aidl \
-	core/java/android/hardware/location/IFusedLocationHardwareSink.aidl \
-	core/java/android/hardware/location/IGeofenceHardware.aidl \
-	core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
-	core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
-	core/java/android/hardware/location/IContextHubCallback.aidl \
-	core/java/android/hardware/location/IContextHubClient.aidl \
-	core/java/android/hardware/location/IContextHubClientCallback.aidl \
-	core/java/android/hardware/location/IContextHubService.aidl \
-	core/java/android/hardware/location/IContextHubTransactionCallback.aidl \
-	core/java/android/hardware/radio/IRadioService.aidl \
-	core/java/android/hardware/radio/ITuner.aidl \
-	core/java/android/hardware/radio/ITunerCallback.aidl \
-	core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl \
-	core/java/android/hardware/usb/IUsbManager.aidl \
-	core/java/android/net/ICaptivePortal.aidl \
-	core/java/android/net/IConnectivityManager.aidl \
-	core/java/android/net/IIpConnectivityMetrics.aidl \
-	core/java/android/net/IEthernetManager.aidl \
-	core/java/android/net/IEthernetServiceListener.aidl \
-	core/java/android/net/INetdEventCallback.aidl \
-	core/java/android/net/IIpSecService.aidl \
-	core/java/android/net/INetworkManagementEventObserver.aidl \
-	core/java/android/net/INetworkPolicyListener.aidl \
-	core/java/android/net/INetworkPolicyManager.aidl \
-	core/java/android/net/INetworkRecommendationProvider.aidl \
-	core/java/android/net/INetworkScoreCache.aidl \
-	core/java/android/net/INetworkScoreService.aidl \
-	core/java/android/net/INetworkStatsService.aidl \
-	core/java/android/net/INetworkStatsSession.aidl \
-	core/java/android/net/ITetheringStatsProvider.aidl \
-	core/java/android/net/nsd/INsdManager.aidl \
-	core/java/android/nfc/IAppCallback.aidl \
-	core/java/android/nfc/INfcAdapter.aidl \
-	core/java/android/nfc/INfcAdapterExtras.aidl \
-	core/java/android/nfc/INfcTag.aidl \
-	core/java/android/nfc/INfcCardEmulation.aidl \
-	core/java/android/nfc/INfcFCardEmulation.aidl \
-	core/java/android/nfc/INfcUnlockHandler.aidl \
-	core/java/android/nfc/INfcDta.aidl \
-	core/java/android/nfc/ITagRemovedCallback.aidl \
-	core/java/android/os/IBatteryPropertiesListener.aidl \
-	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
-	core/java/android/os/ICancellationSignal.aidl \
-	core/java/android/os/IDeviceIdentifiersPolicyService.aidl \
-	core/java/android/os/IDeviceIdleController.aidl \
-	core/java/android/os/IHardwarePropertiesManager.aidl \
-	core/java/android/os/IIncidentManager.aidl \
-	core/java/android/os/IIncidentReportCompletedListener.aidl \
-	core/java/android/os/IIncidentReportStatusListener.aidl \
-	core/java/android/os/IMaintenanceActivityListener.aidl \
-	core/java/android/os/IMessenger.aidl \
-	core/java/android/os/INetworkActivityListener.aidl \
-	core/java/android/os/INetworkManagementService.aidl \
-	core/java/android/os/IPermissionController.aidl \
-	core/java/android/os/IProcessInfoService.aidl \
-	core/java/android/os/IProgressListener.aidl \
-	core/java/android/os/IPowerManager.aidl \
-	core/java/android/os/IRecoverySystem.aidl \
-	core/java/android/os/IRecoverySystemProgressListener.aidl \
-	core/java/android/os/IRemoteCallback.aidl \
-	core/java/android/os/ISchedulingPolicyService.aidl \
-	core/java/android/os/IStatsCompanionService.aidl \
-	core/java/android/os/IStatsManager.aidl \
-	core/java/android/os/IThermalEventListener.aidl \
-	core/java/android/os/IThermalService.aidl \
-	core/java/android/os/IUpdateLock.aidl \
-	core/java/android/os/IUserManager.aidl \
-	core/java/android/os/IVibratorService.aidl \
-	core/java/android/os/storage/IStorageManager.aidl \
-	core/java/android/os/storage/IStorageEventListener.aidl \
-	core/java/android/os/storage/IStorageShutdownObserver.aidl \
-	core/java/android/os/storage/IObbActionListener.aidl \
-	core/java/android/security/IKeystoreService.aidl \
-	core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \
-	core/java/android/service/autofill/IAutoFillService.aidl \
-	core/java/android/service/autofill/IFillCallback.aidl \
-	core/java/android/service/autofill/ISaveCallback.aidl \
-	core/java/android/service/carrier/ICarrierService.aidl \
-	core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
-	core/java/android/service/carrier/ICarrierMessagingService.aidl \
-	core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl \
-	core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl \
-	core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl \
-	core/java/android/service/euicc/IEuiccService.aidl \
-	core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl \
-	core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl \
-	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/IRetainSubscriptionsForFactoryResetCallback.aidl \
-	core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl \
-	core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl \
-	core/java/android/service/gatekeeper/IGateKeeperService.aidl \
-	core/java/android/service/notification/INotificationListener.aidl \
-	core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
-	core/java/android/service/notification/IConditionListener.aidl \
-	core/java/android/service/notification/IConditionProvider.aidl \
-	core/java/android/service/settings/suggestions/ISuggestionService.aidl \
-	core/java/android/service/vr/IPersistentVrStateCallbacks.aidl \
-	core/java/android/service/vr/IVrListener.aidl \
-	core/java/android/service/vr/IVrManager.aidl \
-	core/java/android/service/vr/IVrStateCallbacks.aidl \
-	core/java/android/print/ILayoutResultCallback.aidl \
-	core/java/android/print/IPrinterDiscoveryObserver.aidl \
-	core/java/android/print/IPrintDocumentAdapter.aidl \
-	core/java/android/print/IPrintDocumentAdapterObserver.aidl \
-	core/java/android/print/IPrintJobStateChangeListener.aidl \
-	core/java/android/print/IPrintServicesChangeListener.aidl \
-	core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl \
-	core/java/android/print/IPrintManager.aidl \
-	core/java/android/print/IPrintSpooler.aidl \
-	core/java/android/print/IPrintSpoolerCallbacks.aidl \
-	core/java/android/print/IPrintSpoolerClient.aidl \
-	core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl \
-	core/java/android/printservice/recommendation/IRecommendationService.aidl \
-	core/java/android/print/IWriteResultCallback.aidl \
-	core/java/android/printservice/IPrintService.aidl \
-	core/java/android/printservice/IPrintServiceClient.aidl \
-	core/java/android/companion/ICompanionDeviceManager.aidl \
-	core/java/android/companion/ICompanionDeviceDiscoveryService.aidl \
-	core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl \
-	core/java/android/companion/IFindDeviceCallback.aidl \
-	core/java/android/service/dreams/IDreamManager.aidl \
-	core/java/android/service/dreams/IDreamService.aidl \
-	core/java/android/service/oemlock/IOemLockService.aidl \
-	core/java/android/service/persistentdata/IPersistentDataBlockService.aidl \
-	core/java/android/service/trust/ITrustAgentService.aidl \
-	core/java/android/service/trust/ITrustAgentServiceCallback.aidl \
-	core/java/android/service/voice/IVoiceInteractionService.aidl \
-	core/java/android/service/voice/IVoiceInteractionSession.aidl \
-	core/java/android/service/voice/IVoiceInteractionSessionService.aidl \
-	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
-	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
-	core/java/android/service/wallpaper/IWallpaperService.aidl \
-	core/java/android/service/chooser/IChooserTargetService.aidl \
-	core/java/android/service/chooser/IChooserTargetResult.aidl \
-	core/java/android/service/resolver/IResolverRankerService.aidl \
-	core/java/android/service/resolver/IResolverRankerResult.aidl \
-	core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
-	core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
-	core/java/android/view/accessibility/IAccessibilityManager.aidl \
-	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
-	core/java/android/view/autofill/IAutoFillManager.aidl \
-	core/java/android/view/autofill/IAutoFillManagerClient.aidl \
-	core/java/android/view/autofill/IAutofillWindowPresenter.aidl \
-	core/java/android/view/IApplicationToken.aidl \
-	core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
-	core/java/android/view/IDockedStackListener.aidl \
-	core/java/android/view/IGraphicsStats.aidl \
-	core/java/android/view/IGraphicsStatsCallback.aidl \
-	core/java/android/view/IInputFilter.aidl \
-	core/java/android/view/IInputFilterHost.aidl \
-	core/java/android/view/IOnKeyguardExitResult.aidl \
-	core/java/android/view/IPinnedStackController.aidl \
-	core/java/android/view/IPinnedStackListener.aidl \
-	core/java/android/view/IRotationWatcher.aidl \
-	core/java/android/view/IWallpaperVisibilityListener.aidl \
-	core/java/android/view/IWindow.aidl \
-	core/java/android/view/IWindowFocusObserver.aidl \
-	core/java/android/view/IWindowId.aidl \
-	core/java/android/view/IWindowManager.aidl \
-	core/java/android/view/IWindowSession.aidl \
-	core/java/android/view/IWindowSessionCallback.aidl \
-	core/java/android/webkit/IWebViewUpdateService.aidl \
-	core/java/android/speech/IRecognitionListener.aidl \
-	core/java/android/speech/IRecognitionService.aidl \
-	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
-	core/java/android/speech/tts/ITextToSpeechService.aidl \
-	core/java/com/android/internal/app/IAppOpsCallback.aidl \
-	core/java/com/android/internal/app/IAppOpsService.aidl \
-	core/java/com/android/internal/app/IBatteryStats.aidl \
-	core/java/com/android/internal/app/ISoundTriggerService.aidl \
-	core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
-	core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl \
-	core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \
-	core/java/com/android/internal/app/IVoiceInteractor.aidl \
-	core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \
-	core/java/com/android/internal/app/IVoiceInteractorRequest.aidl \
-	core/java/com/android/internal/app/IMediaContainerService.aidl \
-	core/java/com/android/internal/app/procstats/IProcessStats.aidl \
-	core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
-	core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
-	core/java/com/android/internal/backup/IBackupTransport.aidl \
-	core/java/com/android/internal/backup/IObbBackupService.aidl \
-	core/java/com/android/internal/car/ICarServiceHelper.aidl \
-	core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \
-	core/java/com/android/internal/net/INetworkWatchlistManager.aidl \
-	core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \
-	core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl \
-	core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
-	core/java/com/android/internal/policy/IKeyguardService.aidl \
-	core/java/com/android/internal/policy/IKeyguardStateCallback.aidl \
-	core/java/com/android/internal/policy/IShortcutService.aidl \
-	core/java/com/android/internal/os/IDropBoxManagerService.aidl \
-	core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl \
-	core/java/com/android/internal/os/IResultReceiver.aidl \
-	core/java/com/android/internal/os/IShellCallback.aidl \
-	core/java/com/android/internal/statusbar/IStatusBar.aidl \
-	core/java/com/android/internal/statusbar/IStatusBarService.aidl \
-	core/java/com/android/internal/textservice/ISpellCheckerService.aidl \
-	core/java/com/android/internal/textservice/ISpellCheckerServiceCallback.aidl \
-	core/java/com/android/internal/textservice/ISpellCheckerSession.aidl \
-	core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
-	core/java/com/android/internal/textservice/ITextServicesManager.aidl \
-	core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
-	core/java/com/android/internal/view/IDragAndDropPermissions.aidl \
-	core/java/com/android/internal/view/IInputContext.aidl \
-	core/java/com/android/internal/view/IInputContextCallback.aidl \
-	core/java/com/android/internal/view/IInputMethod.aidl \
-	core/java/com/android/internal/view/IInputMethodClient.aidl \
-	core/java/com/android/internal/view/IInputMethodManager.aidl \
-	core/java/com/android/internal/view/IInputMethodSession.aidl \
-	core/java/com/android/internal/view/IInputSessionCallback.aidl \
-	core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl \
-	core/java/com/android/internal/widget/ILockSettings.aidl \
-	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
-	keystore/java/android/security/IKeyChainAliasCallback.aidl \
-	keystore/java/android/security/IKeyChainService.aidl \
-	location/java/android/location/IBatchedLocationCallback.aidl \
-	location/java/android/location/ICountryDetector.aidl \
-	location/java/android/location/ICountryListener.aidl \
-	location/java/android/location/IFusedProvider.aidl \
-	location/java/android/location/IGeocodeProvider.aidl \
-	location/java/android/location/IGeofenceProvider.aidl \
-	location/java/android/location/IGnssStatusListener.aidl \
-	location/java/android/location/IGnssStatusProvider.aidl \
-	location/java/android/location/IGnssMeasurementsListener.aidl \
-	location/java/android/location/IGnssNavigationMessageListener.aidl \
-	location/java/android/location/ILocationListener.aidl \
-	location/java/android/location/ILocationManager.aidl \
-	location/java/android/location/IFusedGeofenceHardware.aidl \
-	location/java/android/location/IGpsGeofenceHardware.aidl \
-	location/java/android/location/INetInitiatedListener.aidl \
-	location/java/com/android/internal/location/ILocationProvider.aidl \
-	media/java/android/media/IAudioService.aidl \
-	media/java/android/media/IAudioFocusDispatcher.aidl \
-	media/java/android/media/IAudioRoutesObserver.aidl \
-	media/java/android/media/IMediaHTTPConnection.aidl \
-	media/java/android/media/IMediaHTTPService.aidl \
-	media/java/android/media/IMediaResourceMonitor.aidl \
-	media/java/android/media/IMediaRouterClient.aidl \
-	media/java/android/media/IMediaRouterService.aidl \
-	media/java/android/media/IMediaScannerListener.aidl \
-	media/java/android/media/IMediaScannerService.aidl \
-	media/java/android/media/IPlaybackConfigDispatcher.aidl \
-	../av/media/libaudioclient/aidl/android/media/IPlayer.aidl \
-	media/java/android/media/IRecordingConfigDispatcher.aidl \
-	media/java/android/media/IRemoteDisplayCallback.aidl \
-	media/java/android/media/IRemoteDisplayProvider.aidl \
-	media/java/android/media/IRemoteVolumeController.aidl \
-	media/java/android/media/IRemoteVolumeObserver.aidl \
-	media/java/android/media/IRingtonePlayer.aidl \
-	media/java/android/media/IVolumeController.aidl \
-	media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \
-	media/java/android/media/midi/IBluetoothMidiService.aidl \
-	media/java/android/media/midi/IMidiDeviceListener.aidl \
-	media/java/android/media/midi/IMidiDeviceOpenCallback.aidl \
-	media/java/android/media/midi/IMidiDeviceServer.aidl \
-	media/java/android/media/midi/IMidiManager.aidl \
-	media/java/android/media/projection/IMediaProjection.aidl \
-	media/java/android/media/projection/IMediaProjectionCallback.aidl \
-	media/java/android/media/projection/IMediaProjectionManager.aidl \
-	media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \
-	media/java/android/media/session/IActiveSessionsListener.aidl \
-	media/java/android/media/session/ICallback.aidl \
-	media/java/android/media/session/IOnMediaKeyListener.aidl \
-	media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl \
-	media/java/android/media/session/ISession.aidl \
-	media/java/android/media/session/ISessionCallback.aidl \
-	media/java/android/media/session/ISessionController.aidl \
-	media/java/android/media/session/ISessionControllerCallback.aidl \
-	media/java/android/media/session/ISessionManager.aidl \
-	media/java/android/media/tv/ITvInputClient.aidl \
-	media/java/android/media/tv/ITvInputHardware.aidl \
-	media/java/android/media/tv/ITvInputHardwareCallback.aidl \
-	media/java/android/media/tv/ITvInputManager.aidl \
-	media/java/android/media/tv/ITvInputManagerCallback.aidl \
-	media/java/android/media/tv/ITvInputService.aidl \
-	media/java/android/media/tv/ITvInputServiceCallback.aidl \
-	media/java/android/media/tv/ITvInputSession.aidl \
-	media/java/android/media/tv/ITvInputSessionCallback.aidl \
-	media/java/android/media/tv/ITvRemoteProvider.aidl \
-	media/java/android/media/tv/ITvRemoteServiceInput.aidl \
-	media/java/android/service/media/IMediaBrowserService.aidl \
-	media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl \
-	telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl \
-	telecomm/java/com/android/internal/telecom/ICallScreeningService.aidl \
-	telecomm/java/com/android/internal/telecom/IVideoCallback.aidl \
-	telecomm/java/com/android/internal/telecom/IVideoProvider.aidl \
-	telecomm/java/com/android/internal/telecom/IConnectionService.aidl \
-	telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl \
-	telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl \
-	telecomm/java/com/android/internal/telecom/IInCallService.aidl \
-	telecomm/java/com/android/internal/telecom/ITelecomService.aidl \
-	telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl \
-	telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl \
-	telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl \
-	telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl \
-        telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl \
-	telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl \
-	telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl \
-	telephony/java/com/android/ims/internal/IImsCallSession.aidl \
-	telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl \
-	telephony/java/com/android/ims/internal/IImsConfig.aidl \
-	telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl \
-	telephony/java/com/android/ims/internal/IImsEcbm.aidl \
-	telephony/java/com/android/ims/internal/IImsEcbmListener.aidl \
-        telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl \
-        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/IImsRcsFeature.aidl \
-	telephony/java/com/android/ims/internal/IImsService.aidl \
-	telephony/java/com/android/ims/internal/IImsServiceController.aidl \
-	telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl \
-        telephony/java/com/android/ims/internal/IImsSmsFeature.aidl \
-	telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl \
-	telephony/java/com/android/ims/internal/IImsUt.aidl \
-	telephony/java/com/android/ims/internal/IImsUtListener.aidl \
-	telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl \
-	telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl \
-        telephony/java/com/android/ims/internal/ISmsListener.aidl \
-        telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl \
-        telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl \
-        telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl \
-        telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl \
-        telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl \
-        telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl \
-	telephony/java/com/android/ims/ImsConfigListener.aidl \
-	telephony/java/com/android/internal/telephony/IApnSourceService.aidl \
-	telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl \
-	telephony/java/com/android/internal/telephony/IMms.aidl \
-	telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl \
-	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
-	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
-	telephony/java/com/android/internal/telephony/ISms.aidl \
-	telephony/java/com/android/internal/telephony/ISub.aidl \
-	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/IEuiccController.aidl \
-	wifi/java/android/net/wifi/IWifiManager.aidl \
-	wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
-	wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \
-	wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl \
-	wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
-	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
-	wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
-	wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
-	wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl \
-	wifi/java/android/net/wifi/IWifiScanner.aidl \
-	wifi/java/android/net/wifi/IRttManager.aidl \
-	packages/services/PacProcessor/com/android/net/IProxyService.aidl \
-	packages/services/Proxy/com/android/net/IProxyCallback.aidl \
-	packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
-	core/java/android/service/quicksettings/IQSService.aidl \
-	core/java/android/service/quicksettings/IQSTileService.aidl \
-
-# The following are native binders that need to go with the native component
-# at system/update_engine/binder_bindings/. Use relative path to refer to them.
-LOCAL_SRC_FILES += \
-	../../system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl \
-	../../system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl \
-
-LOCAL_SRC_FILES += \
-	../../system/core/storaged/binder/android/os/IStoraged.aidl \
-	../../system/netd/server/binder/android/net/INetd.aidl \
-	../../system/vold/binder/android/os/IVold.aidl \
-	../../system/vold/binder/android/os/IVoldListener.aidl \
-	../../system/vold/binder/android/os/IVoldTaskListener.aidl \
-	../native/cmds/installd/binder/android/os/IInstalld.aidl \
-
-LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
-
-LOCAL_AIDL_INCLUDES += core/java/android/os/StatsLogEventWrapper.aidl
-
-LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java
-LOCAL_SRC_FILES += \
-	lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl \
-	lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl \
-	lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl \
-	lowpan/java/android/net/lowpan/ILowpanInterface.aidl \
-	lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl \
-	lowpan/java/android/net/lowpan/ILowpanManager.aidl
-
-# StatsLog generated functions
-statslog_src_dir := $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)/statslog
-gen := $(statslog_src_dir)/android/util/StatsLog.java
-$(gen): PRIVATE_PATH := $(LOCAL_PATH)
-$(gen): PRIVATE_CUSTOM_TOOL = $(HOST_OUT_EXECUTABLES)/stats-log-api-gen --java $@
-$(gen): $(HOST_OUT_EXECUTABLES)/stats-log-api-gen
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(gen)
-statslog_src_dir:=
-gen:=
-
-# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
-LOCAL_AIDL_INCLUDES += \
-      $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
-      frameworks/native/aidl/binder
-
-LOCAL_AIDL_INCLUDES += \
-	frameworks/av/camera/aidl \
-	frameworks/av/drm/libmediadrm/aidl \
-	frameworks/av/media/libaudioclient/aidl \
-	frameworks/native/aidl/gui \
-	system/core/storaged/binder \
-	system/netd/server/binder \
-	system/vold/binder \
-	system/bt/binder
-
-LOCAL_INTERMEDIATE_SOURCES := \
-			$(framework_res_source_path)/android/R.java \
-			$(framework_res_source_path)/android/Manifest.java \
-			$(framework_res_source_path)/com/android/internal/R.java
-
-# Make sure that R.java and Manifest.java are built before we build
-# the source for this library.
-framework_res_R_stamp := \
-	$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
-LOCAL_ADDITIONAL_DEPENDENCIES := $(framework_res_R_stamp)
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp bouncycastle ext
-
-LOCAL_STATIC_JAVA_LIBRARIES :=                           \
-    framework-protos                                     \
-    android.hidl.base-V1.0-java                          \
-    android.hardware.cas-V1.0-java                       \
-    android.hardware.contexthub-V1.0-java                \
-    android.hardware.health-V1.0-java-constants          \
-    android.hardware.thermal-V1.0-java-constants         \
-    android.hardware.tv.input-V1.0-java-constants        \
-    android.hardware.usb-V1.0-java-constants             \
-    android.hardware.usb-V1.1-java-constants             \
-    android.hardware.vibrator-V1.0-java-constants        \
-    android.hardware.vibrator-V1.1-java-constants        \
-    android.hardware.wifi-V1.0-java-constants            \
-
-# Loaded with System.loadLibrary by android.view.textclassifier
-LOCAL_REQUIRED_MODULES += libtextclassifier
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := stream
-LOCAL_PROTOC_FLAGS := \
-    -Iexternal/protobuf/src
-
-LOCAL_MODULE := framework
-
-LOCAL_JAVAC_SHARD_SIZE := 150
-
-LOCAL_DX_FLAGS := --core-library --multi-dex
-LOCAL_JACK_FLAGS := --multi-dex native
-
-LOCAL_RMTYPEDEFS := true
-
-ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true)
-LOCAL_EMMA_INSTRUMENT := true
-endif
-
-include $(BUILD_JAVA_LIBRARY)
-
-framework_module := $(LOCAL_INSTALLED_MODULE)
-$(framework_module): | $(dir $(framework_module))framework-res.apk
-
-framework_built := $(call java-lib-deps,framework)
-
 # Copy AIDL files to be preprocessed and included in the SDK,
 # specified relative to the root of the build tree.
 # ============================================================
@@ -910,85 +257,78 @@
 
 # TODO: deal with com/google/android/googleapps
 packages_to_document := \
-	android \
-	javax/microedition/khronos \
-	org/apache/http/conn \
-	org/apache/http/params
-
-
-# Search through the base framework dirs for these packages.
-# The result will be relative to frameworks/base.
-fwbase_dirs_to_document := \
-	test-base/src \
-	$(patsubst $(LOCAL_PATH)/%,%, \
-	  $(wildcard \
-	    $(foreach dir, $(FRAMEWORKS_BASE_JAVA_SRC_DIRS), \
-	      $(addprefix $(dir)/, $(packages_to_document)) \
-	     ) \
-	   ) \
-	 )
+  android \
+  javax/microedition/khronos \
+  org/apache/http/conn \
+  org/apache/http/params \
 
 # include definition of libcore_to_document
 include libcore/Docs.mk
 
 non_base_dirs := \
-	../opt/telephony/src/java/android/telephony \
-	../opt/telephony/src/java/android/telephony/gsm \
-	../opt/net/voip/src/java/android/net/rtp \
-	../opt/net/voip/src/java/android/net/sip
+  ../opt/telephony/src/java/android/telephony \
+  ../opt/telephony/src/java/android/telephony/gsm \
+  ../opt/net/voip/src/java/android/net/rtp \
+  ../opt/net/voip/src/java/android/net/sip \
 
 framework_base_android_test_mock_src_files := \
-	$(call all-java-files-under, test-mock/src/android/test/mock)
+  $(call all-java-files-under, test-mock/src/android/test/mock)
 
 framework_base_android_test_runner_excluding_mock_src_files := \
-	$(filter-out $(framework_base_android_test_mock_src_files), $(call all-java-files-under, test-runner/src))
+  $(filter-out $(framework_base_android_test_mock_src_files), $(call all-java-files-under, test-runner/src))
 
-# These are relative to frameworks/base
-dirs_to_check_apis := \
-  $(fwbase_dirs_to_document) \
-	$(non_base_dirs)
+# Find all files in specific directories (relative to frameworks/base)
+# to document and check apis
+files_to_check_apis := \
+  $(call find-other-java-files, \
+    test-base/src \
+    $(non_base_dirs) \
+  )
 
-###########################################################
-## Return all directories that have a 'NO_DOCS' file in
-## them, appending a '%' to them to form a pattern to
-## filter out files under those directories.
-## $(1): A list of base directories to look at.
-###########################################################
-define find-no-docs-pattern
-$(addsuffix %, $(dir $(foreach dir, $(1), $(shell cd $(LOCAL_PATH); find $(dir) -name NO_DOCS))))
-endef
+# Find all files in specific packages that were used to compile
+# framework.jar to document and check apis
+files_to_check_apis += \
+  $(addprefix ../../,\
+    $(filter \
+      $(foreach dir,$(FRAMEWORKS_BASE_JAVA_SRC_DIRS),\
+        $(foreach package,$(packages_to_document),\
+          $(dir)/$(package)/%.java)),\
+      $(SOONG_FRAMEWORK_SRCS)))
+
+# Find all generated files that were used to compile framework.jar
+files_to_check_apis_generated := \
+  $(filter $(OUT_DIR)/%,\
+    $(SOONG_FRAMEWORK_SRCS))
 
 # These are relative to frameworks/base
 # FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
-dirs_to_document := \
-	$(dirs_to_check_apis) \
-	test-runner/src \
-	$(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS))
-
-patterns_to_not_document := \
-	$(call find-no-docs-pattern, $(dirs_to_document))
+files_to_document := \
+  $(files_to_check_apis) \
+  $(call find-other-java-files,\
+    $(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS)) \
+    test-runner/src)
 
 # These are relative to frameworks/base
 html_dirs := \
 	$(FRAMEWORKS_BASE_SUBDIRS) \
-	$(non_base_dirs)
+	$(non_base_dirs) \
 
 # Common sources for doc check and api check
 common_src_files := \
 	$(call find-other-html-files, $(html_dirs)) \
-	$(addprefix ../../, $(libcore_to_document))
+	$(addprefix ../../, $(libcore_to_document)) \
 
 # These are relative to frameworks/base
 framework_docs_LOCAL_SRC_FILES := \
-	$(filter-out $(patterns_to_not_document), $(call find-other-java-files, $(dirs_to_document))) \
-	$(common_src_files)
+  $(files_to_document) \
+  $(common_src_files) \
 
 # These are relative to frameworks/base
 framework_docs_LOCAL_API_CHECK_SRC_FILES := \
-	$(framework_base_android_test_mock_src_files) \
-	$(framework_base_android_test_runner_excluding_mock_src_files) \
-	$(call all-java-files-under, $(dirs_to_check_apis)) \
-	$(common_src_files)
+  $(framework_base_android_test_mock_src_files) \
+  $(framework_base_android_test_runner_excluding_mock_src_files) \
+  $(files_to_check_apis) \
+  $(common_src_files) \
 
 # This is used by ide.mk as the list of source files that are
 # always included.
@@ -997,11 +337,11 @@
 framework_docs_LOCAL_DROIDDOC_SOURCE_PATH := \
 	$(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
 
-framework_docs_LOCAL_INTERMEDIATE_SOURCES := \
-	$(framework_res_source_path)/android/R.java \
-	$(framework_res_source_path)/android/Manifest.java \
-	$(framework_res_source_path)/com/android/internal/R.java \
-	$(patsubst $(TARGET_OUT_COMMON_INTERMEDIATES)/%,%,$(libcore_to_document_generated))
+framework_docs_LOCAL_SRCJARS := $(SOONG_FRAMEWORK_SRCJARS)
+
+framework_docs_LOCAL_GENERATED_SOURCES := \
+  $(libcore_to_document_generated) \
+  $(files_to_check_apis_generated) \
 
 framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
 	core-oj \
@@ -1122,7 +462,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES := $(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
@@ -1147,7 +488,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES := $(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
@@ -1172,7 +514,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
@@ -1198,7 +541,7 @@
 include $(BUILD_DROIDDOC)
 
 # $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(framework_built) $(gen)
+$(full_target): $(gen)
 $(INTERNAL_PLATFORM_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 
@@ -1206,7 +549,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
@@ -1234,7 +578,7 @@
 include $(BUILD_DROIDDOC)
 
 # $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(framework_built) $(gen)
+$(full_target): $(gen)
 $(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 
@@ -1242,7 +586,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
@@ -1271,7 +616,7 @@
 include $(BUILD_DROIDDOC)
 
 # $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(framework_built) $(gen)
+$(full_target): $(gen)
 $(INTERNAL_PLATFORM_TEST_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
 
@@ -1279,7 +624,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
@@ -1301,7 +647,7 @@
 include $(BUILD_DROIDDOC)
 
 # $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(framework_built) $(gen)
+$(full_target): $(gen)
 
 # Run this for checkbuild
 checkbuild: doc-comment-check-docs
@@ -1312,7 +658,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
@@ -1341,14 +688,14 @@
 	$(hide) $(ACP) $< $@
 
 $(full_target): $(static_doc_index_redirect)
-$(full_target): $(framework_built)
 
 
 # ==== Public API static reference docs ==================================
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
@@ -1383,14 +730,14 @@
 
 $(full_target): $(static_doc_index_redirect)
 $(full_target): $(static_doc_properties)
-$(full_target): $(framework_built)
 
 
 # ==== System API static reference docs ==================================
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
@@ -1434,7 +781,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
@@ -1462,7 +810,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
@@ -1497,7 +846,8 @@
 # ==== docs for the web (on the devsite app engine server) =======================
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
@@ -1526,7 +876,8 @@
 # ==== docs for the web (on the devsite app engine server) =======================
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
@@ -1555,7 +906,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
@@ -1580,7 +932,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
@@ -1608,7 +961,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
-LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
+LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
 LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 57a61ec8..92ee7cc 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -22,6 +22,11 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import android.content.res.ColorStateList;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,40 +38,35 @@
 @RunWith(AndroidJUnit4.class)
 public class StaticLayoutPerfTest {
 
-    public StaticLayoutPerfTest() {
-    }
+    public StaticLayoutPerfTest() {}
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    private static final String FIXED_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing "
-            + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
-            + "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
-            + "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
-            + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
-            + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
-    private static final int FIXED_TEXT_LENGTH = FIXED_TEXT.length();
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
+    private static final int PARA_LENGTH = 500;  // Number of characters in a paragraph.
 
-    private static TextPaint PAINT = new TextPaint();
-    private static final int TEXT_WIDTH = 20 * (int) PAINT.getTextSize();
+    private static final boolean NO_STYLE_TEXT = false;
+    private static final boolean STYLE_TEXT = true;
 
-    @Test
-    public void testCreate() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        while (state.keepRunning()) {
-            StaticLayout.Builder.obtain(FIXED_TEXT, 0, FIXED_TEXT_LENGTH, PAINT, TEXT_WIDTH)
-                    .build();
-        }
-    }
+    private final Random mRandom = new Random(31415926535L);
 
     private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
     private static final int ALPHABET_LENGTH = ALPHABET.length();
 
-    private static final int PARA_LENGTH = 500;
-    private final char[] mBuffer = new char[PARA_LENGTH];
-    private final Random mRandom = new Random(31415926535L);
+    private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000);
+    private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
+    private static final int[] STYLES = {
+            Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
+    };
 
-    private CharSequence generateRandomParagraph(int wordLen) {
+    private final char[] mBuffer = new char[PARA_LENGTH];
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+    private CharSequence generateRandomParagraph(int wordLen, boolean applyRandomStyle) {
         for (int i = 0; i < PARA_LENGTH; i++) {
             if (i % (wordLen + 1) == wordLen) {
                 mBuffer[i] = ' ';
@@ -74,29 +74,112 @@
                 mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH));
             }
         }
-        return CharBuffer.wrap(mBuffer);
+
+        CharSequence cs = CharBuffer.wrap(mBuffer);
+        if (!applyRandomStyle) {
+            return cs;
+        }
+
+        SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
+        for (int i = 0; i < ssb.length(); i += WORD_LENGTH) {
+            final int spanStart = i;
+            final int spanEnd = (i + WORD_LENGTH) > ssb.length() ? ssb.length() : i + WORD_LENGTH;
+
+            final TextAppearanceSpan span = new TextAppearanceSpan(
+                  FAMILIES[mRandom.nextInt(FAMILIES.length)],
+                  STYLES[mRandom.nextInt(STYLES.length)],
+                  24 + mRandom.nextInt(32),  // text size. min 24 max 56
+                  TEXT_COLOR, TEXT_COLOR);
+
+            ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+        return ssb;
     }
 
-    // This tries to simulate the case where the cache hit rate is low, and most of the text is
-    // new text.
     @Test
-    public void testCreateRandom() {
+    public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
         while (state.keepRunning()) {
-            final CharSequence text = generateRandomParagraph(9);
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
         }
     }
 
     @Test
-    public void testCreateRandom_breakBalanced() {
+    public void testCreate_RandomText_NoStyled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            final CharSequence text = generateRandomParagraph(9);
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_RandomText_NoStyled_Greedy_Hyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_RandomText_NoStyled_Balanced_NoHyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .build();
         }
     }
+
+    @Test
+    public void testCreate_RandomText_NoStyled_Balanced_Hyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_RandomText_Styled_Greedy_NoHyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .build();
+        }
+    }
 }
diff --git a/api/current.txt b/api/current.txt
index 182dd3a..38b5090 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1470,6 +1470,8 @@
     field public static final int vendor = 16843751; // 0x10103e7
     field public static final int version = 16844057; // 0x1010519
     field public static final int versionCode = 16843291; // 0x101021b
+    field public static final int versionCodeMajor = 16844150; // 0x1010576
+    field public static final int versionMajor = 16844151; // 0x1010577
     field public static final int versionName = 16843292; // 0x101021c
     field public static final int verticalCorrection = 16843322; // 0x101023a
     field public static final int verticalDivider = 16843054; // 0x101012e
@@ -6388,6 +6390,7 @@
     method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
+    method public boolean isLogoutButtonEnabled();
     method public boolean isManagedProfile(android.content.ComponentName);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
@@ -6430,6 +6433,7 @@
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
     method public void setLockTaskFeatures(android.content.ComponentName, int);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
+    method public void setLogoutButtonEnabled(android.content.ComponentName, boolean);
     method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
@@ -6465,6 +6469,7 @@
     method public void setShortSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
+    method public void setSystemSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
     method public boolean setTime(android.content.ComponentName, long);
     method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
@@ -6766,6 +6771,7 @@
     method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
     method public void onQuotaExceeded(long, long);
     method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
+    method public void onRestore(android.app.backup.BackupDataInput, long, android.os.ParcelFileDescriptor) throws java.io.IOException;
     method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
     method public void onRestoreFinished();
     field public static final int TYPE_DIRECTORY = 2; // 0x2
@@ -7209,6 +7215,7 @@
   }
 
   public final class UsageStatsManager {
+    method public int getAppStandbyBucket();
     method public boolean isAppInactive(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
     method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
@@ -7219,6 +7226,10 @@
     field public static final int INTERVAL_MONTHLY = 2; // 0x2
     field public static final int INTERVAL_WEEKLY = 1; // 0x1
     field public static final int INTERVAL_YEARLY = 3; // 0x3
+    field public static final int STANDBY_BUCKET_ACTIVE = 10; // 0xa
+    field public static final int STANDBY_BUCKET_FREQUENT = 30; // 0x1e
+    field public static final int STANDBY_BUCKET_RARE = 40; // 0x28
+    field public static final int STANDBY_BUCKET_WORKING_SET = 20; // 0x14
   }
 
 }
@@ -10584,6 +10595,8 @@
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
+    method public long getLongVersionCode();
+    method public void setLongVersionCode(long);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInfo> CREATOR;
     field public static final int INSTALL_LOCATION_AUTO = 0; // 0x0
@@ -10613,7 +10626,7 @@
     field public android.content.pm.Signature[] signatures;
     field public java.lang.String[] splitNames;
     field public int[] splitRevisionCodes;
-    field public int versionCode;
+    field public deprecated int versionCode;
     field public java.lang.String versionName;
   }
 
@@ -11128,9 +11141,10 @@
     method public int describeContents();
     method public android.content.pm.VersionedPackage getDeclaringPackage();
     method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
+    method public long getLongVersion();
     method public java.lang.String getName();
     method public int getType();
-    method public int getVersion();
+    method public deprecated int getVersion();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
     field public static final int TYPE_BUILTIN = 0; // 0x0
@@ -11224,9 +11238,11 @@
 
   public final class VersionedPackage implements android.os.Parcelable {
     ctor public VersionedPackage(java.lang.String, int);
+    ctor public VersionedPackage(java.lang.String, long);
     method public int describeContents();
+    method public long getLongVersionCode();
     method public java.lang.String getPackageName();
-    method public int getVersionCode();
+    method public deprecated int getVersionCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.VersionedPackage> CREATOR;
   }
@@ -26166,6 +26182,7 @@
   public class TrafficStats {
     ctor public TrafficStats();
     method public static void clearThreadStatsTag();
+    method public static void clearThreadStatsUid();
     method public static int getAndSetThreadStatsTag(int);
     method public static long getMobileRxBytes();
     method public static long getMobileRxPackets();
@@ -26191,9 +26208,12 @@
     method public static void incrementOperationCount(int);
     method public static void incrementOperationCount(int, int);
     method public static void setThreadStatsTag(int);
+    method public static void setThreadStatsUidSelf();
     method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
     method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
     method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method public static void untagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
     method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
     field public static final int UNSUPPORTED = -1; // 0xffffffff
   }
@@ -34291,6 +34311,8 @@
     field public static final java.lang.String IS_READ_ONLY = "is_read_only";
     field public static final java.lang.String IS_SUPER_PRIMARY = "is_super_primary";
     field public static final java.lang.String MIMETYPE = "mimetype";
+    field public static final java.lang.String PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME = "preferred_phone_account_component_name";
+    field public static final java.lang.String PREFERRED_PHONE_ACCOUNT_ID = "preferred_phone_account_id";
     field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
     field public static final java.lang.String RES_PACKAGE = "res_package";
     field public static final java.lang.String SYNC1 = "data_sync1";
@@ -35957,6 +35979,7 @@
     field public static final java.lang.String DATE = "date";
     field public static final java.lang.String DELETED = "deleted";
     field public static final java.lang.String DIRTY = "dirty";
+    field public static final int DIRTY_RETAIN = -1; // 0xffffffff
     field public static final java.lang.String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String HAS_CONTENT = "has_content";
@@ -37473,6 +37496,8 @@
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
     method public android.service.autofill.FillResponse.Builder setFlags(int);
+    method public android.service.autofill.FillResponse.Builder setFooter(android.widget.RemoteViews);
+    method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
@@ -39273,6 +39298,7 @@
     field public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; // 0x3
     field public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; // 0x2
     field public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; // 0x4
+    field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5; // 0x5
   }
 
   public static class Call.Details {
@@ -39452,6 +39478,7 @@
     method public final int getState();
     method public final android.telecom.StatusHints getStatusHints();
     method public final android.telecom.Connection.VideoProvider getVideoProvider();
+    method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream);
     method public final boolean isRingbackRequested();
     method public void onAbort();
     method public void onAnswer(int);
@@ -39468,8 +39495,10 @@
     method public void onReject(java.lang.String);
     method public void onSeparate();
     method public void onShowIncomingCallUi();
+    method public void onStartRtt(android.telecom.Connection.RttTextStream);
     method public void onStateChanged(int);
     method public void onStopDtmfTone();
+    method public void onStopRtt();
     method public void onUnhold();
     method public static java.lang.String propertiesToString(int);
     method public final void putExtras(android.os.Bundle);
@@ -39477,6 +39506,10 @@
     method public final void removeExtras(java.lang.String...);
     method public void requestBluetoothAudio(java.lang.String);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
+    method public final void sendRemoteRttRequest();
+    method public final void sendRttInitiationFailure(int);
+    method public final void sendRttInitiationSuccess();
+    method public final void sendRttSessionRemotelyTerminated();
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
     method public final void setAudioModeIsVoip(boolean);
@@ -39530,6 +39563,7 @@
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+    field public static final int PROPERTY_IS_RTT = 256; // 0x100
     field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
@@ -39549,6 +39583,12 @@
     field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
   }
 
+  public static final class Connection.RttTextStream {
+    method public java.lang.String read() throws java.io.IOException;
+    method public java.lang.String readImmediately() throws java.io.IOException;
+    method public void write(java.lang.String) throws java.io.IOException;
+  }
+
   public static abstract class Connection.VideoProvider {
     ctor public Connection.VideoProvider();
     method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -39589,7 +39629,9 @@
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public android.net.Uri getAddress();
     method public android.os.Bundle getExtras();
+    method public android.telecom.Connection.RttTextStream getRttTextStream();
     method public int getVideoState();
+    method public boolean isRequestingRtt();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
   }
@@ -49053,31 +49095,34 @@
 package android.view.textclassifier {
 
   public final class TextClassification {
-    method public int getActionCount();
     method public float getConfidenceScore(java.lang.String);
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
-    method public android.graphics.drawable.Drawable getIcon(int);
     method public android.graphics.drawable.Drawable getIcon();
-    method public android.content.Intent getIntent(int);
     method public android.content.Intent getIntent();
-    method public java.lang.CharSequence getLabel(int);
     method public java.lang.CharSequence getLabel();
-    method public android.view.View.OnClickListener getOnClickListener(int);
     method public android.view.View.OnClickListener getOnClickListener();
+    method public int getSecondaryActionsCount();
+    method public android.graphics.drawable.Drawable getSecondaryIcon(int);
+    method public android.content.Intent getSecondaryIntent(int);
+    method public java.lang.CharSequence getSecondaryLabel(int);
+    method public android.view.View.OnClickListener getSecondaryOnClickListener(int);
+    method public java.lang.String getSignature();
     method public java.lang.String getText();
   }
 
   public static final class TextClassification.Builder {
     ctor public TextClassification.Builder();
-    method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
+    method public android.view.textclassifier.TextClassification.Builder addSecondaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
     method public android.view.textclassifier.TextClassification build();
-    method public android.view.textclassifier.TextClassification.Builder clearActions();
+    method public android.view.textclassifier.TextClassification.Builder clearSecondaryActions();
     method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
     method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
     method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
     method public android.view.textclassifier.TextClassification.Builder setLabel(java.lang.String);
     method public android.view.textclassifier.TextClassification.Builder setOnClickListener(android.view.View.OnClickListener);
+    method public android.view.textclassifier.TextClassification.Builder setPrimaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
+    method public android.view.textclassifier.TextClassification.Builder setSignature(java.lang.String);
     method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String);
   }
 
@@ -49142,12 +49187,14 @@
     method public int getEntityCount();
     method public int getSelectionEndIndex();
     method public int getSelectionStartIndex();
+    method public java.lang.String getSignature();
   }
 
   public static final class TextSelection.Builder {
     ctor public TextSelection.Builder(int, int);
     method public android.view.textclassifier.TextSelection build();
     method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
+    method public android.view.textclassifier.TextSelection.Builder setSignature(java.lang.String);
   }
 
   public static final class TextSelection.Options {
@@ -54868,7 +54915,6 @@
     method public java.lang.Object[] getSigners();
     method public java.lang.String getSimpleName();
     method public java.lang.Class<? super T> getSuperclass();
-    method public java.lang.String getTypeName();
     method public synchronized java.lang.reflect.TypeVariable<java.lang.Class<T>>[] getTypeParameters();
     method public boolean isAnnotation();
     method public boolean isAnonymousClass();
@@ -56529,6 +56575,11 @@
     ctor public MalformedParameterizedTypeException();
   }
 
+  public class MalformedParametersException extends java.lang.RuntimeException {
+    ctor public MalformedParametersException();
+    ctor public MalformedParametersException(java.lang.String);
+  }
+
   public abstract interface Member {
     method public abstract java.lang.Class<?> getDeclaringClass();
     method public abstract int getModifiers();
@@ -56626,6 +56677,7 @@
   }
 
   public abstract interface Type {
+    method public default java.lang.String getTypeName();
   }
 
   public abstract interface TypeVariable<D extends java.lang.reflect.GenericDeclaration> implements java.lang.reflect.Type {
diff --git a/api/system-current.txt b/api/system-current.txt
index 4cf9d95..1ce043c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -126,6 +126,7 @@
     field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
     field public static final java.lang.String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
     field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
+    field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
     field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
     field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
     field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
@@ -409,8 +410,9 @@
     field public static final java.lang.String EXTRA_LOG_CANCEL_ALL = "android.app.backup.extra.LOG_CANCEL_ALL";
     field public static final java.lang.String EXTRA_LOG_EVENT_CATEGORY = "android.app.backup.extra.LOG_EVENT_CATEGORY";
     field public static final java.lang.String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID";
+    field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_FULL_VERSION";
     field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_NAME = "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME";
-    field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
+    field public static final deprecated java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
     field public static final java.lang.String EXTRA_LOG_EXCEPTION_FULL_BACKUP = "android.app.backup.extra.LOG_EXCEPTION_FULL_BACKUP";
     field public static final java.lang.String EXTRA_LOG_ILLEGAL_KEY = "android.app.backup.extra.LOG_ILLEGAL_KEY";
     field public static final java.lang.String EXTRA_LOG_MANIFEST_PACKAGE_NAME = "android.app.backup.extra.LOG_MANIFEST_PACKAGE_NAME";
@@ -609,8 +611,11 @@
   }
 
   public final class UsageStatsManager {
+    method public int getAppStandbyBucket(java.lang.String);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
+    field public static final int STANDBY_BUCKET_EXEMPTED = 5; // 0x5
+    field public static final int STANDBY_BUCKET_NEVER = 50; // 0x32
   }
 
 }
@@ -777,13 +782,15 @@
 
   public final class InstantAppResolveInfo implements android.os.Parcelable {
     ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, int);
+    ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, long);
     ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>);
     method public int describeContents();
     method public byte[] getDigestBytes();
     method public int getDigestPrefix();
     method public java.util.List<android.content.pm.InstantAppIntentFilter> getIntentFilters();
+    method public long getLongVersionCode();
     method public java.lang.String getPackageName();
-    method public int getVersionCode();
+    method public deprecated int getVersionCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR;
   }
@@ -840,6 +847,7 @@
   public abstract class PackageManager {
     method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
+    method public android.content.pm.dex.ArtManager getArtManager();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
@@ -947,6 +955,24 @@
 
 }
 
+package android.content.pm.dex {
+
+  public class ArtManager {
+    method public boolean isRuntimeProfilingEnabled();
+    method public void snapshotRuntimeProfile(java.lang.String, java.lang.String, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback, android.os.Handler);
+    field public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; // 0x1
+    field public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; // 0x2
+    field public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; // 0x0
+  }
+
+  public static abstract class ArtManager.SnapshotRuntimeProfileCallback {
+    ctor public ArtManager.SnapshotRuntimeProfileCallback();
+    method public abstract void onError(int);
+    method public abstract void onSuccess(android.os.ParcelFileDescriptor);
+  }
+
+}
+
 package android.content.pm.permission {
 
   public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
@@ -4103,6 +4129,7 @@
     method public java.lang.String getCdmaMdn(int);
     method public java.lang.String getCdmaMin();
     method public java.lang.String getCdmaMin(int);
+    method public java.lang.String getCdmaPrlVersion();
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
     method public deprecated boolean getDataEnabled();
@@ -4329,6 +4356,7 @@
   public final class StatsManager {
     method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
     method public byte[] getData(java.lang.String);
+    method public byte[] getMetadata();
     method public boolean removeConfiguration(java.lang.String);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index b181538..7e0731a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -18,7 +18,7 @@
     method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
     method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
     method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
-    method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect) throws java.lang.SecurityException;
+    method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
     method public static boolean supportsMultiWindow(android.content.Context);
     method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
     field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
@@ -457,19 +457,12 @@
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
   }
 
-  public final class FieldsDetection implements android.os.Parcelable {
-    ctor public FieldsDetection(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldsDetection> CREATOR;
-  }
-
   public static final class FillEventHistory.Event {
-    method public java.util.Map<java.lang.String, java.lang.Integer> getDetectedFields();
+    method public java.util.Map<java.lang.String, java.lang.Integer> getFieldsClassification();
   }
 
   public static final class FillResponse.Builder {
-    method public android.service.autofill.FillResponse.Builder setFieldsDetection(android.service.autofill.FieldsDetection);
+    method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
   }
 
   public final class ImageTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -501,6 +494,22 @@
     method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
   }
 
+  public final class UserData implements android.os.Parcelable {
+    method public int describeContents();
+    method public static int getMaxFieldClassificationIdsSize();
+    method public static int getMaxUserDataSize();
+    method public static int getMaxValueLength();
+    method public static int getMinValueLength();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
+  }
+
+  public static final class UserData.Builder {
+    ctor public UserData.Builder(java.lang.String, java.lang.String);
+    method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
+    method public android.service.autofill.UserData build();
+  }
+
   public abstract interface ValueFinder {
     method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
   }
@@ -591,32 +600,6 @@
 
 }
 
-package android.telecom {
-
-  public abstract class Connection extends android.telecom.Conferenceable {
-    method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream);
-    method public void onStartRtt(android.telecom.Connection.RttTextStream);
-    method public void onStopRtt();
-    method public final void sendRemoteRttRequest();
-    method public final void sendRttInitiationFailure(int);
-    method public final void sendRttInitiationSuccess();
-    method public final void sendRttSessionRemotelyTerminated();
-    field public static final int PROPERTY_IS_RTT = 256; // 0x100
-  }
-
-  public static final class Connection.RttTextStream {
-    method public java.lang.String read() throws java.io.IOException;
-    method public java.lang.String readImmediately() throws java.io.IOException;
-    method public void write(java.lang.String) throws java.io.IOException;
-  }
-
-  public final class ConnectionRequest implements android.os.Parcelable {
-    method public android.telecom.Connection.RttTextStream getRttTextStream();
-    method public boolean isRequestingRtt();
-  }
-
-}
-
 package android.telephony {
 
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
@@ -983,6 +966,11 @@
     ctor public AutofillId(int);
   }
 
+  public final class AutofillManager {
+    method public android.service.autofill.UserData getUserData();
+    method public void setUserData(android.service.autofill.UserData);
+  }
+
 }
 
 package android.widget {
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index 140b12c..5db2239 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -16,37 +16,18 @@
 
 #include "Privacy.h"
 
+#include <android/os/IncidentReportArgs.h>
 #include <stdlib.h>
 
-// DESTINATION enum value
-const uint8_t DEST_LOCAL = 0;
-const uint8_t DEST_EXPLICIT = 1;
-const uint8_t DEST_AUTOMATIC = 2;
+uint64_t encode_field_id(const Privacy* p) { return (uint64_t)p->type << 32 | p->field_id; }
 
-// type of the field, identitical to protobuf definition
-const uint8_t TYPE_STRING = 9;
-const uint8_t TYPE_MESSAGE = 11;
-
-bool
-Privacy::IsMessageType() const { return type == TYPE_MESSAGE; }
-
-uint64_t
-Privacy::EncodedFieldId() const { return (uint64_t)type << 32 | field_id; }
-
-bool
-Privacy::IsStringType() const { return type == TYPE_STRING; }
-
-bool
-Privacy::HasChildren() const { return children != NULL && children[0] != NULL; }
-
-const Privacy*
-Privacy::lookup(uint32_t fieldId) const
+const Privacy* lookup(const Privacy* p, uint32_t fieldId)
 {
-    if (children == NULL) return NULL;
-    for (int i=0; children[i] != NULL; i++) {
-        if (children[i]->field_id == fieldId) return children[i];
-        // This assumes the list's field id is in ascending order and must be true.
-        if (children[i]->field_id > fieldId) return NULL;
+    if (p->children == NULL) return NULL;
+    for (int i=0; p->children[i] != NULL; i++) { // NULL-terminated.
+        if (p->children[i]->field_id == fieldId) return p->children[i];
+        // Incident section gen tool guarantees field ids in ascending order.
+        if (p->children[i]->field_id > fieldId) return NULL;
     }
     return NULL;
 }
@@ -54,11 +35,14 @@
 static bool allowDest(const uint8_t dest, const uint8_t policy)
 {
     switch (policy) {
-    case DEST_LOCAL:
-        return dest == DEST_LOCAL;
-    case DEST_EXPLICIT:
-        return dest == DEST_LOCAL || dest == DEST_EXPLICIT;
-    case DEST_AUTOMATIC:
+    case android::os::DEST_LOCAL:
+        return dest == android::os::DEST_LOCAL;
+    case android::os::DEST_EXPLICIT:
+    case DEST_UNSET:
+        return dest == android::os::DEST_LOCAL ||
+            dest == android::os::DEST_EXPLICIT ||
+            dest == DEST_UNSET;
+    case android::os::DEST_AUTOMATIC:
         return true;
     default:
         return false;
@@ -72,18 +56,19 @@
 }
 
 bool
-PrivacySpec::CheckPremission(const Privacy* privacy) const
+PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const
 {
-    uint8_t policy = privacy == NULL ? DEST_DEFAULT_VALUE : privacy->dest;
+    uint8_t policy = privacy != NULL ? privacy->dest : defaultDest;
     return allowDest(dest, policy);
 }
 
 bool
-PrivacySpec::RequireAll() const { return dest == DEST_LOCAL; }
+PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; }
 
-PrivacySpec new_spec_from_args(int dest) {
+PrivacySpec new_spec_from_args(int dest)
+{
   if (dest < 0) return PrivacySpec();
   return PrivacySpec(dest);
 }
 
-PrivacySpec get_default_dropbox_spec() { return PrivacySpec(DEST_AUTOMATIC); }
\ No newline at end of file
+PrivacySpec get_default_dropbox_spec() { return PrivacySpec(android::os::DEST_AUTOMATIC); }
\ No newline at end of file
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index f514f19..9e15ff4 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -19,35 +19,46 @@
 
 #include <stdint.h>
 
-// This is the default value of DEST enum
-const uint8_t DEST_DEFAULT_VALUE = 1;
+// This is the default value of DEST enum, sync with privacy.proto
+const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident
+const uint8_t DEST_DEFAULT_VALUE = DEST_UNSET;
 
 /*
- * In order not to depend on libprotobuf-cpp-full nor libplatformprotos in incidentd,
- * privacy options's data structure are explicitly redefined in this file.
+ * In order to NOT auto-generate large chuck of code by proto compiler in incidentd,
+ * privacy options's data structure are explicitly redefined here and
+ * the values are populated by incident_section_gen tool.
+ *
+ * Each proto field will have a Privacy when it is different from its parent, otherwise
+ * it uses its parent's tag. A message type will have an array of Privacy.
  */
 struct Privacy {
+    // The field number
     uint32_t field_id;
+
+    // The field type, see external/protobuf/src/google/protobuf/descriptor.h
     uint8_t type;
-    // ignore parent's privacy flags if children are set, NULL-terminated
+
+    // If children is null, it is a primitive field,
+    // otherwise it is a message field which could have overridden privacy tags here.
+    // This array is NULL-terminated.
     Privacy** children;
 
-    // the following fields are identitical to
-    // frameworks/base/libs/incident/proto/android/privacy.proto
+    // DESTINATION Enum in frameworks/base/libs/incident/proto/android/privacy.proto.
     uint8_t dest;
-    const char** patterns; // only set when type is string
-
-    bool IsMessageType() const;
-    bool IsStringType() const;
-    bool HasChildren() const;
-    uint64_t EncodedFieldId() const;
-
-    const Privacy* lookup(uint32_t fieldId) const;
+    // A list of regexp rules for stripping string fields in proto.
+    const char** patterns;
 };
 
+// Encode field id used by ProtoOutputStream.
+uint64_t encode_field_id(const Privacy* p);
+
+// Look up the child with given fieldId, if not found, return NULL.
+const Privacy* lookup(const Privacy* p, uint32_t fieldId);
+
 /**
  * PrivacySpec defines the request has what level of privacy authorization.
  * For example, a device without user consent should only be able to upload AUTOMATIC fields.
+ * DEST_UNSET are treated as DEST_EXPLICIT.
  */
 class PrivacySpec {
 public:
@@ -58,7 +69,10 @@
 
     bool operator<(const PrivacySpec& other) const;
 
-    bool CheckPremission(const Privacy* privacy) const;
+    // check permission of a policy, if returns true, don't strip the data.
+    bool CheckPremission(const Privacy* privacy, const uint8_t defaultDest = DEST_DEFAULT_VALUE) const;
+
+    // if returns true, no data need to be stripped.
     bool RequireAll() const;
 };
 
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 77ae1a7..03faa92 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-
+#define LOG_TAG "incidentd"
 
 #include "PrivacyBuffer.h"
 #include "io_util.h"
 
 #include <android/util/protobuf.h>
+#include <cutils/log.h>
 
 using namespace android::util;
 
+const bool DEBUG = false;
+
 /**
  * Write the field to buf based on the wire type, iterator will point to next field.
  * If skip is set to true, no data will be written to buf. Return number of bytes written.
@@ -30,6 +33,9 @@
 void
 PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip)
 {
+    if (DEBUG) ALOGD("%s field %d (wiretype = %d)", skip ? "skip" : "write",
+        read_field_id(fieldTag), read_wire_type(fieldTag));
+
     uint8_t wireType = read_wire_type(fieldTag);
     size_t bytesToWrite = 0;
     uint32_t varint = 0;
@@ -55,6 +61,7 @@
             bytesToWrite = 4;
             break;
     }
+    if (DEBUG) ALOGD("%s %d bytes of data", skip ? "skip" : "write", (int)bytesToWrite);
     if (skip) {
         mData.rp()->move(bytesToWrite);
     } else {
@@ -76,10 +83,13 @@
 {
     if (!mData.hasNext() || parentPolicy == NULL) return BAD_VALUE;
     uint32_t fieldTag = mData.readRawVarint();
-    const Privacy* policy = parentPolicy->lookup(read_field_id(fieldTag));
+    const Privacy* policy = lookup(parentPolicy, read_field_id(fieldTag));
 
-    if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
-        bool skip = !spec.CheckPremission(policy);
+    if (policy == NULL || policy->children == NULL) {
+        if (DEBUG) ALOGD("Not a message field %d: dest(%d)", read_field_id(fieldTag),
+            policy != NULL ? policy->dest : parentPolicy->dest);
+
+        bool skip = !spec.CheckPremission(policy, parentPolicy->dest);
         // iterator will point to head of next field
         writeFieldOrSkip(fieldTag, skip);
         return NO_ERROR;
@@ -87,7 +97,7 @@
     // current field is message type and its sub-fields have extra privacy policies
     uint32_t msgSize = mData.readRawVarint();
     EncodedBuffer::Pointer start = mData.rp()->copy();
-    long long token = mProto.start(policy->EncodedFieldId());
+    long long token = mProto.start(encode_field_id(policy));
     while (mData.rp()->pos() - start.pos() != msgSize) {
         status_t err = stripField(policy, spec);
         if (err != NO_ERROR) return err;
@@ -112,8 +122,9 @@
 status_t
 PrivacyBuffer::strip(const PrivacySpec& spec)
 {
+    if (DEBUG) ALOGD("Strip with spec %d", spec.dest);
     // optimization when no strip happens
-    if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
+    if (mPolicy == NULL || mPolicy->children == NULL || spec.RequireAll()) {
         if (spec.CheckPremission(mPolicy)) mSize = mData.size();
         return NO_ERROR;
     }
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index c08b9ea..1bf795b 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -210,9 +210,9 @@
 {
     for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) {
         const sp<ReportRequest> request = *it;
-        const vector<vector<int8_t>>& headers = request->args.headers();
+        const vector<vector<uint8_t>>& headers = request->args.headers();
 
-        for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) {
+        for (vector<vector<uint8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) {
             if (buf->empty()) continue;
 
             // So the idea is only requests with negative fd are written to dropbox file.
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
index f043d36..af4a35c 100644
--- a/cmds/incidentd/src/io_util.cpp
+++ b/cmds/incidentd/src/io_util.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "incidentd"
+
 #include "io_util.h"
 
 #include <unistd.h>
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index ca94623..32b9e42 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -12,27 +12,27 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#define LOG_TAG "incidentd"
+
 #include "FdBuffer.h"
 #include "PrivacyBuffer.h"
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
+#include <android/os/IncidentReportArgs.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <string.h>
 
 using namespace android;
 using namespace android::base;
+using namespace android::os;
 using namespace std;
 using ::testing::StrEq;
 using ::testing::Test;
 using ::testing::internal::CaptureStdout;
 using ::testing::internal::GetCapturedStdout;
 
-const uint8_t LOCAL = 0;
-const uint8_t EXPLICIT = 1;
-const uint8_t AUTOMATIC = 2;
-
 const uint8_t OTHER_TYPE = 1;
 const uint8_t STRING_TYPE = 9;
 const uint8_t MESSAGE_TYPE = 11;
@@ -109,21 +109,11 @@
         p->field_id = field_id;
         p->type = MESSAGE_TYPE;
         p->children = children;
-        p->dest = EXPLICIT;
+        p->dest = DEST_DEFAULT_VALUE;
         p->patterns = NULL;
         return p;
     }
 
-    Privacy* create_string_privacy(uint32_t field_id, uint8_t dest, const char** patterns) {
-        Privacy* p = new_uninit_privacy();
-        p->field_id = field_id;
-        p->type = STRING_TYPE;
-        p->children = NULL;
-        p->dest = dest;
-        p->patterns = patterns;
-        return p;
-    }
-
     FdBuffer buffer;
 private:
     TemporaryFile tf;
@@ -138,103 +128,103 @@
     }
 };
 
-TEST_F(PrivacyBufferTest, NullFieldPolicy) {
+TEST_F(PrivacyBufferTest, NullPolicy) {
     writeToFdBuffer(STRING_FIELD_0);
-    assertStrip(EXPLICIT, STRING_FIELD_0, create_string_privacy(300, AUTOMATIC, NULL));
+    assertStrip(DEST_EXPLICIT, STRING_FIELD_0, NULL);
 }
 
-TEST_F(PrivacyBufferTest, StripSpecNotAllowed) {
+TEST_F(PrivacyBufferTest, StripUnsetField) {
     writeToFdBuffer(STRING_FIELD_0);
-    assertStripByFields(AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, EXPLICIT));
+    assertStripByFields(DEST_AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, DEST_UNSET));
 }
 
 TEST_F(PrivacyBufferTest, StripVarintField) {
     writeToFdBuffer(VARINT_FIELD_1);
-    assertStripByFields(EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, LOCAL));
+    assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripLengthDelimitedField_String) {
     writeToFdBuffer(STRING_FIELD_2);
-    assertStripByFields(EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, LOCAL));
+    assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripFixed64Field) {
     writeToFdBuffer(FIX64_FIELD_3);
-    assertStripByFields(EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, LOCAL));
+    assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripFixed32Field) {
     writeToFdBuffer(FIX32_FIELD_4);
-    assertStripByFields(EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, LOCAL));
+    assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripLengthDelimitedField_Message) {
     writeToFdBuffer(MESSAGE_FIELD_5);
-    assertStripByFields(EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, LOCAL));
+    assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, NoStripVarintField) {
     writeToFdBuffer(VARINT_FIELD_1);
-    assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) {
     writeToFdBuffer(STRING_FIELD_2);
-    assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripFixed64Field) {
     writeToFdBuffer(FIX64_FIELD_3);
-    assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripFixed32Field) {
     writeToFdBuffer(FIX32_FIELD_4);
-    assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) {
     writeToFdBuffer(MESSAGE_FIELD_5);
-    assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, AUTOMATIC));
+    assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC));
 }
 
 TEST_F(PrivacyBufferTest, StripVarintAndString) {
     writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
             + FIX64_FIELD_3 + FIX32_FIELD_4);
     string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4;
-    assertStripByFields(EXPLICIT, expected, 2,
-            create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(2, STRING_TYPE, LOCAL));
+    assertStripByFields(DEST_EXPLICIT, expected, 2,
+            create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(2, STRING_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripVarintAndFixed64) {
     writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
             + FIX64_FIELD_3 + FIX32_FIELD_4);
     string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4;
-    assertStripByFields(EXPLICIT, expected, 2,
-            create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(3, OTHER_TYPE, LOCAL));
+    assertStripByFields(DEST_EXPLICIT, expected, 2,
+            create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(3, OTHER_TYPE, DEST_LOCAL));
 }
 
 TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) {
     writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
     string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
-    assertStripByFields(EXPLICIT, expected, 1, create_message_privacy(5, list));
+    assertStripByFields(DEST_EXPLICIT, expected, 1, create_message_privacy(5, list));
 }
 
 TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) {
     writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
     string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
-    assertStripByFields(EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, LOCAL), create_message_privacy(5, list));
+    assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), create_message_privacy(5, list));
 }
 
 TEST_F(PrivacyBufferTest, ClearAndStrip) {
     string data = STRING_FIELD_0 + VARINT_FIELD_1;
     writeToFdBuffer(data);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
     EncodedBuffer::iterator bufData = buffer.data();
     PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
-    PrivacySpec spec1(EXPLICIT), spec2(LOCAL);
+    PrivacySpec spec1(DEST_EXPLICIT), spec2(DEST_LOCAL);
 
     ASSERT_EQ(privacyBuf.strip(spec1), NO_ERROR);
     assertBuffer(privacyBuf, STRING_FIELD_0);
@@ -244,7 +234,7 @@
 
 TEST_F(PrivacyBufferTest, BadDataInFdBuffer) {
     writeToFdBuffer("iambaddata");
-    Privacy* list[] = { create_privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
+    Privacy* list[] = { create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL };
     EncodedBuffer::iterator bufData = buffer.data();
     PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
     PrivacySpec spec;
@@ -253,7 +243,7 @@
 
 TEST_F(PrivacyBufferTest, BadDataInNestedMessage) {
     writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
     Privacy* field5[] = { create_message_privacy(5, list), NULL };
     EncodedBuffer::iterator bufData = buffer.data();
     PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData);
@@ -265,8 +255,17 @@
     string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5;
     writeToFdBuffer(input);
     Privacy* field5 = create_message_privacy(5, NULL);
-    Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), field5, NULL };
+    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL };
     field5->children = list;
     string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2;
-    assertStrip(EXPLICIT, expected, field5);
+    assertStrip(DEST_EXPLICIT, expected, field5);
 }
+
+TEST_F(PrivacyBufferTest, AutoMessage) {
+    writeToFdBuffer(STRING_FIELD_2 + MESSAGE_FIELD_5);
+    Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
+    Privacy* autoMsg = create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC);
+    autoMsg->children = list;
+    string expected = "\x2a\xd" + STRING_FIELD_2;
+    assertStripByFields(DEST_AUTOMATIC, expected, 1, autoMsg);
+}
\ No newline at end of file
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index 8d99fc7..531c9f2 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -141,7 +141,7 @@
     IncidentReportArgs args1, args2;
     args1.addSection(1);
     args2.addSection(2);
-    std::vector<int8_t> header {'a', 'b', 'c', 'd', 'e'};
+    std::vector<uint8_t> header {'a', 'b', 'c', 'd', 'e'};
     args2.addHeader(header);
     sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd);
     sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd);
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 649e908..0c7876c 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -66,12 +66,12 @@
     args1.addSection(2);
     args2.setAll(true);
 
-    vector<int8_t> head1;
+    vector<uint8_t> head1;
     head1.push_back('a');
     head1.push_back('x');
     head1.push_back('e');
 
-    vector<int8_t> head2;
+    vector<uint8_t> head2;
     head2.push_back('p');
     head2.push_back('u');
     head2.push_back('p');
diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp
index 4acc429..1d6213f 100644
--- a/cmds/incidentd/tests/section_list.cpp
+++ b/cmds/incidentd/tests/section_list.cpp
@@ -5,20 +5,16 @@
     NULL
 };
 
-const uint8_t LOCAL = 0;
-const uint8_t EXPLICIT = 1;
-const uint8_t AUTOMATIC = 2;
-
-Privacy sub_field_1 { 1, 1, NULL, LOCAL, NULL };
-Privacy sub_field_2 { 2, 9, NULL, AUTOMATIC, NULL };
+Privacy sub_field_1 { 1, 1, NULL, DEST_LOCAL, NULL };
+Privacy sub_field_2 { 2, 9, NULL, DEST_AUTOMATIC, NULL };
 
 Privacy* list[] = {
     &sub_field_1,
     &sub_field_2,
     NULL };
 
-Privacy field_0 { 0, 11, list, EXPLICIT, NULL };
-Privacy field_1 { 1, 9, NULL, AUTOMATIC, NULL };
+Privacy field_0 { 0, 11, list, DEST_EXPLICIT, NULL };
+Privacy field_1 { 1, 9, NULL, DEST_AUTOMATIC, NULL };
 
 Privacy* final_list[] = {
     &field_0,
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6ded246..3172281 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -159,7 +159,7 @@
     void const* mapbase = MAP_FAILED;
     ssize_t mapsize = -1;
 
-    void const* base = NULL;
+    void* base = NULL;
     uint32_t w, s, h, f;
     android_dataspace d;
     size_t size = 0;
@@ -179,7 +179,6 @@
     ProcessState::self()->setThreadPoolMaxThreadCount(0);
     ProcessState::self()->startThreadPool();
 
-    ScreenshotClient screenshot;
     sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
     if (display == NULL) {
         fprintf(stderr, "Unable to get handle for display %d\n", displayId);
@@ -199,51 +198,57 @@
     uint8_t displayOrientation = configs[activeConfig].orientation;
     uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
 
-    status_t result = screenshot.update(display, Rect(),
-            0 /* reqWidth */, 0 /* reqHeight */,
-            INT32_MIN, INT32_MAX, /* all layers */
-            false, captureOrientation);
-    if (result == NO_ERROR) {
-        base = screenshot.getPixels();
-        w = screenshot.getWidth();
-        h = screenshot.getHeight();
-        s = screenshot.getStride();
-        f = screenshot.getFormat();
-        d = screenshot.getDataSpace();
-        size = screenshot.getSize();
+    sp<GraphicBuffer> outBuffer;
+    status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
+            0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation,
+            &outBuffer);
+    if (result != NO_ERROR) {
+        close(fd);
+        _exit(1);
     }
 
-    if (base != NULL) {
-        if (png) {
-            const SkImageInfo info =
-                SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType,
-                    dataSpaceToColorSpace(d));
-            SkPixmap pixmap(info, base, s * bytesPerPixel(f));
-            struct FDWStream final : public SkWStream {
-              size_t fBytesWritten = 0;
-              int fFd;
-              FDWStream(int f) : fFd(f) {}
-              size_t bytesWritten() const override { return fBytesWritten; }
-              bool write(const void* buffer, size_t size) override {
-                fBytesWritten += size;
-                return size == 0 || ::write(fFd, buffer, size) > 0;
-              }
-            } fdStream(fd);
-            (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
-            if (fn != NULL) {
-                notifyMediaScanner(fn);
-            }
-        } else {
-            uint32_t c = dataSpaceToInt(d);
-            write(fd, &w, 4);
-            write(fd, &h, 4);
-            write(fd, &f, 4);
-            write(fd, &c, 4);
-            size_t Bpp = bytesPerPixel(f);
-            for (size_t y=0 ; y<h ; y++) {
-                write(fd, base, w*Bpp);
-                base = (void *)((char *)base + s*Bpp);
-            }
+    result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
+
+    if (base == NULL) {
+        close(fd);
+        _exit(1);
+    }
+
+    w = outBuffer->getWidth();
+    h = outBuffer->getHeight();
+    s = outBuffer->getStride();
+    f = outBuffer->getPixelFormat();
+    d = HAL_DATASPACE_UNKNOWN;
+    size = s * h * bytesPerPixel(f);
+
+    if (png) {
+        const SkImageInfo info =
+            SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, dataSpaceToColorSpace(d));
+        SkPixmap pixmap(info, base, s * bytesPerPixel(f));
+        struct FDWStream final : public SkWStream {
+          size_t fBytesWritten = 0;
+          int fFd;
+          FDWStream(int f) : fFd(f) {}
+          size_t bytesWritten() const override { return fBytesWritten; }
+          bool write(const void* buffer, size_t size) override {
+            fBytesWritten += size;
+            return size == 0 || ::write(fFd, buffer, size) > 0;
+          }
+        } fdStream(fd);
+        (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
+        if (fn != NULL) {
+            notifyMediaScanner(fn);
+        }
+    } else {
+        uint32_t c = dataSpaceToInt(d);
+        write(fd, &w, 4);
+        write(fd, &h, 4);
+        write(fd, &f, 4);
+        write(fd, &c, 4);
+        size_t Bpp = bytesPerPixel(f);
+        for (size_t y=0 ; y<h ; y++) {
+            write(fd, base, w*Bpp);
+            base = (void *)((char *)base + s*Bpp);
         }
     }
     close(fd);
@@ -253,4 +258,4 @@
 
     // b/36066697: Avoid running static destructors.
     _exit(0);
-}
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 2fd7947..c6b8418 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -84,10 +84,12 @@
 void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
     StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC);
     // pass the event to metrics managers.
-    for (auto& pair : mMetricsManagers) {
-        pair.second->onLogEvent(msg);
-        flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
-    }
+    // TODO: THIS CHECK FAILS BECAUSE ONCE UIDMAP SIZE EXCEEDS LIMIT, DROPPING METRICS DATA
+    // DOESN'T HELP. FIX THIS.
+    //for (auto& pair : mMetricsManagers) {
+    //    pair.second->onLogEvent(msg);
+    //    flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
+    //}
 
     // Hard-coded logic to update the isolated uid's in the uid-map.
     // The field numbers need to be currently updated by hand with atoms.proto
@@ -221,7 +223,14 @@
     std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
 
     size_t totalBytes = metricsManager->byteSize() + mUidMap->getBytesUsed();
-    if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data.
+    // TODO: Find a way to test that the dropping and broadcasts are sent when memory is exceeded.
+    if (totalBytes > kMaxSerializedBytes) {  // Too late. We need to start clearing data.
+        // We ignore the return value so we force each metric producer to clear its contents.
+        metricsManager->onDumpReport();
+        StatsdStats::getInstance().noteDataDropped(key);
+        VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
+    } else if (totalBytes >
+               .9 * kMaxSerializedBytes) {  // Send broadcast so that receivers can pull data.
         auto lastFlushNs = mLastBroadcastTimes.find(key);
         if (lastFlushNs != mLastBroadcastTimes.end()) {
             if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) {
@@ -232,11 +241,6 @@
         VLOG("StatsD requesting broadcast for %s", key.ToString().c_str());
         mSendBroadcast(key);
         StatsdStats::getInstance().noteBroadcastSent(key);
-    } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
-        // We ignore the return value so we force each metric producer to clear its contents.
-        metricsManager->onDumpReport();
-        StatsdStats::getInstance().noteDataDropped(key);
-        VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
     }
 }
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 9259793..fa7fe0c 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -551,7 +551,7 @@
     return NO_ERROR;
 }
 
-Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                       const vector<String16>& app) {
     VLOG("StatsService::informAllUidData was called");
 
@@ -566,7 +566,7 @@
     return Status::ok();
 }
 
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t version) {
+Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version) {
     VLOG("StatsService::informOnePackage was called");
 
     if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
@@ -715,6 +715,18 @@
     }
 }
 
+Status StatsService::getMetadata(vector<uint8_t>* output) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
+         ipc->getCallingUid());
+    if (checkCallingPermission(String16(kPermissionDump))) {
+        StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
+        return Status::ok();
+    } else {
+        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+    }
+}
+
 Status StatsService::addConfiguration(const String16& key,
                                       const vector <uint8_t>& config,
                                       const String16& package, const String16& cls,
@@ -731,6 +743,7 @@
         *success = true;
         return Status::ok();
     } else {
+        *success = false;
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
 }
@@ -740,6 +753,7 @@
     if (checkCallingPermission(String16(kPermissionDump))) {
         string keyStr = string(String8(key).string());
         mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr));
+        *success = true;
         return Status::ok();
     } else {
         *success = false;
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 007227e..bdae1ef 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -54,9 +54,9 @@
     virtual Status statsCompanionReady();
     virtual Status informAnomalyAlarmFired();
     virtual Status informPollAlarmFired();
-    virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+    virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                     const vector<String16>& app);
-    virtual Status informOnePackage(const String16& app, int32_t uid, int32_t version);
+    virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version);
     virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
     virtual Status writeDataToDisk();
 
@@ -76,6 +76,11 @@
     virtual Status getData(const String16& key, vector<uint8_t>* output) override;
 
     /**
+     * Binder call for clients to get metadata across all configs in statsd.
+     */
+    virtual Status getMetadata(vector<uint8_t>* output) override;
+
+    /**
      * Binder call to let clients send a configuration and indicate they're interested when they
      * should requestData for this configuration.
      */
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 4b7e49a..4b82e68 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -324,6 +324,16 @@
     durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
     durationMetric->set_what("SCREEN_IS_ON");
 
+    // Anomaly threshold for background count.
+    alert = config.add_alert();
+    alert->set_name("ALERT_8");
+    alert->set_metric_name("METRIC_8");
+    alert->set_number_of_buckets(4);
+    alert->set_trigger_if_sum_gt(2000000000); // 2 seconds
+    alert->set_refractory_period_secs(120);
+    details = alert->mutable_incidentd_details();
+    details->add_section(-1);
+
     // Value metric to count KERNEL_WAKELOCK when screen turned on
     ValueMetric* valueMetric = config.add_value_metric();
     valueMetric->set_name("METRIC_6");
@@ -348,69 +358,69 @@
     gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
 
     // Event matchers............
-    LogEntryMatcher* temperatureEntryMatcher = config.add_log_entry_matcher();
-    temperatureEntryMatcher->set_name("DEVICE_TEMPERATURE");
-    temperatureEntryMatcher->mutable_simple_log_entry_matcher()->set_tag(
+    AtomMatcher* temperatureAtomMatcher = config.add_atom_matcher();
+    temperatureAtomMatcher->set_name("DEVICE_TEMPERATURE");
+    temperatureAtomMatcher->mutable_simple_atom_matcher()->set_tag(
         DEVICE_TEMPERATURE_TAG_ID);
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_TURNED_ON");
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(SCREEN_EVENT_TAG_ID);
-    KeyValueMatcher* keyValueMatcher = simpleLogEntryMatcher->add_key_value_matcher();
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID);
+    KeyValueMatcher* keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
     keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY);
     keyValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_TURNED_OFF");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(SCREEN_EVENT_TAG_ID);
-    keyValueMatcher = simpleLogEntryMatcher->add_key_value_matcher();
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID);
+    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
     keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY);
     keyValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("PROCESS_STATE_CHANGE");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(UID_PROCESS_STATE_TAG_ID);
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(UID_PROCESS_STATE_TAG_ID);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_GOES_BACKGROUND");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(APP_USAGE_ID);
-    keyValueMatcher = simpleLogEntryMatcher->add_key_value_matcher();
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(APP_USAGE_ID);
+    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
     keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY);
     keyValueMatcher->set_eq_int(APP_USAGE_BACKGROUND);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_GOES_FOREGROUND");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(APP_USAGE_ID);
-    keyValueMatcher = simpleLogEntryMatcher->add_key_value_matcher();
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(APP_USAGE_ID);
+    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
     keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY);
     keyValueMatcher->set_eq_int(APP_USAGE_FOREGROUND);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_GET_WL");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(WAKE_LOCK_TAG_ID);
-    keyValueMatcher = simpleLogEntryMatcher->add_key_value_matcher();
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID);
+    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
     keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY);
     keyValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_RELEASE_WL");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(WAKE_LOCK_TAG_ID);
-    keyValueMatcher = simpleLogEntryMatcher->add_key_value_matcher();
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID);
+    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
     keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY);
     keyValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE);
 
     // pulled events
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("KERNEL_WAKELOCK");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID);
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID);
 
     // Conditions.............
     Condition* condition = config.add_condition();
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 8e96399..e1c02d7 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -45,10 +45,11 @@
     if (statsCompanion != NULL) {
         Status status = statsCompanion->pullData(tagId, &returned_value);
         if (!status.isOk()) {
-            ALOGW("error pulling kernel wakelock");
+            ALOGW("error pulling for %d", tagId);
             return false;
         }
         data->clear();
+        long timestamp = time(nullptr);
         for (const StatsLogEventWrapper& it : returned_value) {
             log_msg tmp;
             tmp.entry_v1.len = it.bytes.size();
@@ -56,9 +57,10 @@
             tmp.entry.hdr_size = kLogMsgHeaderSize;
             // And set the received bytes starting after the 28 bytes reserved for header.
             std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
+            tmp.entry_v1.sec = timestamp;
             data->push_back(make_shared<LogEvent>(tmp));
         }
-        ALOGD("KernelWakelockPuller::pull succeeded!");
+        ALOGD("StatsCompanionServicePuller::pull succeeded for %d", tagId);
         return true;
     } else {
         ALOGW("statsCompanion not found!");
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 3ffcd77..c4688a2 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -76,7 +76,9 @@
     if (DEBUG) ALOGD("Initiating pulling %d", tagId);
 
     if (mPullers.find(tagId) != mPullers.end()) {
-        return mPullers.find(tagId)->second->Pull(tagId, data);
+        bool ret = mPullers.find(tagId)->second->Pull(tagId, data);
+        ALOGD("pulled %d items", (int)data->size());
+        return ret;
     } else {
         ALOGD("Unknown tagId %d", tagId);
         return false;  // Return early since we don't know what to pull.
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
index 78ba762..51a38b6 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -36,7 +36,7 @@
 CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
 }
 
-bool CombinationLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
                                          const vector<sp<LogMatchingTracker>>& allTrackers,
                                          const unordered_map<string, int>& matcherMap,
                                          vector<bool>& stack) {
@@ -47,7 +47,7 @@
     // mark this node as visited in the recursion stack.
     stack[mIndex] = true;
 
-    LogEntryMatcher_Combination matcher = allLogMatchers[mIndex].combination();
+    AtomMatcher_Combination matcher = allLogMatchers[mIndex].combination();
 
     // LogicalOperation is missing in the config
     if (!matcher.has_operation()) {
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
index adb691e..81f6e80 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -28,12 +28,12 @@
 namespace os {
 namespace statsd {
 
-// Represents a LogEntryMatcher_Combination in the StatsdConfig.
+// Represents a AtomMatcher_Combination in the StatsdConfig.
 class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
 public:
     CombinationLogMatchingTracker(const std::string& name, const int index);
 
-    bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+    bool init(const std::vector<AtomMatcher>& allLogMatchers,
               const std::vector<sp<LogMatchingTracker>>& allTrackers,
               const std::unordered_map<std::string, int>& matcherMap,
               std::vector<bool>& stack);
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index fea3e9b..8162c44 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -39,14 +39,14 @@
     virtual ~LogMatchingTracker(){};
 
     // Initialize this LogMatchingTracker.
-    // allLogMatchers: the list of the LogEntryMatcher proto config. This is needed because we don't
+    // allLogMatchers: the list of the AtomMatcher proto config. This is needed because we don't
     //                 store the proto object in memory. We only need it during initilization.
     // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with
     //              allLogMatchers. This is needed because the initialization is done recursively
     //              for CombinationLogMatchingTrackers using DFS.
     // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
     //        circle dependency.
-    virtual bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+    virtual bool init(const std::vector<AtomMatcher>& allLogMatchers,
                       const std::vector<sp<LogMatchingTracker>>& allTrackers,
                       const std::unordered_map<std::string, int>& matcherMap,
                       std::vector<bool>& stack) = 0;
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
index ad37b01..ac217ab 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -30,7 +30,7 @@
 
 
 SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index,
-                                                   const SimpleLogEntryMatcher& matcher)
+                                                   const SimpleAtomMatcher& matcher)
     : LogMatchingTracker(name, index), mMatcher(matcher) {
     if (!matcher.has_tag()) {
         mInitialized = false;
@@ -43,7 +43,7 @@
 SimpleLogMatchingTracker::~SimpleLogMatchingTracker() {
 }
 
-bool SimpleLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+bool SimpleLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
                                     const vector<sp<LogMatchingTracker>>& allTrackers,
                                     const unordered_map<string, int>& matcherMap,
                                     vector<bool>& stack) {
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index 5dca55e..2c188c1 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -32,11 +32,11 @@
 class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
 public:
     SimpleLogMatchingTracker(const std::string& name, const int index,
-                             const SimpleLogEntryMatcher& matcher);
+                             const SimpleAtomMatcher& matcher);
 
     ~SimpleLogMatchingTracker();
 
-    bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+    bool init(const std::vector<AtomMatcher>& allLogMatchers,
               const std::vector<sp<LogMatchingTracker>>& allTrackers,
               const std::unordered_map<std::string, int>& matcherMap,
               std::vector<bool>& stack) override;
@@ -46,7 +46,7 @@
                     std::vector<MatchingState>& matcherResults) override;
 
 private:
-    const SimpleLogEntryMatcher mMatcher;
+    const SimpleAtomMatcher mMatcher;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index f7352cd..534d54e 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -91,7 +91,7 @@
     return matched;
 }
 
-bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& event) {
+bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& event) {
     const int tagId = event.GetTagId();
 
     if (simpleMatcher.tag() != tagId) {
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
index 4ea6f0b..f54ab36 100644
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -42,7 +42,7 @@
 bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
                       const std::vector<MatchingState>& matcherResults);
 
-bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& wrapper);
+bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
 
 std::vector<KeyValuePair> getDimensionKey(const LogEvent& event,
                                           const std::vector<KeyMatcher>& dimensions);
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index ce38f58..800a2b9 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -51,7 +51,8 @@
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 66e8c61..fd484c2 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -103,6 +103,17 @@
     VLOG("~DurationMetric() called");
 }
 
+sp<AnomalyTracker> DurationMetricProducer::createAnomalyTracker(const Alert &alert) {
+    if (alert.trigger_if_sum_gt() > alert.number_of_buckets() * mBucketSizeNs) {
+        ALOGW("invalid alert: threshold (%lld) > possible recordable value (%d x %lld)",
+              alert.trigger_if_sum_gt(), alert.number_of_buckets(),
+              (long long)mBucketSizeNs);
+        return nullptr;
+    }
+    // TODO: return a DurationAnomalyTracker (which should sublclass AnomalyTracker)
+    return new AnomalyTracker(alert);
+}
+
 void DurationMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
     mProto = std::make_unique<ProtoOutputStream>();
     mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
@@ -111,16 +122,16 @@
 }
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
-        const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const {
+        const HashableDimensionKey& eventKey) const {
     switch (mMetric.aggregation_type()) {
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
                     mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
-                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
                     mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
-                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
     }
 }
 
@@ -168,13 +179,6 @@
             continue;
         }
 
-        // If there is no duration bucket info for this key, don't include it in the report.
-        // For example, duration started, but condition is never turned to true.
-        // TODO: Only add the key to the map when we add duration buckets info for it.
-        if (pair.second.size() == 0) {
-            continue;
-        }
-
         long long wrapperToken =
                 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
@@ -217,7 +221,7 @@
                   (long long)mCurrentBucketStartTimeNs);
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked();
     startNewProtoOutputStreamLocked(endTime);
-    // TODO: Properly clear the old buckets.
+    mPastBuckets.clear();
     return buffer;
 }
 
@@ -227,7 +231,7 @@
     }
     VLOG("flushing...........");
     for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
-        if (it->second->flushIfNeeded(eventTime)) {
+        if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) {
             VLOG("erase bucket for key %s", it->first.c_str());
             it = mCurrentSlicedDuration.erase(it);
         } else {
@@ -279,7 +283,7 @@
         if (hitGuardRailLocked(eventKey)) {
             return;
         }
-        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
+        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey);
     }
 
     auto it = mCurrentSlicedDuration.find(eventKey);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 8e32d14..4bf9d1c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -45,10 +45,13 @@
 
     virtual ~DurationMetricProducer();
 
+    virtual sp<AnomalyTracker> createAnomalyTracker(const Alert &alert) override;
+
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
@@ -79,13 +82,13 @@
 
     const DurationMetric mMetric;
 
-    // Index of the SimpleLogEntryMatcher which defines the start.
+    // Index of the SimpleAtomMatcher which defines the start.
     const size_t mStartIndex;
 
-    // Index of the SimpleLogEntryMatcher which defines the stop.
+    // Index of the SimpleAtomMatcher which defines the stop.
     const size_t mStopIndex;
 
-    // Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
+    // Index of the SimpleAtomMatcher which defines the stop all for all dimensions.
     const size_t mStopAllIndex;
 
     // nest counting -- for the same key, stops must match the number of starts to make real stop
@@ -104,7 +107,7 @@
 
     // Helper function to create a duration tracker given the metric aggregation type.
     std::unique_ptr<DurationTracker> createDurationTracker(
-            const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const;
+            const HashableDimensionKey& eventKey) const;
 
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const HashableDimensionKey& newKey);
@@ -118,4 +121,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index afb48c4..da3b3ca 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -43,7 +43,8 @@
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 2284ff1..50cc8d4 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -99,6 +99,9 @@
 
 GaugeMetricProducer::~GaugeMetricProducer() {
     VLOG("~GaugeMetricProducer() called");
+    if (mPullTagId != -1) {
+        mStatsPullerManager.UnRegisterReceiver(mPullTagId, this);
+    }
 }
 
 void GaugeMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index c839c09..36705b1 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -59,7 +59,8 @@
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 5df712c..819df77 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -93,6 +93,10 @@
         return byteSizeLocked();
     }
 
+    virtual sp<AnomalyTracker> createAnomalyTracker(const Alert &alert) {
+        return new AnomalyTracker(alert);
+    }
+
     void addAnomalyTracker(sp<AnomalyTracker> tracker) {
         std::lock_guard<std::mutex> lock(mMutex);
         mAnomalyTrackers.push_back(tracker);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 8da8316a..9fdc6fa 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -39,7 +39,7 @@
 
 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) : mConfigKey(key) {
     mConfigValid =
-            initStatsdConfig(key, config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
+            initStatsdConfig(key, config, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
                              mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
                              mTrackerToMetricMap, mTrackerToConditionMap);
 
@@ -47,11 +47,11 @@
     // no matter whether this config is valid, log it in the stats.
     StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
                                                   mAllConditionTrackers.size(),
-                                                  mAllLogEntryMatchers.size(), 0, mConfigValid);
+                                                  mAllAtomMatchers.size(), 0, mConfigValid);
     // Guardrail. Reject the config if it's too big.
     if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
         mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
-        mAllLogEntryMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
+        mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
         ALOGE("This config is too big! Reject!");
         mConfigValid = false;
     }
@@ -95,10 +95,10 @@
         return;
     }
 
-    vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
+    vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed);
 
-    for (auto& matcher : mAllLogEntryMatchers) {
-        matcher->onLogEvent(event, mAllLogEntryMatchers, matcherCache);
+    for (auto& matcher : mAllAtomMatchers) {
+        matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
     }
 
     // A bitmap to see which ConditionTracker needs to be re-evaluated.
@@ -149,11 +149,11 @@
         }
     }
 
-    // For matched LogEntryMatchers, tell relevant metrics that a matched event has come.
-    for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) {
+    // For matched AtomMatchers, tell relevant metrics that a matched event has come.
+    for (size_t i = 0; i < mAllAtomMatchers.size(); i++) {
         if (matcherCache[i] == MatchingState::kMatched) {
             StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
-                                                          mAllLogEntryMatchers[i]->getName());
+                                                          mAllAtomMatchers[i]->getName());
             auto pair = mTrackerToMetricMap.find(i);
             if (pair != mTrackerToMetricMap.end()) {
                 auto& metricList = pair->second;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 62b4c87..4e6a0ce 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -71,8 +71,8 @@
     // holds A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get
     // the related results from a cache using the index.
 
-    // Hold all the log entry matchers from the config.
-    std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers;
+    // Hold all the atom matchers from the config.
+    std::vector<sp<LogMatchingTracker>> mAllAtomMatchers;
 
     // Hold all the conditions from the config.
     std::vector<sp<ConditionTracker>> mAllConditionTrackers;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 60b725d..a2efd3f 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -49,7 +49,8 @@
     void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 834f7f5..3c714b3 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -63,8 +63,7 @@
     DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey,
                     sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                     uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
-                    const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                    std::vector<DurationBucket>& bucket)
+                    const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
         : mConfigKey(key),
           mName(name),
           mEventKey(eventKey),
@@ -73,7 +72,6 @@
           mBucketSizeNs(bucketSizeNs),
           mNested(nesting),
           mCurrentBucketStartTimeNs(currentBucketStartNs),
-          mBucket(bucket),
           mDuration(0),
           mCurrentBucketNum(0),
           mAnomalyTrackers(anomalyTrackers){};
@@ -91,7 +89,9 @@
 
     // Flush stale buckets if needed, and return true if the tracker has no on-going duration
     // events, so that the owner can safely remove the tracker.
-    virtual bool flushIfNeeded(uint64_t timestampNs) = 0;
+    virtual bool flushIfNeeded(
+            uint64_t timestampNs,
+            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0;
 
     // Predict the anomaly timestamp given the current status.
     virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
@@ -159,8 +159,6 @@
 
     uint64_t mCurrentBucketStartTimeNs;
 
-    std::vector<DurationBucket>& mBucket;  // where to write output
-
     int64_t mDuration;  // current recorded duration result
 
     uint64_t mCurrentBucketNum;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 4b346dd..08c9135 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -28,10 +28,9 @@
                                        const HashableDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
-                                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                                       std::vector<DurationBucket>& bucket)
+                                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, anomalyTrackers, bucket) {
+                      bucketSizeNs, anomalyTrackers) {
 }
 
 bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -145,7 +144,8 @@
     }
 }
 
-bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) {
+bool MaxDurationTracker::flushIfNeeded(
+        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
         return false;
     }
@@ -202,7 +202,7 @@
 
     if (mDuration != 0) {
         info.mDuration = mDuration;
-        mBucket.push_back(info);
+        (*output)[mEventKey].push_back(info);
         addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
         VLOG("  final duration for last bucket: %lld", (long long)mDuration);
     }
@@ -215,7 +215,7 @@
             info.mBucketEndNs = endTime + mBucketSizeNs * i;
             info.mBucketNum = mCurrentBucketNum + i;
             info.mDuration = mBucketSizeNs;
-            mBucket.push_back(info);
+            (*output)[mEventKey].push_back(info);
             addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
             VLOG("  filling gap bucket with duration %lld", (long long)mBucketSizeNs);
         }
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index e0d1466..10eddb8 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -32,15 +32,16 @@
                        const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                        int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                        uint64_t bucketSizeNs,
-                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                       std::vector<DurationBucket>& bucket);
+                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
                   const bool stopAll) override;
     void noteStopAll(const uint64_t eventTime) override;
 
-    bool flushIfNeeded(uint64_t timestampNs) override;
+    bool flushIfNeeded(
+            uint64_t timestampNs,
+            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
 
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index abdfbc0..8122744 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -29,10 +29,9 @@
                                            sp<ConditionWizard> wizard, int conditionIndex,
                                            bool nesting, uint64_t currentBucketStartNs,
                                            uint64_t bucketSizeNs,
-                                           const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                                           std::vector<DurationBucket>& bucket)
+                                           const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, anomalyTrackers, bucket),
+                      bucketSizeNs, anomalyTrackers),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
@@ -128,7 +127,8 @@
     mConditionKeyMap.clear();
 }
 
-bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
+bool OringDurationTracker::flushIfNeeded(
+        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
     if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return false;
     }
@@ -145,7 +145,7 @@
     }
     if (mDuration > 0) {
         current_info.mDuration = mDuration;
-        mBucket.push_back(current_info);
+        (*output)[mEventKey].push_back(current_info);
         addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum);
         VLOG("  duration: %lld", (long long)current_info.mDuration);
     }
@@ -157,9 +157,9 @@
             info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
             info.mBucketNum = mCurrentBucketNum + i;
             info.mDuration = mBucketSizeNs;
-                mBucket.push_back(info);
-                addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
-                VLOG("  add filling bucket with duration %lld", (long long)info.mDuration);
+            (*output)[mEventKey].push_back(info);
+            addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
+            VLOG("  add filling bucket with duration %lld", (long long)info.mDuration);
         }
     }
     mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index a8404a9..b7d3cba 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -31,8 +31,7 @@
                          const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                          int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                          uint64_t bucketSizeNs,
-                         const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                         std::vector<DurationBucket>& bucket);
+                         const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
 
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
@@ -43,7 +42,9 @@
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
 
-    bool flushIfNeeded(uint64_t timestampNs) override;
+    bool flushIfNeeded(
+            uint64_t timestampNs,
+            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
 
     int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
                                       const uint64_t currentTimestamp) const override;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 33683f0..00048f5 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -40,17 +40,17 @@
 
 bool handleMetricWithLogTrackers(const string what, const int metricIndex,
                                  const bool usedForDimension,
-                                 const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                                 const vector<sp<LogMatchingTracker>>& allAtomMatchers,
                                  const unordered_map<string, int>& logTrackerMap,
                                  unordered_map<int, std::vector<int>>& trackerToMetricMap,
                                  int& logTrackerIndex) {
     auto logTrackerIt = logTrackerMap.find(what);
     if (logTrackerIt == logTrackerMap.end()) {
-        ALOGW("cannot find the LogEntryMatcher \"%s\" in config", what.c_str());
+        ALOGW("cannot find the AtomMatcher \"%s\" in config", what.c_str());
         return false;
     }
-    if (usedForDimension && allLogEntryMatchers[logTrackerIt->second]->getTagIds().size() > 1) {
-        ALOGE("LogEntryMatcher \"%s\" has more than one tag ids. When a metric has dimension, "
+    if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getTagIds().size() > 1) {
+        ALOGE("AtomMatcher \"%s\" has more than one tag ids. When a metric has dimension, "
               "the \"what\" can only about one atom type.",
               what.c_str());
         return false;
@@ -93,23 +93,23 @@
 }
 
 bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
-                     vector<sp<LogMatchingTracker>>& allLogEntryMatchers, set<int>& allTagIds) {
-    vector<LogEntryMatcher> matcherConfigs;
-    const int logEntryMatcherCount = config.log_entry_matcher_size();
-    matcherConfigs.reserve(logEntryMatcherCount);
-    allLogEntryMatchers.reserve(logEntryMatcherCount);
+                     vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
+    vector<AtomMatcher> matcherConfigs;
+    const int atomMatcherCount = config.atom_matcher_size();
+    matcherConfigs.reserve(atomMatcherCount);
+    allAtomMatchers.reserve(atomMatcherCount);
 
-    for (int i = 0; i < logEntryMatcherCount; i++) {
-        const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
+    for (int i = 0; i < atomMatcherCount; i++) {
+        const AtomMatcher& logMatcher = config.atom_matcher(i);
 
-        int index = allLogEntryMatchers.size();
+        int index = allAtomMatchers.size();
         switch (logMatcher.contents_case()) {
-            case LogEntryMatcher::ContentsCase::kSimpleLogEntryMatcher:
-                allLogEntryMatchers.push_back(new SimpleLogMatchingTracker(
-                        logMatcher.name(), index, logMatcher.simple_log_entry_matcher()));
+            case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
+                allAtomMatchers.push_back(new SimpleLogMatchingTracker(
+                        logMatcher.name(), index, logMatcher.simple_atom_matcher()));
                 break;
-            case LogEntryMatcher::ContentsCase::kCombination:
-                allLogEntryMatchers.push_back(
+            case AtomMatcher::ContentsCase::kCombination:
+                allAtomMatchers.push_back(
                         new CombinationLogMatchingTracker(logMatcher.name(), index));
                 break;
             default:
@@ -118,16 +118,16 @@
                 // continue;
         }
         if (logTrackerMap.find(logMatcher.name()) != logTrackerMap.end()) {
-            ALOGE("Duplicate LogEntryMatcher found!");
+            ALOGE("Duplicate AtomMatcher found!");
             return false;
         }
         logTrackerMap[logMatcher.name()] = index;
         matcherConfigs.push_back(logMatcher);
     }
 
-    vector<bool> stackTracker2(allLogEntryMatchers.size(), false);
-    for (auto& matcher : allLogEntryMatchers) {
-        if (!matcher->init(matcherConfigs, allLogEntryMatchers, logTrackerMap, stackTracker2)) {
+    vector<bool> stackTracker2(allAtomMatchers.size(), false);
+    for (auto& matcher : allAtomMatchers) {
+        if (!matcher->init(matcherConfigs, allAtomMatchers, logTrackerMap, stackTracker2)) {
             return false;
         }
         // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
@@ -191,7 +191,7 @@
 bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
                  const unordered_map<string, int>& logTrackerMap,
                  const unordered_map<string, int>& conditionTrackerMap,
-                 const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                 const vector<sp<LogMatchingTracker>>& allAtomMatchers,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
                  vector<sp<MetricProducer>>& allMetricProducers,
                  unordered_map<int, std::vector<int>>& conditionToMetricMap,
@@ -217,7 +217,7 @@
         metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
-                                         allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
+                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
                                          trackerIndex)) {
             return false;
         }
@@ -268,7 +268,7 @@
         int trackerIndices[3] = {-1, -1, -1};
         if (!simpleCondition.has_start() ||
             !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
-                                         metric.dimension_size() > 0, allLogEntryMatchers,
+                                         metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
             ALOGE("Duration metrics must specify a valid the start event matcher");
             return false;
@@ -276,14 +276,14 @@
 
         if (simpleCondition.has_stop() &&
             !handleMetricWithLogTrackers(simpleCondition.stop(), metricIndex,
-                                         metric.dimension_size() > 0, allLogEntryMatchers,
+                                         metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
             return false;
         }
 
         if (simpleCondition.has_stop_all() &&
             !handleMetricWithLogTrackers(simpleCondition.stop_all(), metricIndex,
-                                         metric.dimension_size() > 0, allLogEntryMatchers,
+                                         metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
             return false;
         }
@@ -325,7 +325,7 @@
             return false;
         }
         int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allLogEntryMatchers,
+        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndex)) {
             return false;
         }
@@ -363,12 +363,12 @@
         metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
-                                         allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
+                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
                                          trackerIndex)) {
             return false;
         }
 
-        sp<LogMatchingTracker> atomMatcher = allLogEntryMatchers.at(trackerIndex);
+        sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
         // If it is pulled atom, it should be simple matcher with one tagId.
         int pullTagId = -1;
         for (int tagId : atomMatcher->getTagIds()) {
@@ -412,12 +412,12 @@
         metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
-                                         allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
+                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
                                          trackerIndex)) {
             return false;
         }
 
-        sp<LogMatchingTracker> atomMatcher = allLogEntryMatchers.at(trackerIndex);
+        sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
         // If it is pulled atom, it should be simple matcher with one tagId.
         int pullTagId = -1;
         for (int tagId : atomMatcher->getTagIds()) {
@@ -468,26 +468,18 @@
             return false;
         }
         const int metricIndex = itr->second;
-        if (alert.trigger_if_sum_gt() >
-            (int64_t)alert.number_of_buckets() *
-                    allMetricProducers[metricIndex]->getBuckeSizeInNs()) {
-            ALOGW("invalid alert: threshold (%lld) > possible recordable value (%d x %lld)",
-                  alert.trigger_if_sum_gt(), alert.number_of_buckets(),
-                  (long long)allMetricProducers[metricIndex]->getBuckeSizeInNs());
-            return false;
+        sp<MetricProducer> metric = allMetricProducers[metricIndex];
+        sp<AnomalyTracker> anomalyTracker = metric->createAnomalyTracker(alert);
+        if (anomalyTracker != nullptr) {
+            metric->addAnomalyTracker(anomalyTracker);
+            allAnomalyTrackers.push_back(anomalyTracker);
         }
-
-        // TODO: Give each MetricProducer a method called createAnomalyTracker(alert), which
-        //       creates either an AnomalyTracker or a DurationAnomalyTracker and returns it.
-        sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
-        allMetricProducers[metricIndex]->addAnomalyTracker(anomalyTracker);
-        allAnomalyTrackers.push_back(anomalyTracker);
     }
     return true;
 }
 
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, set<int>& allTagIds,
-                      vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                      vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
@@ -498,7 +490,7 @@
     unordered_map<string, int> conditionTrackerMap;
     unordered_map<string, int> metricProducerMap;
 
-    if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) {
+    if (!initLogTrackers(config, logTrackerMap, allAtomMatchers, allTagIds)) {
         ALOGE("initLogMatchingTrackers failed");
         return false;
     }
@@ -510,7 +502,7 @@
         return false;
     }
 
-    if (!initMetrics(key, config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
+    if (!initMetrics(key, config, logTrackerMap, conditionTrackerMap, allAtomMatchers,
                      allConditionTrackers, allMetricProducers, conditionToMetricMap,
                      trackerToMetricMap, metricProducerMap)) {
         ALOGE("initMetricProducers failed");
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index e7cbd53..751d8df 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -40,11 +40,11 @@
 // [config]: the input StatsdConfig
 // output:
 // [logTrackerMap]: this map should contain matcher name to index mapping
-// [allLogEntryMatchers]: should store the sp to all the LogMatchingTracker
+// [allAtomMatchers]: should store the sp to all the LogMatchingTracker
 // [allTagIds]: contains the set of all interesting tag ids to this config.
 bool initLogTrackers(const StatsdConfig& config,
                      std::unordered_map<std::string, int>& logTrackerMap,
-                     std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                     std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                      std::set<int>& allTagIds);
 
 // Initialize ConditionTrackers
@@ -80,7 +80,7 @@
         const std::unordered_map<std::string, int>& logTrackerMap,
         const std::unordered_map<std::string, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
-        const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+        const vector<sp<LogMatchingTracker>>& allAtomMatchers,
         vector<sp<ConditionTracker>>& allConditionTrackers,
         std::vector<sp<MetricProducer>>& allMetricProducers,
         std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
@@ -89,7 +89,7 @@
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, std::set<int>& allTagIds,
-                      std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+                      std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h
index 5aa3db5..bc8b0de 100644
--- a/cmds/statsd/src/packages/PackageInfoListener.h
+++ b/cmds/statsd/src/packages/PackageInfoListener.h
@@ -28,7 +28,7 @@
 public:
     // Uid map will notify this listener that the app with apk name and uid has been upgraded to
     // the specified version.
-    virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0;
+    virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int64_t version) = 0;
 
     // Notify interested listeners that the given apk and uid combination no longer exits.
     virtual void notifyAppRemoved(const std::string& apk, const int uid) = 0;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index db592e2..6e7a613 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -23,6 +23,8 @@
 #include <binder/IServiceManager.h>
 #include <utils/Errors.h>
 
+#include <inttypes.h>
+
 using namespace android;
 
 namespace android {
@@ -46,7 +48,7 @@
     return false;
 }
 
-int UidMap::getAppVersion(int uid, const string& packageName) const {
+int64_t UidMap::getAppVersion(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
     auto range = mMap.equal_range(uid);
@@ -58,13 +60,13 @@
     return 0;
 }
 
-void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
+void UidMap::updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode,
                        const vector<String16>& packageName) {
     updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName);
 }
 
 void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
-                       const vector<int32_t>& versionCode, const vector<String16>& packageName) {
+                       const vector<int64_t>& versionCode, const vector<String16>& packageName) {
     lock_guard<mutex> lock(mMutex);  // Exclusively lock for updates.
 
     mMap.clear();
@@ -87,12 +89,12 @@
     ensureBytesUsedBelowLimit();
 }
 
-void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
+void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int64_t& versionCode) {
     updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode);
 }
 
 void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
-                       const int32_t& versionCode) {
+                       const int64_t& versionCode) {
     lock_guard<mutex> lock(mMutex);
 
     string app = string(String8(app_16).string());
@@ -116,7 +118,7 @@
     auto range = mMap.equal_range(int(uid));
     for (auto it = range.first; it != range.second; ++it) {
         if (it->second.packageName == app) {
-            it->second.versionCode = int(versionCode);
+            it->second.versionCode = versionCode;
             return;
         }
         VLOG("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid);
@@ -124,7 +126,7 @@
     }
 
     // Otherwise, we need to add an app at this uid.
-    mMap.insert(make_pair(uid, AppData(app, int(versionCode))));
+    mMap.insert(make_pair(uid, AppData(app, versionCode)));
 }
 
 void UidMap::ensureBytesUsedBelowLimit() {
@@ -298,8 +300,8 @@
     lock_guard<mutex> lock(mMutex);
 
     for (auto it : mMap) {
-        fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode,
-                it.first);
+        fprintf(out, "%s, v%" PRId64 " (%i)\n", it.second.packageName.c_str(),
+                it.second.versionCode, it.first);
     }
 }
 
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index d2971c9..9e1ad69 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -41,9 +41,9 @@
 
 struct AppData {
     const string packageName;
-    int versionCode;
+    int64_t versionCode;
 
-    AppData(const string& a, const int v) : packageName(a), versionCode(v){};
+    AppData(const string& a, const int64_t v) : packageName(a), versionCode(v){};
 };
 
 // UidMap keeps track of what the corresponding app name (APK name) and version code for every uid
@@ -57,16 +57,16 @@
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
      */
-    void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
+    void updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode,
                    const vector<String16>& packageName);
 
-    void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
+    void updateApp(const String16& packageName, const int32_t& uid, const int64_t& versionCode);
     void removeApp(const String16& packageName, const int32_t& uid);
 
     // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
     bool hasApp(int uid, const string& packageName) const;
 
-    int getAppVersion(int uid, const string& packageName) const;
+    int64_t getAppVersion(int uid, const string& packageName) const;
 
     // Helper for debugging contents of this uid map. Can be triggered with:
     // adb shell cmd stats print-uid-map
@@ -104,10 +104,10 @@
 
 private:
     void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
-                   const vector<int32_t>& versionCode, const vector<String16>& packageName);
+                   const vector<int64_t>& versionCode, const vector<String16>& packageName);
 
     void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
-                   const int32_t& versionCode);
+                   const int64_t& versionCode);
     void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
 
     UidMapping getOutput(const int64_t& timestamp, const ConfigKey& key);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index cc8a26d..60d9a3d 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -102,7 +102,7 @@
     message PackageInfo {
       optional string name = 1;
 
-      optional int32 version = 2;
+      optional int64 version = 2;
 
       optional int32 uid = 3;
     }
@@ -119,7 +119,7 @@
     optional string app = 3;
     optional int32 uid = 4;
 
-    optional int32 version = 5;
+    optional int64 version = 5;
   }
   repeated Change changes = 2;
 }
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c8fa155..6837be0 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -23,93 +23,93 @@
 option java_outer_classname = "StatsdConfigProto";
 
 message KeyMatcher {
-  optional int32 key = 1;
+    optional int32 key = 1;
 
-  optional bool as_package_name = 2 [ default = false ];
+    optional bool as_package_name = 2 [default = false];
 }
 
 message KeyValueMatcher {
-  optional KeyMatcher key_matcher = 1;
+    optional KeyMatcher key_matcher = 1;
 
-  oneof value_matcher {
-    bool eq_bool = 2;
-    string eq_string = 3;
-    int32 eq_int = 4;
+    oneof value_matcher {
+        bool eq_bool = 2;
+        string eq_string = 3;
+        int32 eq_int = 4;
 
-    int64 lt_int = 5;
-    int64 gt_int = 6;
-    float lt_float = 7;
-    float gt_float = 8;
+        int64 lt_int = 5;
+        int64 gt_int = 6;
+        float lt_float = 7;
+        float gt_float = 8;
 
-    int64 lte_int = 9;
-    int64 gte_int = 10;
-  }
+        int64 lte_int = 9;
+        int64 gte_int = 10;
+    }
 }
 
 enum LogicalOperation {
-  LOGICAL_OPERATION_UNSPECIFIED = 0;
-  AND = 1;
-  OR = 2;
-  NOT = 3;
-  NAND = 4;
-  NOR = 5;
+    LOGICAL_OPERATION_UNSPECIFIED = 0;
+    AND = 1;
+    OR = 2;
+    NOT = 3;
+    NAND = 4;
+    NOR = 5;
 }
 
-message SimpleLogEntryMatcher {
-  optional int32 tag = 1;
+message SimpleAtomMatcher {
+    optional int32 tag = 1;
 
-  repeated KeyValueMatcher key_value_matcher = 2;
+    repeated KeyValueMatcher key_value_matcher = 2;
 }
 
-message LogEntryMatcher {
-  optional string name = 1;
+message AtomMatcher {
+    optional string name = 1;
 
-  message Combination {
-    optional LogicalOperation operation = 1;
+    message Combination {
+        optional LogicalOperation operation = 1;
 
-    repeated string matcher = 2;
-  }
-  oneof contents {
-    SimpleLogEntryMatcher simple_log_entry_matcher = 2;
-    Combination combination = 3;
-  }
+        repeated string matcher = 2;
+    }
+    oneof contents {
+        SimpleAtomMatcher simple_atom_matcher = 2;
+        Combination combination = 3;
+    }
 }
 
 message SimpleCondition {
-  optional string start = 1;
+    optional string start = 1;
 
-  optional string stop = 2;
+    optional string stop = 2;
 
-  optional bool count_nesting = 3 [default = true];
+    optional bool count_nesting = 3 [default = true];
 
-  optional string stop_all = 4;
+    optional string stop_all = 4;
 
-  enum InitialValue {
-    UNKNOWN = 0;
-    FALSE = 1;
-  }
-  optional InitialValue initial_value = 5 [default = FALSE];
+    enum InitialValue {
+        UNKNOWN = 0;
+        FALSE = 1;
+    }
+    optional InitialValue initial_value = 5 [default = FALSE];
 
-  repeated KeyMatcher dimension = 6;
+    repeated KeyMatcher dimension = 6;
 }
 
 message Condition {
-  optional string name = 1;
+    optional string name = 1;
 
-  message Combination {
-    optional LogicalOperation operation = 1;
+    message Combination {
+        optional LogicalOperation operation = 1;
 
-    repeated string condition = 2;
-  }
+        repeated string condition = 2;
+    }
 
-  oneof contents {
-    SimpleCondition simple_condition = 2;
-    Combination combination = 3;
-  }
+    oneof contents {
+        SimpleCondition simple_condition = 2;
+        Combination combination = 3;
+    }
 }
 
 message Bucket {
-  optional int64 bucket_size_millis = 1;
+    optional int64 bucket_size_millis = 1;
 }
 
 message EventConditionLink {
@@ -154,9 +154,9 @@
     repeated EventConditionLink links = 4;
 
     enum AggregationType {
-      SUM = 1;
+        SUM = 1;
 
-      MAX_SPARSE = 2;
+        MAX_SPARSE = 2;
     }
     optional AggregationType aggregation_type = 5 [default = SUM];
 
@@ -196,9 +196,7 @@
 
     repeated EventConditionLink links = 7;
 
-    enum AggregationType {
-      SUM = 1;
-    }
+    enum AggregationType { SUM = 1; }
     optional AggregationType aggregation_type = 8 [default = SUM];
 }
 
@@ -208,7 +206,7 @@
     optional string metric_name = 2;
 
     message IncidentdDetails {
-      repeated int32 section = 1;
+        repeated int32 section = 1;
     }
     optional IncidentdDetails incidentd_details = 3;
 
@@ -232,7 +230,7 @@
 
     repeated DurationMetric duration_metric = 6;
 
-    repeated LogEntryMatcher log_entry_matcher = 7;
+    repeated AtomMatcher atom_matcher = 7;
 
     repeated Condition condition = 8;
 
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 40c0e9d..1ec9155 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -36,10 +36,10 @@
 extern "C" void android_log_rewind(android_log_context ctx);
 
 #ifdef __ANDROID__
-TEST(LogEntryMatcherTest, TestSimpleMatcher) {
+TEST(AtomMatcherTest, TestSimpleMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
     simpleMatcher->set_tag(TAG_ID);
 
     LogEvent event(TAG_ID, 0);
@@ -49,10 +49,10 @@
     EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
 }
 
-TEST(LogEntryMatcherTest, TestBoolMatcher) {
+TEST(AtomMatcherTest, TestBoolMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
     simpleMatcher->set_tag(TAG_ID);
     auto keyValue1 = simpleMatcher->add_key_value_matcher();
     keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
@@ -84,10 +84,10 @@
     EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
 }
 
-TEST(LogEntryMatcherTest, TestStringMatcher) {
+TEST(AtomMatcherTest, TestStringMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
     simpleMatcher->set_tag(TAG_ID);
     auto keyValue = simpleMatcher->add_key_value_matcher();
     keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
@@ -103,10 +103,10 @@
     EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
 }
 
-TEST(LogEntryMatcherTest, TestMultiFieldsMatcher) {
+TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
     simpleMatcher->set_tag(TAG_ID);
     auto keyValue1 = simpleMatcher->add_key_value_matcher();
     keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
@@ -135,10 +135,10 @@
     EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
 }
 
-TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
+TEST(AtomMatcherTest, TestIntComparisonMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
 
     simpleMatcher->set_tag(TAG_ID);
     auto keyValue = simpleMatcher->add_key_value_matcher();
@@ -192,10 +192,10 @@
     EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
 }
 
-TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
+TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
     // Set up the matcher
-    LogEntryMatcher matcher;
-    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
     simpleMatcher->set_tag(TAG_ID);
 
     auto keyValue = simpleMatcher->add_key_value_matcher();
@@ -225,14 +225,14 @@
 }
 
 // Helper for the composite matchers.
-void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, int val) {
+void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) {
     simpleMatcher->set_tag(tag);
     auto keyValue = simpleMatcher->add_key_value_matcher();
     keyValue->mutable_key_matcher()->set_key(key);
     keyValue->set_eq_int(val);
 }
 
-TEST(LogEntryMatcherTest, TestAndMatcher) {
+TEST(AtomMatcherTest, TestAndMatcher) {
     // Set up the matcher
     LogicalOperation operation = LogicalOperation::AND;
 
@@ -256,7 +256,7 @@
     EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
 }
 
-TEST(LogEntryMatcherTest, TestOrMatcher) {
+TEST(AtomMatcherTest, TestOrMatcher) {
     // Set up the matcher
     LogicalOperation operation = LogicalOperation::OR;
 
@@ -280,7 +280,7 @@
     EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
 }
 
-TEST(LogEntryMatcherTest, TestNotMatcher) {
+TEST(AtomMatcherTest, TestNotMatcher) {
     // Set up the matcher
     LogicalOperation operation = LogicalOperation::NOT;
 
@@ -297,7 +297,7 @@
     EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
 }
 
-TEST(LogEntryMatcherTest, TestNandMatcher) {
+TEST(AtomMatcherTest, TestNandMatcher) {
     // Set up the matcher
     LogicalOperation operation = LogicalOperation::NAND;
 
@@ -322,7 +322,7 @@
     EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
 }
 
-TEST(LogEntryMatcherTest, TestNorMatcher) {
+TEST(AtomMatcherTest, TestNorMatcher) {
     // Set up the matcher
     LogicalOperation operation = LogicalOperation::NOR;
 
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 5384e0c..e4750e9 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -46,30 +46,30 @@
     StatsdConfig config;
     config.set_name("12345");
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
 
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_OFF");
 
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_ON_OR_OFF");
 
-    LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
     combination->add_matcher("SCREEN_IS_ON");
     combination->add_matcher("SCREEN_IS_OFF");
@@ -94,20 +94,20 @@
     StatsdConfig config;
     config.set_name("12345");
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
 
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_ON_OR_OFF");
 
-    LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
     combination->add_matcher("SCREEN_IS_ON");
     // Circle dependency
@@ -120,7 +120,7 @@
     StatsdConfig config;
     config.set_name("12345");
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
 
     CountMetric* metric = config.add_count_metric();
@@ -143,20 +143,20 @@
     StatsdConfig config;
     config.set_name("12345");
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
 
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_ON_OR_OFF");
 
-    LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
     combination->add_matcher("SCREEN_IS_ON");
     // undefined matcher
@@ -175,11 +175,11 @@
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     metric->set_condition("SOME_CONDITION");
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_EVENT");
 
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2);
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2);
 
     return config;
 }
@@ -188,20 +188,20 @@
     StatsdConfig config;
     config.set_name("12345");
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("BATTERY_VERY_LOW");
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2);
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("BATTERY_VERY_VERY_LOW");
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(3);
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(3);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("BATTERY_LOW");
 
-    LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
     combination->add_matcher("BATTERY_VERY_LOW");
     combination->add_matcher("BATTERY_VERY_VERY_LOW");
@@ -227,24 +227,24 @@
     StatsdConfig config;
     config.set_name("12345");
 
-    LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
 
-    SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
-    eventMatcher = config.add_log_entry_matcher();
+    eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_OFF");
 
-    simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
-    simpleLogEntryMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
     auto condition = config.add_condition();
@@ -267,7 +267,7 @@
 TEST(MetricsManagerTest, TestGoodConfig) {
     StatsdConfig config = buildGoodConfig();
     set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
@@ -275,7 +275,7 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
     EXPECT_EQ(1u, allMetricProducers.size());
@@ -285,7 +285,7 @@
 TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
     StatsdConfig config = buildDimensionMetricsWithMultiTags();
     set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
@@ -293,7 +293,7 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
@@ -301,7 +301,7 @@
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
     StatsdConfig config = buildCircleMatchers();
     set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
@@ -309,7 +309,7 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
@@ -317,14 +317,14 @@
 TEST(MetricsManagerTest, TestMissingMatchers) {
     StatsdConfig config = buildMissingMatchers();
     set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
@@ -332,14 +332,14 @@
 TEST(MetricsManagerTest, TestMissingCondition) {
     StatsdConfig config = buildMissingCondition();
     set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
@@ -347,7 +347,7 @@
 TEST(MetricsManagerTest, TestCircleConditionDependency) {
     StatsdConfig config = buildCircleConditions();
     set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
@@ -355,7 +355,7 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
@@ -363,7 +363,7 @@
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
     StatsdConfig config = buildAlertWithUnknownMetric();
     set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+    vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
@@ -371,7 +371,7 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index aa194e6..5b2cedd 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -61,7 +61,7 @@
 TEST(UidMapTest, TestMatching) {
     UidMap m;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
 
     uids.push_back(1000);
@@ -79,7 +79,7 @@
 TEST(UidMapTest, TestAddAndRemove) {
     UidMap m;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
 
     uids.push_back(1000);
@@ -107,7 +107,7 @@
     m.OnConfigUpdated(config1);
 
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
     uids.push_back(1000);
     uids.push_back(1000);
@@ -161,7 +161,7 @@
 
     size_t startBytes = m.mBytesUsed;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
     uids.push_back(1000);
     apps.push_back(String16(kApp1.c_str()));
@@ -191,7 +191,7 @@
 
     size_t startBytes = m.mBytesUsed;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
     for (int i = 0; i < 100; i++) {
         uids.push_back(1);
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 3f2b7cd..3158c27 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -89,7 +89,7 @@
     LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
 
     DurationMetricProducer durationProducer(
-            kConfigKey, metric, 0 /*no condition*/, 1 /* start index */, 2 /* stop index */,
+            kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs);
     EXPECT_FALSE(durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -97,12 +97,7 @@
     durationProducer.onMatchedLogEvent(1 /* start index*/, event1, false /* scheduledPull */);
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2, false /* scheduledPull */);
     durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
-                durationProducer.mPastBuckets.end());
-    const auto& buckets1 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
-    EXPECT_EQ(0UL, buckets1.size());
-
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
 
     durationProducer.onMatchedLogEvent(1 /* start index*/, event3, false /* scheduledPull */);
     durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 5d47437..1adcc11 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -37,18 +37,19 @@
 namespace statsd {
 
 const ConfigKey kConfigKey(0, "test");
+const string eventKey = "event";
 
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     tracker.noteStart("1", true, bucketStartTimeNs, key1);
     // Event starts again. This would not change anything as it already starts.
@@ -60,50 +61,53 @@
     tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
     tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(20ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(20ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestStopAll) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
 
     // Another event starts in this bucket.
     tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets);
     tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
     EXPECT_TRUE(tracker.mInfos.empty());
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40);
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(40ULL, buckets[1].mDuration);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets);
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(40ULL, buckets[eventKey][1].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     // The event starts.
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -112,25 +116,26 @@
     tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1);
 
     // The event stops at early 4th bucket.
-    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20);
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets);
     tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/);
-    EXPECT_EQ(3u, buckets.size());
-    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
-    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
-    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[2].mDuration);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(3u, buckets[eventKey].size());
+    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[eventKey][0].mDuration);
+    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][1].mDuration);
+    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][2].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     // 2 starts
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -138,20 +143,21 @@
     // one stop
     tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
+    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets);
 
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
     // real stop now.
     tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
-    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
 
-    EXPECT_EQ(3u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
-    EXPECT_EQ(5ULL, buckets[2].mDuration);
+    EXPECT_EQ(3u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
+    EXPECT_EQ(5ULL, buckets[eventKey][2].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
@@ -163,15 +169,15 @@
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
@@ -180,9 +186,10 @@
 
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(5ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
@@ -193,7 +200,7 @@
     alert.set_number_of_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
@@ -202,8 +209,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, {anomalyTracker}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {anomalyTracker});
 
     tracker.noteStart("1", true, eventStartTimeNs, key1);
     tracker.noteStop("1", eventStartTimeNs + 10, false);
@@ -211,7 +218,7 @@
     EXPECT_EQ(10LL, tracker.mDuration);
 
     tracker.noteStart("2", true, eventStartTimeNs + 20, key1);
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets);
     tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
     EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
     EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs,
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 6913c81..fa7b9a7 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -35,6 +35,7 @@
 namespace statsd {
 
 const ConfigKey kConfigKey(0, "test");
+const string eventKey = "event";
 
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -42,15 +43,15 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+                                 bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -58,9 +59,11 @@
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
 
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
-    tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
+    tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationNested) {
@@ -69,14 +72,14 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
@@ -84,9 +87,10 @@
     tracker.noteStop("2:maps", eventStartTimeNs + 2000, false);
     tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(2003ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestStopAll) {
@@ -95,23 +99,24 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
 
     tracker.noteStopAll(eventStartTimeNs + 2003);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(2003ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
@@ -120,32 +125,33 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1);
     EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
 
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
     tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false);
     tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false);
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12);
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
@@ -157,15 +163,15 @@
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+                                 bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
@@ -173,9 +179,10 @@
 
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(5ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
@@ -189,15 +196,15 @@
             .WillOnce(Return(ConditionState::kFalse))
             .WillOnce(Return(ConditionState::kTrue));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+                                 bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
@@ -207,9 +214,10 @@
     // 2nd duration: 1000ns
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(1005ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(1005ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
@@ -221,14 +229,14 @@
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
@@ -239,9 +247,10 @@
 
     tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(15ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(15ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
@@ -252,7 +261,7 @@
     alert.set_number_of_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
@@ -261,8 +270,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {anomalyTracker}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {anomalyTracker});
 
     // Nothing in the past bucket.
     tracker.noteStart("", true, eventStartTimeNs, key1);
@@ -270,7 +279,7 @@
               tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
 
     tracker.noteStop("", eventStartTimeNs + 3, false);
-    EXPECT_EQ(0u, buckets.size());
+    EXPECT_EQ(0u, buckets[eventKey].size());
 
     uint64_t event1StartTimeNs = eventStartTimeNs + 10;
     tracker.noteStart("1", true, event1StartTimeNs, key1);
@@ -279,11 +288,13 @@
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
 
     uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
-    tracker.flushIfNeeded(event1StopTimeNs);
+    tracker.flushIfNeeded(event1StopTimeNs, &buckets);
     tracker.noteStop("1", event1StopTimeNs, false);
-    EXPECT_EQ(1u, buckets.size());
+
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
     EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
-              buckets[0].mDuration);
+              buckets[eventKey][0].mDuration);
 
     const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
     const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
@@ -312,7 +323,7 @@
     alert.set_number_of_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
@@ -321,8 +332,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/,
-                                 bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/,
+                                 bucketStartTimeNs, bucketSizeNs, {anomalyTracker});
 
     tracker.noteStart("", true, eventStartTimeNs, key1);
     tracker.noteStop("", eventStartTimeNs + 10, false);
@@ -336,7 +347,7 @@
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
     EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
               (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
     tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false);
     EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs));
     EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
new file mode 100644
index 0000000..f3f0a7c
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+#
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SRC_FILES += ../../src/stats_log.proto \
+                   ../../src/atoms_copy.proto \
+                   ../../src/statsd_config.proto
+
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := StatsdLoadtest
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_DEX_PREOPT := false
+include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/loadtest/AndroidManifest.xml b/cmds/statsd/tools/loadtest/AndroidManifest.xml
new file mode 100644
index 0000000..d74c707
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.statsd.loadtest"
+    android:sharedUserId="android.uid.system"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+  <uses-permission android:name="android.permission.DUMP" />
+  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+  <application
+      android:allowBackup="true"
+      android:icon="@drawable/ic_launcher"
+      android:label="@string/app_name" >
+    <activity
+        android:name=".LoadtestActivity"
+        android:label="@string/app_name"
+        android:launchMode="singleTop" >
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+    <receiver android:name=".LoadtestActivity$PusherAlarmReceiver" />
+    <receiver android:name=".LoadtestActivity$StopperAlarmReceiver"/>
+  </application>
+</manifest>
diff --git a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..55621cc
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..11ec206
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..7c02b78
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..915d914
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
new file mode 100644
index 0000000..82964ab
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <LinearLayout
+        android:id="@+id/outside"
+        android:clickable="true"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:gravity="center"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginRight="10dp"
+        android:layout_marginLeft="10dp"
+        android:orientation="vertical">
+      <requestFocus />
+
+        <LinearLayout
+            android:gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+            <TextView
+                android:textSize="30dp"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/replication_label" />
+            <EditText
+                android:id="@+id/replication"
+                android:inputType="number"
+                android:layout_weight="1"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLength="3"
+                android:text="@integer/replication_default"
+                android:textSize="30dp"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+            <TextView
+                android:textSize="30dp"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/bucket_label" />
+            <EditText
+                android:id="@+id/bucket"
+                android:inputType="number"
+                android:layout_weight="1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:maxLength="3"
+                android:text="@integer/bucket_default"
+                android:textSize="30dp"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+            <TextView
+                android:textSize="30dp"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/period_label" />
+            <EditText
+                android:id="@+id/period"
+                android:inputType="number"
+                android:layout_weight="1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:maxLength="3"
+                android:text="@integer/period_default"
+                android:textSize="30dp"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+            <TextView
+                android:textSize="30dp"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/burst_label" />
+            <EditText
+                android:id="@+id/burst"
+                android:inputType="number"
+                android:layout_weight="1"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLength="2"
+                android:text="@integer/burst_default"
+                android:textSize="30dp"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+            <TextView
+                android:textSize="30dp"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/duration_label" />
+            <EditText
+                android:id="@+id/duration"
+                android:inputType="number"
+                android:layout_weight="1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:maxLength="4"
+                android:text="@integer/duration_default"
+                android:textSize="30dp"/>
+        </LinearLayout>
+	<CheckBox
+            android:id="@+id/placebo"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/placebo"
+            android:checked="false" />
+
+        <Space
+            android:layout_width="1dp"
+            android:layout_height="30dp"/>
+
+        <Button
+            android:id="@+id/start_stop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="#ffff0000"
+            android:text="@string/start"
+            android:textSize="50dp"/>
+
+        <Space
+            android:layout_width="1dp"
+            android:layout_height="30dp"/>
+
+        <Button
+            android:id="@+id/display_output"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/display_output"
+            android:textSize="30dp"/>
+        <Button
+            android:id="@+id/display_perf"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/display_perf"
+            android:textSize="30dp"/>
+
+        <Space
+            android:layout_width="1dp"
+            android:layout_height="30dp"/>
+
+        <TextView
+            android:id="@+id/report_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/cmds/statsd/tools/loadtest/res/raw/loadtest_config b/cmds/statsd/tools/loadtest/res/raw/loadtest_config
new file mode 100755
index 0000000..78223674
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/raw/loadtest_config
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/values/integers.xml b/cmds/statsd/tools/loadtest/res/values/integers.xml
new file mode 100644
index 0000000..76b5692
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/values/integers.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <integer name="bucket_default">10</integer>
+    <integer name="burst_default">1</integer>
+    <integer name="period_default">2</integer>
+    <integer name="replication_default">1</integer>
+    <integer name="duration_default">240</integer>
+</resources>
diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml
new file mode 100644
index 0000000..9913503
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/res/values/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <string name="app_name">Statsd Loadtest</string>
+    <string name="bucket_label">bucket size (mins):&#160;</string>
+    <string name="burst_label">burst:&#160;</string>
+    <string name="display_output">Show metrics data</string>
+    <string name="display_perf">Show perf data</string>
+    <string name="placebo">placebo</string>
+    <string name="period_label">logging period (secs):&#160;</string>
+    <string name="replication_label">metric replication:&#160;</string>
+    <string name="duration_label">test duration (mins):&#160;</string>
+    <string name="start"> &#160;Start&#160; </string>
+    <string name="stop"> &#160;Stop&#160; </string>
+
+</resources>
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
new file mode 100644
index 0000000..96e6bef
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.util.Log;
+import java.text.ParseException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class BatteryStatsParser {
+
+    private static final Pattern LINE_PATTERN =
+        Pattern.compile("\\s*\\+*(\\S*)\\s\\(\\d+\\)\\s(\\d\\d\\d)\\s.*");
+    private static final Pattern TIME_PATTERN =
+        Pattern.compile("(\\d+)?(h)?(\\d+)?(m)?(\\d+)?(s)?(\\d+)?(ms)?");
+    private static final String TAG = "BatteryStatsParser";
+
+    private boolean mHistoryStarted;
+    private boolean mHistoryEnded;
+
+    public BatteryStatsParser() {
+    }
+
+    @Nullable
+    public String parseLine(String line) {
+        if (mHistoryEnded) {
+            return null;
+        }
+        if (!mHistoryStarted) {
+            if (line.contains("Battery History")) {
+                mHistoryStarted = true;
+            }
+            return null;
+        }
+        if (line.isEmpty()) {
+            mHistoryEnded = true;
+            return null;
+        }
+        Matcher lineMatcher = LINE_PATTERN.matcher(line);
+        if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
+            if (lineMatcher.group(1).equals("0")) {
+                return "0," + lineMatcher.group(2) + "\n";
+            } else {
+                Matcher timeMatcher = TIME_PATTERN.matcher(lineMatcher.group(1));
+                if (timeMatcher.find()) {
+                    Long time = getTime(lineMatcher.group(1));
+                    if (time != null) {
+                        return time + "," + lineMatcher.group(2) + "\n";
+                      } else {
+                        return null; // bad time
+                    }
+                } else {
+                  return null;  // bad or no time
+                }
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    private Long getTime(String group) {
+        if ("0".equals(group)) {
+            return 0L;
+        }
+        Matcher timeMatcher = TIME_PATTERN.matcher(group);
+        if (!timeMatcher.find()) {
+            return null;
+        }
+
+        // Get rid of "ms".
+        String[] matches = group.split("ms", -1);
+        if (matches.length > 1) {
+            group = matches[0];
+        }
+
+        long time = 0L;
+        matches = group.split("h");
+        if (matches.length > 1) {
+            time += Long.parseLong(matches[0]) * 60 * 60 * 1000;  // hours
+            group = matches[1];
+        }
+        matches = group.split("m");
+        if (matches.length > 1) {
+            time += Long.parseLong(matches[0]) * 60 * 1000;  // minutes
+            group = matches[1];
+        }
+        matches = group.split("s");
+        if (matches.length > 1) {
+            time += Long.parseLong(matches[0]) * 1000; // seconds
+            group = matches[1];
+        }
+
+        if (!group.isEmpty()) {
+            time += Long.parseLong(group); // milliseconds
+        }
+        return time;
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
new file mode 100644
index 0000000..e7bb539
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+import android.util.StatsLog;
+
+import com.android.internal.os.StatsdConfigProto.Bucket;
+import com.android.internal.os.StatsdConfigProto.Condition;
+import com.android.internal.os.StatsdConfigProto.CountMetric;
+import com.android.internal.os.StatsdConfigProto.DurationMetric;
+import com.android.internal.os.StatsdConfigProto.EventConditionLink;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.ValueMetric;
+import com.android.internal.os.StatsdConfigProto.KeyMatcher;
+import com.android.internal.os.StatsdConfigProto.KeyValueMatcher;
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleCondition;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Creates StatsdConfig protos for loadtesting.
+ */
+public class ConfigFactory {
+    public static final String CONFIG_NAME = "LOADTEST";
+
+    private static final String TAG = "ConfigFactory";
+
+    private final StatsdConfig mTemplate;
+
+    public ConfigFactory(Context context) {
+        // Read the config template from the resoures.
+        Resources res = context.getResources();
+        byte[] template = null;
+        StatsdConfig templateProto = null;
+        try {
+            InputStream inputStream = res.openRawResource(R.raw.loadtest_config);
+            template = new byte[inputStream.available()];
+            inputStream.read(template);
+            templateProto = StatsdConfig.parseFrom(template);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to read or parse loadtest config template. Using an empty config.");
+        }
+        mTemplate = templateProto == null ? StatsdConfig.newBuilder().build() : templateProto;
+
+        Log.d(TAG, "Loadtest template config: " + mTemplate);
+    }
+
+    /**
+     * Generates a config.
+     *
+     * All configs are based on the same template.
+     * That template is designed to make the most use of the set of atoms that {@code SequencePusher}
+     * pushes, and to exercise as many of the metrics features as possible.
+     * Furthermore, by passing a replication factor to this method, one can artificially inflate
+     * the number of metrics in the config. One can also adjust the bucket size for aggregate
+     * metrics.
+     *
+     * @param replication The number of times each metric is replicated in the config.
+     *        If the config template has n metrics, the generated config will have n * replication
+     *        ones
+     * @param bucketMillis The bucket size, in milliseconds, for aggregate metrics
+     * @param placebo If true, only return an empty config
+     * @return The serialized config
+     */
+  public byte[] getConfig(int replication, long bucketMillis, boolean placebo) {
+        StatsdConfig.Builder config = StatsdConfig.newBuilder()
+            .setName(CONFIG_NAME);
+        if (placebo) {
+          replication = 0;  // Config will be empty, aside from a name.
+        }
+        int numMetrics = 0;
+        for (int i = 0; i < replication; i++) {
+            // metrics
+            for (EventMetric metric : mTemplate.getEventMetricList()) {
+                addEventMetric(metric, i, config);
+                numMetrics++;
+            }
+            for (CountMetric metric : mTemplate.getCountMetricList()) {
+                addCountMetric(metric, i, bucketMillis, config);
+                numMetrics++;
+            }
+            for (DurationMetric metric : mTemplate.getDurationMetricList()) {
+                addDurationMetric(metric, i, bucketMillis, config);
+                numMetrics++;
+            }
+            for (GaugeMetric metric : mTemplate.getGaugeMetricList()) {
+                addGaugeMetric(metric, i, bucketMillis, config);
+                numMetrics++;
+            }
+            for (ValueMetric metric : mTemplate.getValueMetricList()) {
+                addValueMetric(metric, i, bucketMillis, config);
+                numMetrics++;
+            }
+            // conditions
+            for (Condition condition : mTemplate.getConditionList()) {
+              addCondition(condition, i, config);
+            }
+            // matchers
+            for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) {
+              addMatcher(matcher, i, config);
+            }
+        }
+
+        Log.d(TAG, "Loadtest config is : " + config.build());
+        Log.d(TAG, "Generated config has " + numMetrics + " metrics");
+
+        return config.build().toByteArray();
+    }
+
+    /**
+     * Creates {@link EventConditionLink}s that are identical to the one passed to this method,
+     * except that the names are appended with the provided suffix.
+     */
+    private List<EventConditionLink> getLinks(
+        List<EventConditionLink> links, int suffix) {
+        List<EventConditionLink> newLinks = new ArrayList();
+        for (EventConditionLink link : links) {
+            newLinks.add(link.toBuilder()
+                .setCondition(link.getCondition() + suffix)
+                .build());
+        }
+        return newLinks;
+    }
+
+    /**
+     * Creates an {@link EventMetric} based on the template. Makes sure that all names are appended
+     * with the provided suffix. Then adds that metric to the config.
+     */
+    private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) {
+        EventMetric.Builder metric = template.toBuilder()
+            .setName(template.getName() + suffix)
+            .setWhat(template.getWhat() + suffix);
+        if (template.hasCondition()) {
+            metric.setCondition(template.getCondition() + suffix);
+        }
+        if (template.getLinksCount() > 0) {
+            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            metric.clearLinks();
+            metric.addAllLinks(links);
+        }
+        config.addEventMetric(metric);
+    }
+
+    private Bucket getBucket(long bucketMillis) {
+        return Bucket.newBuilder()
+            .setBucketSizeMillis(bucketMillis)
+            .build();
+    }
+
+    /**
+     * Creates a {@link CountMetric} based on the template. Makes sure that all names are appended
+     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
+     */
+    private void addCountMetric(CountMetric template, int suffix, long bucketMillis,
+        StatsdConfig.Builder config) {
+        CountMetric.Builder metric = template.toBuilder()
+            .setName(template.getName() + suffix)
+            .setWhat(template.getWhat() + suffix);
+        if (template.hasCondition()) {
+            metric.setCondition(template.getCondition() + suffix);
+        }
+        if (template.getLinksCount() > 0) {
+            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            metric.clearLinks();
+            metric.addAllLinks(links);
+        }
+        metric.setBucket(getBucket(bucketMillis));
+        config.addCountMetric(metric);
+    }
+
+    /**
+     * Creates a {@link DurationMetric} based on the template. Makes sure that all names are appended
+     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
+     */
+    private void addDurationMetric(DurationMetric template, int suffix, long bucketMillis,
+        StatsdConfig.Builder config) {
+        DurationMetric.Builder metric = template.toBuilder()
+            .setName(template.getName() + suffix)
+            .setWhat(template.getWhat() + suffix);
+        if (template.hasCondition()) {
+            metric.setCondition(template.getCondition() + suffix);
+        }
+        if (template.getLinksCount() > 0) {
+            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            metric.clearLinks();
+            metric.addAllLinks(links);
+        }
+        metric.setBucket(getBucket(bucketMillis));
+        config.addDurationMetric(metric);
+    }
+
+    /**
+     * Creates a {@link GaugeMetric} based on the template. Makes sure that all names are appended
+     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
+     */
+    private void addGaugeMetric(GaugeMetric template, int suffix, long bucketMillis,
+        StatsdConfig.Builder config) {
+        GaugeMetric.Builder metric = template.toBuilder()
+            .setName(template.getName() + suffix)
+            .setWhat(template.getWhat() + suffix);
+        if (template.hasCondition()) {
+            metric.setCondition(template.getCondition() + suffix);
+        }
+        if (template.getLinksCount() > 0) {
+            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            metric.clearLinks();
+            metric.addAllLinks(links);
+        }
+        metric.setBucket(getBucket(bucketMillis));
+        config.addGaugeMetric(metric);
+    }
+
+    /**
+     * Creates a {@link ValueMetric} based on the template. Makes sure that all names are appended
+     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
+     */
+    private void addValueMetric(ValueMetric template, int suffix, long bucketMillis,
+        StatsdConfig.Builder config) {
+        ValueMetric.Builder metric = template.toBuilder()
+            .setName(template.getName() + suffix)
+            .setWhat(template.getWhat() + suffix);
+        if (template.hasCondition()) {
+            metric.setCondition(template.getCondition() + suffix);
+        }
+        if (template.getLinksCount() > 0) {
+            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            metric.clearLinks();
+            metric.addAllLinks(links);
+        }
+        metric.setBucket(getBucket(bucketMillis));
+        config.addValueMetric(metric);
+    }
+
+    /**
+     * Creates a {@link Condition} based on the template. Makes sure that all names
+     * are appended with the provided suffix. Then adds that condition to the config.
+     */
+    private void addCondition(Condition template, int suffix, StatsdConfig.Builder config) {
+        Condition.Builder condition = template.toBuilder()
+            .setName(template.getName() + suffix);
+        if (template.hasCombination()) {
+            Condition.Combination.Builder cb = template.getCombination().toBuilder()
+                .clearCondition();
+            for (String child : template.getCombination().getConditionList()) {
+                cb.addCondition(child + suffix);
+            }
+            condition.setCombination(cb.build());
+        }
+        if (template.hasSimpleCondition()) {
+            SimpleCondition.Builder sc = template.getSimpleCondition().toBuilder()
+                .setStart(template.getSimpleCondition().getStart() + suffix)
+                .setStop(template.getSimpleCondition().getStop() + suffix);
+            if (template.getSimpleCondition().hasStopAll()) {
+                sc.setStopAll(template.getSimpleCondition().getStopAll() + suffix);
+            }
+            condition.setSimpleCondition(sc.build());
+        }
+        config.addCondition(condition);
+    }
+
+    /**
+     * Creates a {@link AtomMatcher} based on the template. Makes sure that all names
+     * are appended with the provided suffix. Then adds that matcher to the config.
+     */
+    private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) {
+        AtomMatcher.Builder matcher = template.toBuilder()
+            .setName(template.getName() + suffix);
+        if (template.hasCombination()) {
+            AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder()
+                .clearMatcher();
+            for (String child : template.getCombination().getMatcherList()) {
+                cb.addMatcher(child + suffix);
+            }
+            matcher.setCombination(cb);
+        }
+        config.addAtomMatcher(matcher);
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
new file mode 100644
index 0000000..5e8f26d
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.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 com.android.statsd.loadtest;
+
+import android.text.format.DateFormat;
+
+import com.android.os.StatsLog;
+
+import java.util.List;
+
+public class DisplayProtoUtils {
+    public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) {
+        sb.append("ConfigKey: ");
+        if (reports.hasConfigKey()) {
+            com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey();
+            sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getName())
+                    .append("\n");
+        }
+
+        for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
+            sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
+            for (StatsLog.StatsLogReport log : report.getMetricsList()) {
+                sb.append("\n\n");
+                sb.append("metric id: ").append(log.getMetricName()).append("\n");
+                sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
+                sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
+
+                switch (log.getDataCase()) {
+                    case DURATION_METRICS:
+                        sb.append("Duration metric data\n");
+                        displayDurationMetricData(sb, log);
+                        break;
+                    case EVENT_METRICS:
+                        sb.append("Event metric data\n");
+                        displayEventMetricData(sb, log);
+                        break;
+                    case COUNT_METRICS:
+                        sb.append("Count metric data\n");
+                        displayCountMetricData(sb, log);
+                        break;
+                    case GAUGE_METRICS:
+                        sb.append("Gauge metric data\n");
+                        displayGaugeMetricData(sb, log);
+                        break;
+                    case VALUE_METRICS:
+                        sb.append("Value metric data\n");
+                        displayValueMetricData(sb, log);
+                        break;
+                    case DATA_NOT_SET:
+                        sb.append("No metric data\n");
+                        break;
+                }
+            }
+        }
+    }
+
+    public static String getDateStr(long nanoSec) {
+        return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
+    }
+
+    private static void displayDimension(StringBuilder sb, List<StatsLog.KeyValuePair> pairs) {
+        for (com.android.os.StatsLog.KeyValuePair kv : pairs) {
+            sb.append(kv.getKey()).append(":");
+            if (kv.hasValueBool()) {
+                sb.append(kv.getValueBool());
+            } else if (kv.hasValueFloat()) {
+                sb.append(kv.getValueFloat());
+            } else if (kv.hasValueInt()) {
+                sb.append(kv.getValueInt());
+            } else if (kv.hasValueStr()) {
+                sb.append(kv.getValueStr());
+            }
+            sb.append(" ");
+        }
+    }
+
+    public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
+        StatsLog.StatsLogReport.DurationMetricDataWrapper durationMetricDataWrapper
+                = log.getDurationMetrics();
+        sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
+        for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
+            sb.append("dimension: ");
+            displayDimension(sb, duration.getDimensionList());
+            sb.append("\n");
+
+            for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList())  {
+                sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-")
+                        .append(getDateStr(info.getEndBucketNanos())).append("] -> ")
+                        .append(info.getDurationNanos()).append(" ns\n");
+            }
+        }
+    }
+
+    public static void displayEventMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
+        sb.append("Contains ").append(log.getEventMetrics().getDataCount()).append(" events\n");
+        StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper =
+                log.getEventMetrics();
+        for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) {
+            sb.append(getDateStr(event.getTimestampNanos())).append(": ");
+            switch (event.getAtom().getPushedCase()) {
+                case SETTING_CHANGED:
+                    sb.append("SETTING_CHANGED\n");
+                    break;
+                case SYNC_STATE_CHANGED:
+                    sb.append("SYNC_STATE_CHANGED\n");
+                    break;
+                case AUDIO_STATE_CHANGED:
+                    sb.append("AUDIO_STATE_CHANGED\n");
+                    break;
+                case CAMERA_STATE_CHANGED:
+                    sb.append("CAMERA_STATE_CHANGED\n");
+                    break;
+                case ISOLATED_UID_CHANGED:
+                    sb.append("ISOLATED_UID_CHANGED\n");
+                    break;
+                case SCREEN_STATE_CHANGED:
+                    sb.append("SCREEN_STATE_CHANGED\n");
+                    break;
+                case SENSOR_STATE_CHANGED:
+                    sb.append("SENSOR_STATE_CHANGED\n");
+                    break;
+                case BATTERY_LEVEL_CHANGED:
+                    sb.append("BATTERY_LEVEL_CHANGED\n");
+                    break;
+                case PLUGGED_STATE_CHANGED:
+                    sb.append("PLUGGED_STATE_CHANGED\n");
+                    break;
+                case WAKEUP_ALARM_OCCURRED:
+                    sb.append("WAKEUP_ALARM_OCCURRED\n");
+                    break;
+                case BLE_SCAN_STATE_CHANGED:
+                    sb.append("BLE_SCAN_STATE_CHANGED\n");
+                    break;
+                case CHARGING_STATE_CHANGED:
+                    sb.append("CHARGING_STATE_CHANGED\n");
+                    break;
+                case GPS_SCAN_STATE_CHANGED:
+                    sb.append("GPS_SCAN_STATE_CHANGED\n");
+                    break;
+                case KERNEL_WAKEUP_REPORTED:
+                    sb.append("KERNEL_WAKEUP_REPORTED\n");
+                    break;
+                case WAKELOCK_STATE_CHANGED:
+                    sb.append("WAKELOCK_STATE_CHANGED\n");
+                    break;
+                case WIFI_LOCK_STATE_CHANGED:
+                    sb.append("WIFI_LOCK_STATE_CHANGED\n");
+                    break;
+                case WIFI_SCAN_STATE_CHANGED:
+                    sb.append("WIFI_SCAN_STATE_CHANGED\n");
+                    break;
+                case BLE_SCAN_RESULT_RECEIVED:
+                    sb.append("BLE_SCAN_RESULT_RECEIVED\n");
+                    break;
+                case DEVICE_ON_STATUS_CHANGED:
+                    sb.append("DEVICE_ON_STATUS_CHANGED\n");
+                    break;
+                case FLASHLIGHT_STATE_CHANGED:
+                    sb.append("FLASHLIGHT_STATE_CHANGED\n");
+                    break;
+                case SCREEN_BRIGHTNESS_CHANGED:
+                    sb.append("SCREEN_BRIGHTNESS_CHANGED\n");
+                    break;
+                case UID_PROCESS_STATE_CHANGED:
+                    sb.append("UID_PROCESS_STATE_CHANGED\n");
+                    break;
+                case UID_WAKELOCK_STATE_CHANGED:
+                    sb.append("UID_WAKELOCK_STATE_CHANGED\n");
+                    break;
+                case DEVICE_TEMPERATURE_REPORTED:
+                    sb.append("DEVICE_TEMPERATURE_REPORTED\n");
+                    break;
+                case SCHEDULED_JOB_STATE_CHANGED:
+                    sb.append("SCHEDULED_JOB_STATE_CHANGED\n");
+                    break;
+                case MEDIA_CODEC_ACTIVITY_CHANGED:
+                    sb.append("MEDIA_CODEC_ACTIVITY_CHANGED\n");
+                    break;
+                case WIFI_SIGNAL_STRENGTH_CHANGED:
+                    sb.append("WIFI_SIGNAL_STRENGTH_CHANGED\n");
+                    break;
+                case PHONE_SIGNAL_STRENGTH_CHANGED:
+                    sb.append("PHONE_SIGNAL_STRENGTH_CHANGED\n");
+                    break;
+                case DEVICE_IDLE_MODE_STATE_CHANGED:
+                    sb.append("DEVICE_IDLE_MODE_STATE_CHANGED\n");
+                    break;
+                case BATTERY_SAVER_MODE_STATE_CHANGED:
+                    sb.append("BATTERY_SAVER_MODE_STATE_CHANGED\n");
+                    break;
+                case PROCESS_LIFE_CYCLE_STATE_CHANGED:
+                    sb.append("PROCESS_LIFE_CYCLE_STATE_CHANGED\n");
+                    break;
+                case ACTIVITY_FOREGROUND_STATE_CHANGED:
+                    sb.append("ACTIVITY_FOREGROUND_STATE_CHANGED\n");
+                    break;
+                case BLE_UNOPTIMIZED_SCAN_STATE_CHANGED:
+                    sb.append("BLE_UNOPTIMIZED_SCAN_STATE_CHANGED\n");
+                    break;
+                case LONG_PARTIAL_WAKELOCK_STATE_CHANGED:
+                    sb.append("LONG_PARTIAL_WAKELOCK_STATE_CHANGED\n");
+                    break;
+                case PUSHED_NOT_SET:
+                    sb.append("PUSHED_NOT_SET\n");
+                    break;
+            }
+        }
+    }
+
+    public static void displayCountMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
+        StatsLog.StatsLogReport.CountMetricDataWrapper countMetricDataWrapper
+                = log.getCountMetrics();
+        sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
+        for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
+            sb.append("dimension: ");
+            displayDimension(sb, count.getDimensionList());
+            sb.append("\n");
+
+            for (StatsLog.CountBucketInfo info : count.getBucketInfoList())  {
+                sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-")
+                        .append(getDateStr(info.getEndBucketNanos())).append("] -> ")
+                        .append(info.getCount()).append("\n");
+            }
+        }
+    }
+
+    public static void displayGaugeMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
+        sb.append("Display me!");
+    }
+
+    public static void displayValueMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
+        sb.append("Display me!");
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
new file mode 100644
index 0000000..3ae85a7
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IStatsManager;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.util.StatsLog;
+import android.util.StatsManager;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.view.MotionEvent;
+import android.view.View.OnFocusChangeListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Runs a load test for statsd.
+ * How it works:
+ * <ul>
+ *   <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths.
+ *   <li> Periodically logs certain atoms into logd.
+ *   <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed
+ *        in battery Historian.
+ * </ul>
+ * The load depends on how demanding the config is, as well as how frequently atoms are pushsed
+ * to logd. Those are all controlled by 4 adjustable parameters:
+ * <ul>
+ *   <li> The 'replication' parameter artificially multiplies the number of metrics in the config.
+ *   <li> The bucket size controls the time-bucketing the aggregate metrics.
+ *   <li> The period parameter controls how frequently atoms are pushed to logd.
+ *   <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period).
+ * </ul>
+ */
+public class LoadtestActivity extends Activity {
+
+    private static final String TAG = "StatsdLoadtest";
+    private static final String TYPE = "type";
+    private static final String ALARM = "push_alarm";
+    private static final String START = "start";
+    private static final String STOP = "stop";
+
+    public final static class PusherAlarmReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Intent activityIntent = new Intent(context, LoadtestActivity.class);
+            activityIntent.putExtra(TYPE, ALARM);
+            context.startActivity(activityIntent);
+         }
+    }
+
+    public final static class StopperAlarmReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Intent activityIntent = new Intent(context, LoadtestActivity.class);
+            activityIntent.putExtra(TYPE, STOP);
+            context.startActivity(activityIntent);
+         }
+    }
+
+    private AlarmManager mAlarmMgr;
+
+    /** Used to periodically log atoms to logd. */
+    private PendingIntent mPushPendingIntent;
+
+    /** Used to end the loadtest. */
+    private PendingIntent mStopPendingIntent;
+
+    private Button mStartStop;
+    private EditText mReplicationText;
+    private EditText mBucketText;
+    private EditText mPeriodText;
+    private EditText mBurstText;
+    private EditText mDurationText;
+    private TextView mReportText;
+    private CheckBox mPlaceboCheckBox;
+
+    /** For measuring perf data. */
+    private PerfData mPerfData;
+
+    /** For communicating with statsd. */
+    private StatsManager mStatsManager;
+
+    private PowerManager mPowerManager;
+    private WakeLock mWakeLock;
+
+    /**
+     * If true, we only measure the effect of the loadtest infrastructure. No atom are pushed and
+     * the configuration is empty.
+     */
+    private boolean mPlacebo;
+
+    /** The burst size. */
+    private int mBurst;
+
+    /** The metrics replication. */
+    private int mReplication;
+
+    /** The period, in seconds, at which batches of atoms are pushed. */
+    private long mPeriodSecs;
+
+    /** The bucket size, in minutes, for aggregate metrics. */
+    private long mBucketMins;
+
+    /** The duration, in minutes, of the loadtest. */
+    private long mDurationMins;
+
+    /** Whether the loadtest has started. */
+    private boolean mStarted = false;
+
+    /** Orchestrates the logging of pushed events into logd. */
+    private SequencePusher mPusher;
+
+    /** Generates statsd configs. */
+    private ConfigFactory mFactory;
+
+    /** For intra-minute periods. */
+    private final Handler mHandler = new Handler();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Log.d(TAG, "Starting loadtest");
+
+        setContentView(R.layout.activity_loadtest);
+        mReportText = (TextView) findViewById(R.id.report_text);
+        initBurst();
+        initReplication();
+        initBucket();
+        initPeriod();
+        initDuration();
+        initPlacebo();
+
+        // Hide the keyboard outside edit texts.
+        findViewById(R.id.outside).setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                InputMethodManager imm =
+                    (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+                if (getCurrentFocus() != null) {
+                    imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
+                }
+                return true;
+            }
+        });
+
+        mStartStop = findViewById(R.id.start_stop);
+        mStartStop.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (mStarted) {
+                    stopLoadtest(true);
+                } else {
+                    startLoadtest();
+                }
+            }
+        });
+
+        findViewById(R.id.display_output).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                fetchAndDisplayData();
+            }
+        });
+
+        findViewById(R.id.display_perf).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                mPerfData.publishData(LoadtestActivity.this, mPlacebo, mReplication, mBucketMins,
+                    mPeriodSecs, mBurst);
+            }
+        });
+
+        mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+        mStatsManager = (StatsManager) getSystemService("stats");
+        mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+        mFactory = new ConfigFactory(this);
+        mPerfData = new PerfData();
+        stopLoadtest(false);
+        mReportText.setText("");
+    }
+
+    @Override
+    public void onNewIntent(Intent intent) {
+        String type = intent.getStringExtra(TYPE);
+        if (type == null) {
+            return;
+        }
+        switch (type) {
+            case ALARM:
+              onAlarm(intent);
+              break;
+            case START:
+                startLoadtest();
+                break;
+          case STOP:
+                stopLoadtest(true);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown type: " + type);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(TAG, "Destroying");
+        stopLoadtest(false);
+        clearConfigs();
+        super.onDestroy();
+    }
+
+    private void onAlarm(Intent intent) {
+        Log.d(TAG, "ON ALARM");
+
+        // Set the next task.
+        scheduleNext();
+
+        // Do the work.
+        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StatsdLoadTest");
+        mWakeLock.acquire();
+        if (mPusher != null) {
+            mPusher.next();
+        }
+        mWakeLock.release();
+        mWakeLock = null;
+    }
+
+    /** Schedules the next cycle of pushing atoms into logd. */
+    private void scheduleNext() {
+        Intent intent = new Intent(this, PusherAlarmReceiver.class);
+        intent.putExtra(TYPE, ALARM);
+        mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+        long nextTime =  SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
+        mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
+    }
+
+    private synchronized void startLoadtest() {
+        if (mStarted) {
+            return;
+        }
+
+        // Clean up the state.
+        stopLoadtest(false);
+
+        // Prepare to push a sequence of atoms to logd.
+        mPusher = new SequencePusher(mBurst, mPlacebo);
+
+        // Create a config and push it to statsd.
+        if (!setConfig(mFactory.getConfig(mReplication, mBucketMins * 60 * 1000, mPlacebo))) {
+            return;
+        }
+
+        // Remember to stop in the future.
+        Intent intent = new Intent(this, StopperAlarmReceiver.class);
+        intent.putExtra(TYPE, STOP);
+        mStopPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+        long nextTime =  SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000;
+        mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mStopPendingIntent);
+
+        // Log atoms.
+        scheduleNext();
+
+        // Reset battery data.
+        mPerfData.resetData(this);
+
+        mReportText.setText("");
+
+        updateStarted(true);
+    }
+
+    private synchronized void stopLoadtest(boolean publishPerfData) {
+        if (mPushPendingIntent != null) {
+            Log.d(TAG, "Canceling pre-existing push alarm");
+            mAlarmMgr.cancel(mPushPendingIntent);
+            mPushPendingIntent = null;
+        }
+        if (mStopPendingIntent != null) {
+            Log.d(TAG, "Canceling pre-existing stop alarm");
+            mAlarmMgr.cancel(mStopPendingIntent);
+            mStopPendingIntent = null;
+        }
+        if (mWakeLock != null) {
+            mWakeLock.release();
+            mWakeLock = null;
+        }
+        fetchAndDisplayData();
+        clearConfigs();
+        updateStarted(false);
+        if (publishPerfData) {
+            mPerfData.publishData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
+        }
+    }
+
+    private synchronized void updateStarted(boolean started) {
+        mStarted = started;
+        mStartStop.setBackgroundColor(started ?
+            Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00"));
+        mStartStop.setText(started ? getString(R.string.stop) : getString(R.string.start));
+        updateControlsEnabled();
+    }
+
+    private void updateControlsEnabled() {
+        mBurstText.setEnabled(!mPlacebo && !mStarted);
+        mReplicationText.setEnabled(!mPlacebo && !mStarted);
+        mPeriodText.setEnabled(!mStarted);
+        mBucketText.setEnabled(!mPlacebo && !mStarted);
+        mDurationText.setEnabled(!mStarted);
+        mPlaceboCheckBox.setEnabled(!mStarted);
+    }
+
+    private void fetchAndDisplayData() {
+        if (!statsdRunning()) {
+            return;
+        }
+        if (mStatsManager != null) {
+            byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_NAME);
+            if (data != null) {
+                displayData(data);
+            } else {
+                mReportText.setText("Failed to pull data");
+            }
+        }
+    }
+
+    private void displayData(byte[] data) {
+        com.android.os.StatsLog.ConfigMetricsReportList reports = null;
+        boolean good = false;
+        if (data != null) {
+            try {
+                reports = com.android.os.StatsLog.ConfigMetricsReportList.parseFrom(data);
+                good = true;
+            } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+                // display it in the text view.
+            }
+        }
+        int size = data == null ? 0 : data.length;
+        StringBuilder sb = new StringBuilder();
+        sb.append(good ? "Proto parsing OK!" : "Proto parsing Error!");
+        sb.append(" size:").append(size).append("\n");
+
+        if (good && reports != null) {
+            DisplayProtoUtils.displayLogReport(sb, reports);
+            mReportText.setText(sb.toString());
+        }
+    }
+
+    private boolean statsdRunning() {
+        if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) {
+            Log.d(TAG, "Statsd not running");
+            Toast.makeText(LoadtestActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show();
+            return false;
+        }
+        return true;
+    }
+
+    private int sanitizeInt(int val, int min, int max) {
+        if (val > max) {
+            val = max;
+        } else if (val < min) {
+            val = min;
+        }
+        return val;
+    }
+
+    private void clearConfigs() {
+        // TODO: Clear all configs instead of specific ones.
+        if (mStatsManager != null) {
+            if (!mStatsManager.removeConfiguration("fake")) {
+                Log.d(TAG, "Removed \"fake\" statsd configs.");
+            } else {
+                Log.d(TAG, "Failed to remove \"fake\" config. Loadtest results cannot be trusted.");
+            }
+            if (mStarted) {
+                if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_NAME)) {
+                    Log.d(TAG, "Removed loadtest statsd configs.");
+                } else {
+                    Log.d(TAG, "Failed to remove loadtest configs.");
+                }
+            }
+        }
+    }
+
+    private boolean setConfig(byte[] config) {
+      if (mStatsManager != null) {
+            if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_NAME,
+                config, getPackageName(), LoadtestActivity.this.getClass().getName())) {
+                Log.d(TAG, "Config pushed to statsd");
+                return true;
+            } else {
+                Log.d(TAG, "Failed to push config to statsd");
+            }
+      }
+      return false;
+    }
+
+    private synchronized void setReplication(int replication) {
+        mReplication = replication;
+    }
+
+    private synchronized void setPeriodSecs(long periodSecs) {
+        mPeriodSecs = periodSecs;
+    }
+
+    private synchronized void setBucketMins(long bucketMins) {
+        mBucketMins = bucketMins;
+    }
+
+    private synchronized void setBurst(int burst) {
+        mBurst = burst;
+    }
+
+    private synchronized void setDurationMins(long durationMins) {
+        mDurationMins = durationMins;
+    }
+
+    private synchronized void setPlacebo(boolean placebo) {
+        mPlacebo = placebo;
+        updateControlsEnabled();
+    }
+
+    private void handleFocus(EditText editText) {
+        editText.setOnFocusChangeListener(new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (!hasFocus && editText.getText().toString().isEmpty()) {
+                    editText.setText("-1", TextView.BufferType.EDITABLE);
+                }
+            }
+        });
+    }
+
+    private void initBurst() {
+        mBurst = getResources().getInteger(R.integer.burst_default);
+        mBurstText = (EditText) findViewById(R.id.burst);
+        mBurstText.addTextChangedListener(new NumericalWatcher(mBurstText, 0, 50) {
+            @Override
+            public void onNewValue(int newValue) {
+                setBurst(newValue);
+            }
+        });
+        handleFocus(mBurstText);
+    }
+
+    private void initReplication() {
+        mReplication = getResources().getInteger(R.integer.replication_default);
+        mReplicationText = (EditText) findViewById(R.id.replication);
+        mReplicationText.addTextChangedListener(new NumericalWatcher(mReplicationText, 1, 100) {
+            @Override
+            public void onNewValue(int newValue) {
+                setReplication(newValue);
+            }
+        });
+        handleFocus(mReplicationText);
+    }
+
+    private void initBucket() {
+        mBucketMins = getResources().getInteger(R.integer.bucket_default);
+        mBucketText = (EditText) findViewById(R.id.bucket);
+        mBucketText.addTextChangedListener(new NumericalWatcher(mBucketText, 1, 24 * 60) {
+            @Override
+            public void onNewValue(int newValue) {
+                setBucketMins(newValue);
+            }
+        });
+        handleFocus(mBucketText);
+    }
+
+    private void initPeriod() {
+        mPeriodSecs = getResources().getInteger(R.integer.period_default);
+        mPeriodText = (EditText) findViewById(R.id.period);
+        mPeriodText.addTextChangedListener(new NumericalWatcher(mPeriodText, 1, 60) {
+            @Override
+            public void onNewValue(int newValue) {
+                setPeriodSecs(newValue);
+            }
+        });
+        handleFocus(mPeriodText);
+    }
+
+    private void initDuration() {
+        mDurationMins = getResources().getInteger(R.integer.duration_default);
+        mDurationText = (EditText) findViewById(R.id.duration);
+        mDurationText.addTextChangedListener(new NumericalWatcher(mDurationText, 1, 24 * 60) {
+            @Override
+            public void onNewValue(int newValue) {
+                setDurationMins(newValue);
+            }
+        });
+        handleFocus(mDurationText);
+    }
+
+    private void initPlacebo() {
+        mPlaceboCheckBox = findViewById(R.id.placebo);
+        mPlacebo = false;
+        mPlaceboCheckBox.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                setPlacebo(((CheckBox) view).isChecked());
+            }
+        });
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
new file mode 100644
index 0000000..57f85b5
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.util.StatsLog;
+import android.util.StatsManager;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.os.IStatsManager;
+import android.os.ServiceManager;
+import android.view.View.OnFocusChangeListener;
+
+public abstract class NumericalWatcher implements TextWatcher {
+
+  private static final String TAG = "NumericalWatcher";
+
+    private final TextView mTextView;
+    private final int mMin;
+    private final int mMax;
+    private int currentValue = -1;
+
+    public NumericalWatcher(TextView textView, int min, int max) {
+        mTextView = textView;
+        mMin = min;
+        mMax = max;
+    }
+
+    public abstract void onNewValue(int newValue);
+
+    @Override
+    final public void afterTextChanged(Editable editable) {
+        String s = mTextView.getText().toString();
+        if (s.isEmpty()) {
+          return;
+        }
+        int unsanitized = Integer.parseInt(s);
+        int newValue = sanitize(unsanitized);
+
+        Log.d(TAG, "YOYO " + currentValue + " " + newValue + " " + unsanitized);
+
+        if (currentValue != newValue || unsanitized != newValue) {
+            currentValue = newValue;
+            editable.clear();
+            editable.append(newValue + "");
+        }
+        onNewValue(newValue);
+    }
+
+    @Override
+    final public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+    @Override
+    final public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+    private int sanitize(int val) {
+        if (val > mMax) {
+            val = mMax;
+        } else if (val < mMin) {
+            val = mMin;
+        }
+        return val;
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
new file mode 100644
index 0000000..e3e23f5
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.os.Debug;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/** Prints some information about the device via Dumpsys in order to evaluate health metrics. */
+public class PerfData {
+
+    private static final String TAG = "PerfData";
+    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
+
+    public void resetData(Context context) {
+        runDumpsysStats(context, "batterystats", "--reset");
+    }
+
+    public void publishData(Context context, boolean placebo, int replication, long bucketMins,
+        long periodSecs, int burst) {
+        publishBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    private void publishBatteryData(Context context, boolean placebo, int replication,
+        long bucketMins, long periodSecs, int burst) {
+        // Don't use --checkin.
+        runDumpsysStats(context, "batterystats");
+        writeBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    private void runDumpsysStats(Context context, String cmd, String... args) {
+        boolean success = false;
+        // Call dumpsys Dump statistics to a file.
+        FileOutputStream fo = null;
+        try {
+            fo = context.openFileOutput(DUMP_FILENAME, Context.MODE_PRIVATE);
+            if (!Debug.dumpService(cmd, fo.getFD(), args)) {
+                Log.w(TAG, "Dumpsys failed.");
+            }
+            success = true;
+        } catch (IOException | SecurityException | NullPointerException e) {
+            // SecurityException may occur when trying to dump multi-user info.
+            // NPE can occur during dumpService  (root cause unknown).
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(fo);
+        }
+    }
+
+    private String readDumpFile(Context context) {
+        StringBuilder sb = new StringBuilder();
+        FileInputStream fi = null;
+        BufferedReader br = null;
+        try {
+            fi = context.openFileInput(DUMP_FILENAME);
+            br = new BufferedReader(new InputStreamReader(fi));
+            String line = br.readLine();
+            while (line != null) {
+                sb.append(line);
+                sb.append(System.lineSeparator());
+                line = br.readLine();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(br);
+        }
+        return sb.toString();
+    }
+
+    private static void closeQuietly(@Nullable Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    public void writeBatteryData(Context context, boolean placebo, int replication, long bucketMins,
+        long periodSecs, int burst) {
+        BatteryStatsParser parser = new BatteryStatsParser();
+        FileInputStream fi = null;
+        BufferedReader br = null;
+        String suffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
+        File batteryDataFile = new File(getStorageDir(), "battery_" + suffix + ".csv");
+        Log.d(TAG, "Writing battery data to " + batteryDataFile.getAbsolutePath());
+
+        FileWriter writer = null;
+        try {
+            fi = context.openFileInput(DUMP_FILENAME);
+            writer = new FileWriter(batteryDataFile);
+            writer.append("time,battery_level"
+                + getColumnName(placebo, replication, bucketMins, periodSecs, burst) + "\n");
+            br = new BufferedReader(new InputStreamReader(fi));
+            String line = br.readLine();
+            while (line != null) {
+                String recordLine = parser.parseLine(line);
+                if (recordLine != null) {
+                    writer.append(recordLine);
+                }
+                line = br.readLine();
+            }
+            writer.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(writer);
+            closeQuietly(br);
+        }
+    }
+
+    private File getStorageDir() {
+        File file = new File(Environment.getExternalStoragePublicDirectory(
+            Environment.DIRECTORY_DOCUMENTS), "loadtest");
+        if (!file.mkdirs()) {
+            Log.e(TAG, "Directory not created");
+        }
+        return file;
+    }
+
+    private String getColumnName(boolean placebo, int replication, long bucketMins, long periodSecs,
+        int burst) {
+        if (placebo) {
+            return "_placebo_p=" + periodSecs;
+        }
+        return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java
new file mode 100644
index 0000000..d4b2aa4
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.util.Log;
+import android.util.StatsLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the pushing of atoms into logd for loadtesting.
+ * We rely on small number of pushed atoms, and a config with metrics based on those atoms.
+ * The atoms are:
+ * <ul>
+ *   <li> BatteryLevelChanged   - For EventMetric, CountMetric and GaugeMetric (no dimensions).
+ *   <li> BleScanResultReceived - For CountMetric and ValueMetric, sliced by uid.
+ *   <li> ChargingStateChanged  - For DurationMetric (no dimension).
+ *   <li> GpsScanStateChanged   - For DurationMetric, sliced by uid.
+ *   <li> ScreenStateChanged    - For Conditions with no dimensions.
+ *   <li> AudioStateChanged     - For Conditions with dimensions (uid).
+ * </ul>
+ * The sequence is played over and over at a given frequency.
+ */
+public class SequencePusher {
+    private static final String TAG = "SequencePusher";
+
+    /** Some atoms are pushed in burst of {@code mBurst} events. */
+    private final int mBurst;
+
+    /** If this is true, we don't log anything in logd. */
+    private final boolean mPlacebo;
+
+    /** Current state in the automaton. */
+    private int mCursor = 0;
+
+  public SequencePusher(int burst, boolean placebo) {
+        mBurst = burst;
+        mPlacebo = placebo;
+    }
+
+    /**
+     * Pushes the next atom to logd.
+     * This follows a small automaton which makes the right events and conditions overlap:
+     *   (0)  Push a burst of BatteryLevelChanged atoms.
+     *   (1)  Push a burst of BleScanResultReceived atoms.
+     *   (2)  Push ChargingStateChanged with BATTERY_STATUS_CHARGING once.
+     *   (3)  Push a burst of GpsScanStateChanged atoms with ON, with a different uid each time.
+     *   (4)  Push ChargingStateChanged with BATTERY_STATUS_NOT_CHARGING once.
+     *   (5)  Push a burst GpsScanStateChanged atoms with OFF, with a different uid each time.
+     *   (6)  Push ScreenStateChanged with STATE_ON once.
+     *   (7)  Push a burst of AudioStateChanged with ON, with a different uid each time.
+     *   (8)  Repeat steps (0)-(5).
+     *   (9)  Push ScreenStateChanged with STATE_OFF once.
+     *   (10) Push a burst of AudioStateChanged with OFF, with a different uid each time.
+     * and repeat.
+     */
+    public void next() {
+        Log.d(TAG, "Next step: " + mCursor);
+        if (mPlacebo) {
+            return;
+        }
+        switch (mCursor) {
+            case 0:
+            case 8:
+                for (int i = 0; i < mBurst; i++) {
+                    StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, 50 + i /* battery_level */);
+                }
+                break;
+            case 1:
+            case 9:
+                for (int i = 0; i < mBurst; i++) {
+                    StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, i /* uid */,
+                        100 /* num_of_results */);
+                }
+                break;
+            case 2:
+            case 10:
+                StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
+                    StatsLog.CHARGING_STATE_CHANGED__CHARGING_STATE__BATTERY_STATUS_CHARGING
+                    /* charging_state */);
+                break;
+            case 3:
+            case 11:
+                for (int i = 0; i < mBurst; i++) {
+                    StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
+                        StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON /* state */);
+                }
+                break;
+            case 4:
+            case 12:
+                StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
+                    StatsLog.CHARGING_STATE_CHANGED__CHARGING_STATE__BATTERY_STATUS_NOT_CHARGING
+                    /* charging_state */);
+                break;
+            case 5:
+            case 13:
+                for (int i = 0; i < mBurst; i++) {
+                    StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
+                        StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */);
+                }
+                break;
+            case 6:
+                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+                    StatsLog.SCREEN_STATE_CHANGED__DISPLAY_STATE__STATE_ON /* display_state */);
+                break;
+            case 7:
+                for (int i = 0; i < mBurst; i++) {
+                    StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
+                        StatsLog.AUDIO_STATE_CHANGED__STATE__ON /* state */);
+                }
+                break;
+            case 14:
+                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+                    StatsLog.SCREEN_STATE_CHANGED__DISPLAY_STATE__STATE_OFF /* display_state */);
+                break;
+            case 15:
+                for (int i = 0; i < mBurst; i++) {
+                    StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
+                        StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */);
+                }
+                break;
+            default:
+        }
+        mCursor++;
+        if (mCursor > 15) {
+            mCursor = 0;
+        }
+    }
+
+    /**
+     * Properly finishes in order to be close all conditions and durations.
+     */
+    public void finish() {
+        // Screen goes back to off. This will ensure that conditions get back to false.
+        StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+            StatsLog.SCREEN_STATE_CHANGED__DISPLAY_STATE__STATE_OFF /* display_state */);
+        for (int i = 0; i < mBurst; i++) {
+          StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
+              StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */);
+        }
+        // Stop charging, to ensure the corresponding durations are closed.
+        StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
+            StatsLog.CHARGING_STATE_CHANGED__CHARGING_STATE__BATTERY_STATUS_NOT_CHARGING
+            /* charging_state */);
+        // Stop scanning GPS, to ensure the corresponding conditions get back to false.
+        for (int i = 0; i < mBurst; i++) {
+          StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
+              StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */);
+        }
+    }
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 02b7f8c..9241378 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1944,15 +1944,17 @@
      * @param animate Whether we should play an animation for the moving the task
      * @param initialBounds If the primary stack gets created, it will use these bounds for the
      *                      docked stack. Pass {@code null} to use default bounds.
+     * @param showRecents If the recents activity should be shown on the other side of the task
+     *                    going into split-screen mode.
      * @hide
      */
     @TestApi
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     public void setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
-            boolean animate, Rect initialBounds) throws SecurityException {
+            boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException {
         try {
             getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop, animate,
-                    initialBounds);
+                    initialBounds, showRecents);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 005b7c3..1dbdb59 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -55,6 +55,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
@@ -121,6 +122,8 @@
     private UserManager mUserManager;
     @GuardedBy("mLock")
     private PackageInstaller mInstaller;
+    @GuardedBy("mLock")
+    private ArtManager mArtManager;
 
     @GuardedBy("mDelegates")
     private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
@@ -2750,4 +2753,18 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    @Override
+    public ArtManager getArtManager() {
+        synchronized (mLock) {
+            if (mArtManager == null) {
+                try {
+                    mArtManager = new ArtManager(mPM.getArtManager());
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            return mArtManager;
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 2d6308c..21e4227 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -502,7 +502,7 @@
     void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
     boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
-            boolean animate, in Rect initialBounds);
+            boolean animate, in Rect initialBounds, boolean showRecents);
     /**
      * Dismisses split-screen multi-window mode.
      * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index a07374b..4a85efd 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -79,7 +79,7 @@
      *        passed here as a convenience to the agent.
      */
     void doRestore(in ParcelFileDescriptor data,
-            int appVersionCode, in ParcelFileDescriptor newState,
+            long appVersionCode, in ParcelFileDescriptor newState,
             int token, IBackupManager callbackBinder);
 
     /**
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index b26117d..d01938b 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -18,6 +18,7 @@
 
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.view.InputEvent;
 import android.view.WindowContentFrameStats;
 import android.view.WindowAnimationFrameStats;
@@ -37,7 +38,7 @@
     void disconnect();
     boolean injectInputEvent(in InputEvent event, boolean sync);
     boolean setRotation(int rotation);
-    Bitmap takeScreenshot(int width, int height);
+    Bitmap takeScreenshot(in Rect crop, int rotation);
     boolean clearWindowContentFrameStats(int windowId);
     WindowContentFrameStats getWindowContentFrameStats(int windowId);
     void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index f6d9710..de6230c 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -638,8 +638,7 @@
         final String defaultSearchPaths = System.getProperty("java.library.path");
         final boolean treatVendorApkAsUnbundled = !defaultSearchPaths.contains("/vendor/lib");
         if (mApplicationInfo.getCodePath() != null
-                && mApplicationInfo.getCodePath().startsWith("/vendor/")
-                && treatVendorApkAsUnbundled) {
+                && mApplicationInfo.isVendor() && treatVendorApkAsUnbundled) {
             isBundledApp = false;
         }
 
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 8c47598..6dca400 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -50,6 +50,11 @@
 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";
@@ -69,16 +74,12 @@
     private final Object mLock = new Object();
     private final Object mWritingToDiskLock = new Object();
 
-    @GuardedBy("mLock")
-    private Map<String, Object> mMap;
+    private Future<Map<String, Object>> mMap;
 
     @GuardedBy("mLock")
     private int mDiskWritesInFlight = 0;
 
     @GuardedBy("mLock")
-    private boolean mLoaded = false;
-
-    @GuardedBy("mLock")
     private StructTimespec mStatTimestamp;
 
     @GuardedBy("mLock")
@@ -105,27 +106,18 @@
         mFile = file;
         mBackupFile = makeBackupFile(file);
         mMode = mode;
-        mLoaded = false;
         mMap = null;
         startLoadFromDisk();
     }
 
     private void startLoadFromDisk() {
-        synchronized (mLock) {
-            mLoaded = false;
-        }
-        new Thread("SharedPreferencesImpl-load") {
-            public void run() {
-                loadFromDisk();
-            }
-        }.start();
+        FutureTask<Map<String, Object>> futureTask = new FutureTask<>(() -> loadFromDisk());
+        mMap = futureTask;
+        new Thread(futureTask, "SharedPreferencesImpl-load").start();
     }
 
-    private void loadFromDisk() {
+    private Map<String, Object> loadFromDisk() {
         synchronized (mLock) {
-            if (mLoaded) {
-                return;
-            }
             if (mBackupFile.exists()) {
                 mFile.delete();
                 mBackupFile.renameTo(mFile);
@@ -158,16 +150,14 @@
         }
 
         synchronized (mLock) {
-            mLoaded = true;
             if (map != null) {
-                mMap = map;
                 mStatTimestamp = stat.st_mtim;
                 mStatSize = stat.st_size;
             } else {
-                mMap = new HashMap<>();
+                map = new HashMap<>();
             }
-            mLock.notifyAll();
         }
+        return map;
     }
 
     static File makeBackupFile(File prefsFile) {
@@ -226,36 +216,42 @@
         }
     }
 
-    private void awaitLoadedLocked() {
-        if (!mLoaded) {
+    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()) {
             // 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();
         }
-        while (!mLoaded) {
-            try {
-                mLock.wait();
-            } catch (InterruptedException unused) {
-            }
-        }
+        return getLoaded();
     }
 
     @Override
     public Map<String, ?> getAll() {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            //noinspection unchecked
-            return new HashMap<String, Object>(mMap);
+            return new HashMap<String, Object>(map);
         }
     }
 
     @Override
     @Nullable
     public String getString(String key, @Nullable String defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            String v = (String)mMap.get(key);
+            String v = (String) map.get(key);
             return v != null ? v : defValue;
         }
     }
@@ -263,66 +259,65 @@
     @Override
     @Nullable
     public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Set<String> v = (Set<String>) mMap.get(key);
+            @SuppressWarnings("unchecked")
+            Set<String> v = (Set<String>) map.get(key);
             return v != null ? v : defValues;
         }
     }
 
     @Override
     public int getInt(String key, int defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Integer v = (Integer)mMap.get(key);
+            Integer v = (Integer) map.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public long getLong(String key, long defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Long v = (Long)mMap.get(key);
+            Long v = (Long) map.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public float getFloat(String key, float defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Float v = (Float)mMap.get(key);
+            Float v = (Float) map.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public boolean getBoolean(String key, boolean defValue) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            Boolean v = (Boolean)mMap.get(key);
+            Boolean v = (Boolean) map.get(key);
             return v != null ? v : defValue;
         }
     }
 
     @Override
     public boolean contains(String key) {
+        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            awaitLoadedLocked();
-            return mMap.containsKey(key);
+            return map.containsKey(key);
         }
     }
 
     @Override
     public Editor edit() {
-        // TODO: remove the need to call awaitLoadedLocked() when
+        // TODO: remove the need to call getLoaded() 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.
-        synchronized (mLock) {
-            awaitLoadedLocked();
-        }
+        getLoadedWithBlockGuard();
 
         return new EditorImpl();
     }
@@ -476,13 +471,43 @@
                 // a memory commit comes in when we're already
                 // writing to disk.
                 if (mDiskWritesInFlight > 0) {
-                    // We can't modify our mMap as a currently
+                    // We can't modify our map as a currently
                     // in-flight write owns it.  Clone it before
                     // modifying it.
                     // noinspection unchecked
-                    mMap = new HashMap<String, Object>(mMap);
+                    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;
+                        }
+                    };
                 }
-                mapToWriteToDisk = mMap;
+                mapToWriteToDisk = getLoaded();
                 mDiskWritesInFlight++;
 
                 boolean hasListeners = mListeners.size() > 0;
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index c99de5d..8f01685 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -26,6 +26,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.IBinder;
@@ -690,42 +691,15 @@
                 .getRealDisplay(Display.DEFAULT_DISPLAY);
         Point displaySize = new Point();
         display.getRealSize(displaySize);
-        final int displayWidth = displaySize.x;
-        final int displayHeight = displaySize.y;
 
-        final float screenshotWidth;
-        final float screenshotHeight;
-
-        final int rotation = display.getRotation();
-        switch (rotation) {
-            case ROTATION_FREEZE_0: {
-                screenshotWidth = displayWidth;
-                screenshotHeight = displayHeight;
-            } break;
-            case ROTATION_FREEZE_90: {
-                screenshotWidth = displayHeight;
-                screenshotHeight = displayWidth;
-            } break;
-            case ROTATION_FREEZE_180: {
-                screenshotWidth = displayWidth;
-                screenshotHeight = displayHeight;
-            } break;
-            case ROTATION_FREEZE_270: {
-                screenshotWidth = displayHeight;
-                screenshotHeight = displayWidth;
-            } break;
-            default: {
-                throw new IllegalArgumentException("Invalid rotation: "
-                        + rotation);
-            }
-        }
+        int rotation = display.getRotation();
 
         // Take the screenshot
         Bitmap screenShot = null;
         try {
             // Calling out without a lock held.
-            screenShot = mUiAutomationConnection.takeScreenshot((int) screenshotWidth,
-                    (int) screenshotHeight);
+            screenShot = mUiAutomationConnection.takeScreenshot(
+                    new Rect(0, 0, displaySize.x, displaySize.y), rotation);
             if (screenShot == null) {
                 return null;
             }
@@ -734,21 +708,6 @@
             return null;
         }
 
-        // Rotate the screenshot to the current orientation
-        if (rotation != ROTATION_FREEZE_0) {
-            Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight,
-                    Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(unrotatedScreenShot);
-            canvas.translate(unrotatedScreenShot.getWidth() / 2,
-                    unrotatedScreenShot.getHeight() / 2);
-            canvas.rotate(getDegreesForRotation(rotation));
-            canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2);
-            canvas.drawBitmap(screenShot, 0, 0, null);
-            canvas.setBitmap(null);
-            screenShot.recycle();
-            screenShot = unrotatedScreenShot;
-        }
-
         // Optimization
         screenShot.setHasAlpha(false);
 
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 5e414b8..d3828ab 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Binder;
 import android.os.IBinder;
@@ -153,7 +154,7 @@
     }
 
     @Override
-    public Bitmap takeScreenshot(int width, int height) {
+    public Bitmap takeScreenshot(Rect crop, int rotation) {
         synchronized (mLock) {
             throwIfCalledByNotTrustedUidLocked();
             throwIfShutdownLocked();
@@ -161,7 +162,9 @@
         }
         final long identity = Binder.clearCallingIdentity();
         try {
-            return SurfaceControl.screenshot(width, height);
+            int width = crop.width();
+            int height = crop.height();
+            return SurfaceControl.screenshot(crop, width, height, rotation);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0bca969..acdad1c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6667,7 +6667,7 @@
     }
 
     /**
-     * Called by device owners to update {@link android.provider.Settings.Global} settings.
+     * Called by device owner to update {@link android.provider.Settings.Global} settings.
      * Validation that the value of the setting is in the correct form for the setting type should
      * be performed by the caller.
      * <p>
@@ -6716,6 +6716,37 @@
     }
 
     /**
+     * Called by device owner to update {@link android.provider.Settings.System} settings.
+     * Validation that the value of the setting is in the correct form for the setting type should
+     * be performed by the caller.
+     * <p>
+     * The settings that can be updated with this method are:
+     * <ul>
+     * <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS}</li>
+     * <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS_MODE}</li>
+     * <li>{@link android.provider.Settings.System#SCREEN_OFF_TIMEOUT}</li>
+     * </ul>
+     * <p>
+     *
+     * @see android.provider.Settings.System#SCREEN_OFF_TIMEOUT
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param setting The name of the setting to update.
+     * @param value The value to update the setting to.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public void setSystemSetting(@NonNull ComponentName admin, @NonNull String setting,
+            String value) {
+        throwIfParentInstance("setSystemSetting");
+        if (mService != null) {
+            try {
+                mService.setSystemSetting(admin, setting, value);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Called by device owner to set the system wall clock time. This only takes effect if called
      * when {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
      * returned.
@@ -8444,6 +8475,38 @@
     }
 
     /**
+     * Called by a device owner to specify whether a logout button is enabled for all secondary
+     * users. The system may show a logout button that stops the user and switches back to the
+     * primary user.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param enabled whether logout button should be enabled or not.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public void setLogoutButtonEnabled(@NonNull ComponentName admin, boolean enabled) {
+        throwIfParentInstance("setLogoutButtonEnabled");
+        try {
+            mService.setLogoutButtonEnabled(admin, enabled);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether logout button is enabled by a device owner.
+     *
+     * @return {@code true} if logout button is enabled by device owner, {@code false} otherwise.
+     */
+    public boolean isLogoutButtonEnabled() {
+        throwIfParentInstance("isLogoutButtonEnabled");
+        try {
+            return mService.isLogoutButtonEnabled();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Callback used in {@link #clearApplicationUserData}
      * to indicate that the clearing of an application's user data is done.
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b7740e9..4925f34 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -231,6 +231,7 @@
     int getLockTaskFeatures(in ComponentName who);
 
     void setGlobalSetting(in ComponentName who, in String setting, in String value);
+    void setSystemSetting(in ComponentName who, in String setting, in String value);
     void setSecureSetting(in ComponentName who, in String setting, in String value);
 
     boolean setTime(in ComponentName who, long millis);
@@ -365,4 +366,7 @@
     StringParceledListSlice getOwnerInstalledCaCerts(in UserHandle user);
 
     boolean clearApplicationUserData(in ComponentName admin, in String packageName, in IPackageDataObserver callback);
+
+    void setLogoutButtonEnabled(in ComponentName admin, boolean enabled);
+    boolean isLogoutButtonEnabled();
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 7aa80d2..861cb9a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -263,6 +263,17 @@
             ParcelFileDescriptor newState) throws IOException;
 
     /**
+     * New version of {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}
+     * that handles a long app version code.  Default implementation casts the version code to
+     * an int and calls {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}.
+     */
+    public void onRestore(BackupDataInput data, long appVersionCode,
+            ParcelFileDescriptor newState)
+            throws IOException {
+        onRestore(data, (int) appVersionCode, newState);
+    }
+
+    /**
      * The application is having its entire file system contents backed up.  {@code data}
      * points to the backup destination, and the app has the opportunity to choose which
      * files are to be stored.  To commit a file as part of the backup, call the
@@ -947,7 +958,7 @@
         }
 
         @Override
-        public void doRestore(ParcelFileDescriptor data, int appVersionCode,
+        public void doRestore(ParcelFileDescriptor data, long appVersionCode,
                 ParcelFileDescriptor newState,
                 int token, IBackupManager callbackBinder) throws RemoteException {
             // Ensure that we're running with the app's normal permission level
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index ebad16e..ae4a98a 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -40,9 +40,14 @@
   /** string : the package name */
   public static final String EXTRA_LOG_EVENT_PACKAGE_NAME =
           "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME";
-  /** int : the versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME */
+  /** int : the versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME
+   * @deprecated Use {@link #EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION} */
+  @Deprecated
   public static final String EXTRA_LOG_EVENT_PACKAGE_VERSION =
           "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
+  /** long : the full versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME */
+  public static final String EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION =
+          "android.app.backup.extra.LOG_EVENT_PACKAGE_FULL_VERSION";
   /** int : the id of the log message, will be a unique identifier */
   public static final String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID";
   /**
diff --git a/core/java/android/app/usage/AppStandby.java b/core/java/android/app/usage/AppStandby.java
deleted file mode 100644
index 6f9fc2f..0000000
--- a/core/java/android/app/usage/AppStandby.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.usage;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Set of constants for app standby buckets and reasons. Apps will be moved into different buckets
- * that affect how frequently they can run in the background or perform other battery-consuming
- * actions. Buckets will be assigned based on how frequently or when the system thinks the user
- * is likely to use the app.
- * @hide
- */
-public class AppStandby {
-
-    /** The app was used very recently, currently in use or likely to be used very soon. */
-    public static final int STANDBY_BUCKET_ACTIVE = 0;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app was used recently and/or likely to be used in the next few hours  */
-    public static final int STANDBY_BUCKET_WORKING_SET = 3;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app was used in the last few days and/or likely to be used in the next few days */
-    public static final int STANDBY_BUCKET_FREQUENT = 6;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app has not be used for several days and/or is unlikely to be used for several days */
-    public static final int STANDBY_BUCKET_RARE = 9;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app has never been used. */
-    public static final int STANDBY_BUCKET_NEVER = 12;
-
-    /** Reason for bucketing -- default initial state */
-    public static final String REASON_DEFAULT = "default";
-
-    /** Reason for bucketing -- timeout */
-    public static final String REASON_TIMEOUT = "timeout";
-
-    /** Reason for bucketing -- usage */
-    public static final String REASON_USAGE = "usage";
-
-    /** Reason for bucketing -- forced by user / shell command */
-    public static final String REASON_FORCED = "forced";
-
-    /**
-     * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
-     * be appended.
-     */
-    public static final String REASON_PREDICTED = "predicted";
-
-    @IntDef(flag = false, value = {
-            STANDBY_BUCKET_ACTIVE,
-            STANDBY_BUCKET_WORKING_SET,
-            STANDBY_BUCKET_FREQUENT,
-            STANDBY_BUCKET_RARE,
-            STANDBY_BUCKET_NEVER,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StandbyBuckets {}
-}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 3a3e16e..d614b20 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -16,16 +16,18 @@
 
 package android.app.usage;
 
+import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.app.usage.AppStandby.StandbyBuckets;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -90,6 +92,76 @@
      */
     public static final int INTERVAL_COUNT = 4;
 
+
+    /**
+     * The app is whitelisted for some reason and the bucket cannot be changed.
+     * {@hide}
+     */
+    @SystemApi
+    public static final int STANDBY_BUCKET_EXEMPTED = 5;
+
+    /**
+     * The app was used very recently, currently in use or likely to be used very soon.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_ACTIVE = 10;
+
+    /**
+     * The app was used recently and/or likely to be used in the next few hours.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_WORKING_SET = 20;
+
+    /**
+     * The app was used in the last few days and/or likely to be used in the next few days.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_FREQUENT = 30;
+
+    /**
+     * The app has not be used for several days and/or is unlikely to be used for several days.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_RARE = 40;
+
+    /**
+     * The app has never been used.
+     * {@hide}
+     */
+    @SystemApi
+    public static final int STANDBY_BUCKET_NEVER = 50;
+
+    /** {@hide} Reason for bucketing -- default initial state */
+    public static final String REASON_DEFAULT = "default";
+
+    /** {@hide} Reason for bucketing -- timeout */
+    public static final String REASON_TIMEOUT = "timeout";
+
+    /** {@hide} Reason for bucketing -- usage */
+    public static final String REASON_USAGE = "usage";
+
+    /** {@hide} Reason for bucketing -- forced by user / shell command */
+    public static final String REASON_FORCED = "forced";
+
+    /**
+     * {@hide}
+     * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
+     * be appended.
+     */
+    public static final String REASON_PREDICTED = "predicted";
+
+    /** @hide */
+    @IntDef(flag = false, value = {
+            STANDBY_BUCKET_EXEMPTED,
+            STANDBY_BUCKET_ACTIVE,
+            STANDBY_BUCKET_WORKING_SET,
+            STANDBY_BUCKET_FREQUENT,
+            STANDBY_BUCKET_RARE,
+            STANDBY_BUCKET_NEVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StandbyBuckets {}
+
     private static final UsageEvents sEmptyResults = new UsageEvents();
 
     private final Context mContext;
@@ -237,7 +309,7 @@
     }
 
     /**
-     * @hide
+     * {@hide}
      */
     public void setAppInactive(String packageName, boolean inactive) {
         try {
@@ -248,19 +320,45 @@
     }
 
     /**
-     * @hide
+     * Returns the current standby bucket of the calling app. The system determines the standby
+     * state of the app based on app usage patterns. Standby buckets determine how much an app will
+     * be restricted from running background tasks such as jobs, alarms and certain PendingIntent
+     * callbacks.
+     * Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
+     * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
+     * restrictive. The battery level of the device might also affect the restrictions.
+     *
+     * @return the current standby bucket of the calling app.
      */
+    public @StandbyBuckets int getAppStandbyBucket() {
+        try {
+            return mService.getAppStandbyBucket(mContext.getOpPackageName(),
+                    mContext.getOpPackageName(),
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+        }
+        return STANDBY_BUCKET_ACTIVE;
+    }
+
+    /**
+     * {@hide}
+     * Returns the current standby bucket of the specified app. The caller must hold the permission
+     * android.permission.PACKAGE_USAGE_STATS.
+     * @param packageName the package for which to fetch the current standby bucket.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     public @StandbyBuckets int getAppStandbyBucket(String packageName) {
         try {
             return mService.getAppStandbyBucket(packageName, mContext.getOpPackageName(),
                     mContext.getUserId());
         } catch (RemoteException e) {
         }
-        return AppStandby.STANDBY_BUCKET_ACTIVE;
+        return STANDBY_BUCKET_ACTIVE;
     }
 
     /**
-     * @hide
+     * {@hide}
      * Changes the app standby state to the provided bucket.
      */
     @SystemApi
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 9954484f..4b4fe72 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -16,7 +16,7 @@
 
 package android.app.usage;
 
-import android.app.usage.AppStandby.StandbyBuckets;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.content.ComponentName;
 import android.content.res.Configuration;
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bad452c..55ad5c5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4455,10 +4455,24 @@
     public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
 
     /**
+     * The action that triggered an instant application resolution.
+     * @hide
+     */
+    public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
+
+    /**
+     * The version code of the app to install components from.
+     * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE";
+
+    /**
      * The version code of the app to install components from.
      * @hide
      */
-    public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE";
+    public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
 
     /**
      * The app that triggered the ephemeral installation.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index edb27cd..84b1ff3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -594,6 +594,13 @@
      */
     public static final int PRIVATE_FLAG_OEM = 1 << 17;
 
+    /**
+     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * vendor partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -613,6 +620,7 @@
             PRIVATE_FLAG_PRIVILEGED,
             PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
             PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
+            PRIVATE_FLAG_VENDOR,
             PRIVATE_FLAG_VIRTUAL_PRELOAD,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -888,7 +896,7 @@
      * The app's declared version code.
      * @hide
      */
-    public int versionCode;
+    public long versionCode;
 
     /**
      * The user-visible SDK version (ex. 26) of the framework against which the application claims
@@ -1315,7 +1323,7 @@
         dest.writeInt(uid);
         dest.writeInt(minSdkVersion);
         dest.writeInt(targetSdkVersion);
-        dest.writeInt(versionCode);
+        dest.writeLong(versionCode);
         dest.writeInt(enabled ? 1 : 0);
         dest.writeInt(enabledSetting);
         dest.writeInt(installLocation);
@@ -1384,7 +1392,7 @@
         uid = source.readInt();
         minSdkVersion = source.readInt();
         targetSdkVersion = source.readInt();
-        versionCode = source.readInt();
+        versionCode = source.readLong();
         enabled = source.readInt() != 0;
         enabledSetting = source.readInt();
         installLocation = source.readInt();
@@ -1569,6 +1577,11 @@
         return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
+    /** @hide */
+    public boolean isVendor() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+    }
+
     /**
      * Returns whether or not this application was installed as a virtual preload.
      */
diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java
index 067363d..6bdcefb 100644
--- a/core/java/android/content/pm/AuxiliaryResolveInfo.java
+++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java
@@ -45,7 +45,7 @@
     /** Opaque token to track the instant application resolution */
     public final String token;
     /** The version code of the package */
-    public final int versionCode;
+    public final long versionCode;
     /** An intent to start upon failure to install */
     public final Intent failureIntent;
 
@@ -71,7 +71,7 @@
     public AuxiliaryResolveInfo(@NonNull String packageName,
             @Nullable String splitName,
             @Nullable ComponentName failureActivity,
-            int versionCode,
+            long versionCode,
             @Nullable Intent failureIntent) {
         super();
         this.packageName = packageName;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 64d33d5..56a0def 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -48,6 +48,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.IArtManager;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
@@ -657,4 +658,6 @@
     ComponentName getInstantAppInstallerComponent();
 
     String getInstantAppAndroidId(String packageName, int userId);
+
+    IArtManager getArtManager();
 }
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 22e994f..fb3094c 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -44,10 +44,15 @@
     /** The filters used to match domain */
     private final List<InstantAppIntentFilter> mFilters;
     /** The version code of the app that this class resolves to */
-    private final int mVersionCode;
+    private final long mVersionCode;
 
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
+        this(digest, packageName, filters, (long)versionCode);
+    }
+
+    public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters, long versionCode) {
         // validate arguments
         if ((packageName == null && (filters != null && filters.size() != 0))
                 || (packageName != null && (filters == null || filters.size() == 0))) {
@@ -74,7 +79,7 @@
         mPackageName = in.readString();
         mFilters = new ArrayList<InstantAppIntentFilter>();
         in.readList(mFilters, null /*loader*/);
-        mVersionCode = in.readInt();
+        mVersionCode = in.readLong();
     }
 
     public byte[] getDigestBytes() {
@@ -93,7 +98,15 @@
         return mFilters;
     }
 
+    /**
+     * @deprecated Use {@link #getLongVersionCode} instead.
+     */
+    @Deprecated
     public int getVersionCode() {
+        return (int) (mVersionCode & 0xffffffff);
+    }
+
+    public long getLongVersionCode() {
         return mVersionCode;
     }
 
@@ -107,7 +120,7 @@
         out.writeParcelable(mDigest, flags);
         out.writeString(mPackageName);
         out.writeList(mFilters);
-        out.writeInt(mVersionCode);
+        out.writeLong(mVersionCode);
     }
 
     public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index f8889b6..0c893b0 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -37,13 +37,56 @@
     public String[] splitNames;
 
     /**
+     * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+     * this and the additional
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} attribute.
      * The version number of this package, as specified by the &lt;manifest&gt;
      * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode}
      * attribute.
+     * @see #getLongVersionCode()
      */
+    @Deprecated
     public int versionCode;
 
     /**
+     * @hide
+     * The major version number of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor}
+     * attribute.
+     * @see #getLongVersionCode()
+     */
+    public int versionCodeMajor;
+
+    /**
+     * Return {@link android.R.styleable#AndroidManifest_versionCode versionCode} and
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} combined
+     * together as a single long value.  The
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} is placed in
+     * the upper 32 bits.
+     */
+    public long getLongVersionCode() {
+        return composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
+    /**
+     * Set the full version code in this PackageInfo, updating {@link #versionCode}
+     * with the lower bits.
+     * @see #getLongVersionCode()
+     */
+    public void setLongVersionCode(long longVersionCode) {
+        versionCodeMajor = (int) (longVersionCode>>32);
+        versionCode = (int) longVersionCode;
+    }
+
+    /**
+     * @hide Internal implementation for composing a minor and major version code in to
+     * a single long version code.
+     */
+    public static long composeLongVersionCode(int major, int minor) {
+        return (((long) major) << 32) | (((long) minor) & 0xffffffffL);
+    }
+
+    /**
      * The version name of this package, as specified by the &lt;manifest&gt;
      * tag's {@link android.R.styleable#AndroidManifest_versionName versionName}
      * attribute.
@@ -333,6 +376,7 @@
         dest.writeString(packageName);
         dest.writeStringArray(splitNames);
         dest.writeInt(versionCode);
+        dest.writeInt(versionCodeMajor);
         dest.writeString(versionName);
         dest.writeInt(baseRevisionCode);
         dest.writeIntArray(splitRevisionCodes);
@@ -389,6 +433,7 @@
         packageName = source.readString();
         splitNames = source.createStringArray();
         versionCode = source.readInt();
+        versionCodeMajor = source.readInt();
         versionName = source.readString();
         baseRevisionCode = source.readInt();
         splitRevisionCodes = source.createIntArray();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 1efe082..bbf020d 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -38,9 +38,27 @@
 
     /**
      * The android:versionCode of the package.
+     * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+     * this and the additional
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
      */
+    @Deprecated
     public int versionCode;
 
+    /**
+     * @hide
+     * The android:versionCodeMajor of the package.
+     */
+    public int versionCodeMajor;
+
+    /**
+     * Return {@link #versionCode} and {@link #versionCodeMajor} combined together as a
+     * single long value.  The {@link #versionCodeMajor} is placed in the upper 32 bits.
+     */
+    public long getLongVersionCode() {
+        return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
     /** Revision code of base APK */
     public int baseRevisionCode;
     /** Revision codes of any split APKs, ordered by parsed splitName */
@@ -55,10 +73,10 @@
 
     /**
      * Specifies the recommended install location. Can be one of
-     * {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
-     * {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media
-     * {@link PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors
-     * {@link PackageHelper.RECOMMEND_FAILED_INVALID_APK} for parse errors.
+     * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
+     * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
+     * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
+     * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors.
      */
     public int recommendedInstallLocation;
     public int installLocation;
@@ -82,6 +100,7 @@
         dest.writeString(packageName);
         dest.writeStringArray(splitNames);
         dest.writeInt(versionCode);
+        dest.writeInt(versionCodeMajor);
         dest.writeInt(baseRevisionCode);
         dest.writeIntArray(splitRevisionCodes);
         dest.writeInt(recommendedInstallLocation);
@@ -111,6 +130,7 @@
         packageName = source.readString();
         splitNames = source.createStringArray();
         versionCode = source.readInt();
+        versionCodeMajor = source.readInt();
         baseRevisionCode = source.readInt();
         splitRevisionCodes = source.createIntArray();
         recommendedInstallLocation = source.readInt();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f796aab..ff02c40 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -42,6 +42,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.dex.ArtManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
@@ -2634,11 +2635,20 @@
 
     /**
      * Extra field name for the version code of a package pending verification.
+     * @deprecated Use {@link #EXTRA_VERIFICATION_LONG_VERSION_CODE} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_VERIFICATION_VERSION_CODE
+            = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
+
+    /**
+     * Extra field name for the long version code of a package pending verification.
      *
      * @hide
      */
-    public static final String EXTRA_VERIFICATION_VERSION_CODE
-            = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
+    public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE =
+            "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE";
 
     /**
      * Extra field name for the ID of a intent filter pending verification.
@@ -5842,4 +5852,14 @@
     @SystemApi
     public abstract void registerDexModule(String dexModulePath,
             @Nullable DexModuleRegisterCallback callback);
+
+    /**
+     * Returns the {@link ArtManager} associated with this package manager.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull ArtManager getArtManager() {
+        throw new UnsupportedOperationException("getArtManager not implemented in subclass");
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ebeaad7..153c944 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -394,6 +394,7 @@
     public static class PackageLite {
         public final String packageName;
         public final int versionCode;
+        public final int versionCodeMajor;
         public final int installLocation;
         public final VerifierInfo[] verifiers;
 
@@ -436,6 +437,7 @@
                 String[] splitCodePaths, int[] splitRevisionCodes) {
             this.packageName = baseApk.packageName;
             this.versionCode = baseApk.versionCode;
+            this.versionCodeMajor = baseApk.versionCodeMajor;
             this.installLocation = baseApk.installLocation;
             this.verifiers = baseApk.verifiers;
             this.splitNames = splitNames;
@@ -476,6 +478,7 @@
         public final String configForSplit;
         public final String usesSplitName;
         public final int versionCode;
+        public final int versionCodeMajor;
         public final int revisionCode;
         public final int installLocation;
         public final VerifierInfo[] verifiers;
@@ -489,11 +492,11 @@
         public final boolean isolatedSplits;
 
         public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
-                String configForSplit, String usesSplitName, int versionCode, int revisionCode,
-                int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
-                Certificate[][] certificates, boolean coreApp, boolean debuggable,
-                boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs,
-                boolean isolatedSplits) {
+                String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor,
+                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
+                Signature[] signatures, Certificate[][] certificates, boolean coreApp,
+                boolean debuggable, boolean multiArch, boolean use32bitAbi,
+                boolean extractNativeLibs, boolean isolatedSplits) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -501,6 +504,7 @@
             this.configForSplit = configForSplit;
             this.usesSplitName = usesSplitName;
             this.versionCode = versionCode;
+            this.versionCodeMajor = versionCodeMajor;
             this.revisionCode = revisionCode;
             this.installLocation = installLocation;
             this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
@@ -513,6 +517,10 @@
             this.extractNativeLibs = extractNativeLibs;
             this.isolatedSplits = isolatedSplits;
         }
+
+        public long getLongVersionCode() {
+            return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+        }
     }
 
     /**
@@ -663,6 +671,7 @@
         pi.packageName = p.packageName;
         pi.splitNames = p.splitNames;
         pi.versionCode = p.mVersionCode;
+        pi.versionCodeMajor = p.mVersionCodeMajor;
         pi.baseRevisionCode = p.baseRevisionCode;
         pi.splitRevisionCodes = p.splitRevisionCodes;
         pi.versionName = p.mVersionName;
@@ -1880,6 +1889,7 @@
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
         int versionCode = 0;
+        int versionCodeMajor = 0;
         int revisionCode = 0;
         boolean coreApp = false;
         boolean debuggable = false;
@@ -1898,6 +1908,8 @@
                         PARSE_DEFAULT_INSTALL_LOCATION);
             } else if (attr.equals("versionCode")) {
                 versionCode = attrs.getAttributeIntValue(i, 0);
+            } else if (attr.equals("versionCodeMajor")) {
+                versionCodeMajor = attrs.getAttributeIntValue(i, 0);
             } else if (attr.equals("revisionCode")) {
                 revisionCode = attrs.getAttributeIntValue(i, 0);
             } else if (attr.equals("coreApp")) {
@@ -1963,9 +1975,9 @@
         }
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
-                configForSplit, usesSplitName, versionCode, revisionCode, installLocation,
-                verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi,
-                extractNativeLibs, isolatedSplits);
+                configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode,
+                installLocation, verifiers, signatures, certificates, coreApp, debuggable,
+                multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
     }
 
     /**
@@ -2086,8 +2098,11 @@
         TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifest);
 
-        pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
+        pkg.mVersionCode = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
+        pkg.mVersionCodeMajor = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
+        pkg.applicationInfo.versionCode = pkg.getLongVersionCode();
         pkg.baseRevisionCode = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
         pkg.mVersionName = sa.getNonConfigurationString(
@@ -2912,7 +2927,7 @@
                 1, additionalCertSha256Digests.length);
 
         pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname);
-        pkg.usesStaticLibrariesVersions = ArrayUtils.appendInt(
+        pkg.usesStaticLibrariesVersions = ArrayUtils.appendLong(
                 pkg.usesStaticLibrariesVersions, version, true);
         pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
                 pkg.usesStaticLibrariesCertDigests, certSha256Digests, true);
@@ -3867,6 +3882,9 @@
                         com.android.internal.R.styleable.AndroidManifestStaticLibrary_name);
                 final int version = sa.getInt(
                         com.android.internal.R.styleable.AndroidManifestStaticLibrary_version, -1);
+                final int versionMajor = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary_versionMajor,
+                        0);
 
                 sa.recycle();
 
@@ -3894,7 +3912,12 @@
                 }
 
                 owner.staticSharedLibName = lname.intern();
-                owner.staticSharedLibVersion = version;
+                if (version >= 0) {
+                    owner.staticSharedLibVersion =
+                            PackageInfo.composeLongVersionCode(versionMajor, version);
+                } else {
+                    owner.staticSharedLibVersion = version;
+                }
                 ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY;
 
                 XmlUtils.skipCurrentTag(parser);
@@ -5895,11 +5918,11 @@
         public ArrayList<Package> childPackages;
 
         public String staticSharedLibName = null;
-        public int staticSharedLibVersion = 0;
+        public long staticSharedLibVersion = 0;
         public ArrayList<String> libraryNames = null;
         public ArrayList<String> usesLibraries = null;
         public ArrayList<String> usesStaticLibraries = null;
-        public int[] usesStaticLibrariesVersions = null;
+        public long[] usesStaticLibrariesVersions = null;
         public String[][] usesStaticLibrariesCertDigests = null;
         public ArrayList<String> usesOptionalLibraries = null;
         public String[] usesLibraryFiles = null;
@@ -5916,6 +5939,14 @@
         // The version code declared for this package.
         public int mVersionCode;
 
+        // The major version code declared for this package.
+        public int mVersionCodeMajor;
+
+        // Return long containing mVersionCode and mVersionCodeMajor.
+        public long getLongVersionCode() {
+            return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
+        }
+
         // The version name declared for this package.
         public String mVersionName;
 
@@ -6267,6 +6298,11 @@
         }
 
         /** @hide */
+        public boolean isVendor() {
+            return applicationInfo.isVendor();
+        }
+
+        /** @hide */
         public boolean isPrivileged() {
             return applicationInfo.isPrivilegedApp();
         }
@@ -6385,7 +6421,7 @@
             if (staticSharedLibName != null) {
                 staticSharedLibName = staticSharedLibName.intern();
             }
-            staticSharedLibVersion = dest.readInt();
+            staticSharedLibVersion = dest.readLong();
             libraryNames = dest.createStringArrayList();
             internStringArrayList(libraryNames);
             usesLibraries = dest.createStringArrayList();
@@ -6399,8 +6435,8 @@
                 usesStaticLibraries = new ArrayList<>(libCount);
                 dest.readStringList(usesStaticLibraries);
                 internStringArrayList(usesStaticLibraries);
-                usesStaticLibrariesVersions = new int[libCount];
-                dest.readIntArray(usesStaticLibrariesVersions);
+                usesStaticLibrariesVersions = new long[libCount];
+                dest.readLongArray(usesStaticLibrariesVersions);
                 usesStaticLibrariesCertDigests = new String[libCount][];
                 for (int i = 0; i < libCount; i++) {
                     usesStaticLibrariesCertDigests[i] = dest.createStringArray();
@@ -6540,7 +6576,7 @@
             dest.writeParcelableList(childPackages, flags);
 
             dest.writeString(staticSharedLibName);
-            dest.writeInt(staticSharedLibVersion);
+            dest.writeLong(staticSharedLibVersion);
             dest.writeStringList(libraryNames);
             dest.writeStringList(usesLibraries);
             dest.writeStringList(usesOptionalLibraries);
@@ -6551,7 +6587,7 @@
             } else {
                 dest.writeInt(usesStaticLibraries.size());
                 dest.writeStringList(usesStaticLibraries);
-                dest.writeIntArray(usesStaticLibrariesVersions);
+                dest.writeLongArray(usesStaticLibrariesVersions);
                 for (String[] usesStaticLibrariesCertDigest : usesStaticLibrariesCertDigests) {
                     dest.writeStringArray(usesStaticLibrariesCertDigest);
                 }
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 7588762..551d53b 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -144,6 +144,15 @@
     public static final int PROTECTION_FLAG_OEM = 0x4000;
 
     /**
+     * Additional flag for {${link #protectionLevel}, corresponding
+     * to the <code>vendorPrivileged</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000;
+
+    /**
      * Mask for {@link #protectionLevel}: the basic protection type.
      */
     public static final int PROTECTION_MASK_BASE = 0xf;
@@ -231,6 +240,12 @@
         if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
             level = PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED;
         }
+        if ((level & PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0
+                && (level & PROTECTION_FLAG_PRIVILEGED) == 0) {
+            // 'vendorPrivileged' must be 'privileged'. If not,
+            // drop the vendorPrivileged.
+            level = level & ~PROTECTION_FLAG_VENDOR_PRIVILEGED;
+        }
         return level;
     }
 
@@ -284,6 +299,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_OEM) != 0) {
             protLevel += "|oem";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
+            protLevel += "|vendorPrivileged";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index aea843a..56d61ef 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -361,7 +361,7 @@
         }
         IntArray updatedUids = null;
         for (ServiceInfo<V> service : allServices) {
-            int versionCode = service.componentInfo.applicationInfo.versionCode;
+            long versionCode = service.componentInfo.applicationInfo.versionCode;
             String pkg = service.componentInfo.packageName;
             ApplicationInfo newAppInfo = null;
             try {
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 7d301a3..2f1b256 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -73,8 +73,7 @@
 
     private final String mName;
 
-    // TODO: Make long when we change the paltform to use longs
-    private final int mVersion;
+    private final long mVersion;
     private final @Type int mType;
     private final VersionedPackage mDeclaringPackage;
     private final List<VersionedPackage> mDependentPackages;
@@ -90,7 +89,7 @@
      *
      * @hide
      */
-    public SharedLibraryInfo(String name, int version, int type,
+    public SharedLibraryInfo(String name, long version, int type,
             VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages) {
         mName = name;
         mVersion = version;
@@ -100,7 +99,7 @@
     }
 
     private SharedLibraryInfo(Parcel parcel) {
-        this(parcel.readString(), parcel.readInt(), parcel.readInt(),
+        this(parcel.readString(), parcel.readLong(), parcel.readInt(),
                 parcel.readParcelable(null), parcel.readArrayList(null));
     }
 
@@ -124,6 +123,14 @@
     }
 
     /**
+     * @deprecated Use {@link #getLongVersion()} instead.
+     */
+    @Deprecated
+    public @IntRange(from = -1) int getVersion() {
+        return mVersion < 0 ? (int) mVersion : (int) (mVersion & 0x7fffffff);
+    }
+
+    /**
      * Gets the version of the library. For {@link #TYPE_STATIC static} libraries
      * this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and
      * {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these
@@ -131,7 +138,7 @@
      *
      * @return The version.
      */
-    public @IntRange(from = -1) int getVersion() {
+    public @IntRange(from = -1) long getLongVersion() {
         return mVersion;
     }
 
@@ -192,7 +199,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mName);
-        parcel.writeInt(mVersion);
+        parcel.writeLong(mVersion);
         parcel.writeInt(mType);
         parcel.writeParcelable(mDeclaringPackage, flags);
         parcel.writeList(mDependentPackages);
diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java
index 29c5efe..3953466 100644
--- a/core/java/android/content/pm/VersionedPackage.java
+++ b/core/java/android/content/pm/VersionedPackage.java
@@ -28,7 +28,7 @@
  */
 public final class VersionedPackage implements Parcelable {
     private final String mPackageName;
-    private final int mVersionCode;
+    private final long mVersionCode;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -47,9 +47,21 @@
         mVersionCode = versionCode;
     }
 
+    /**
+     * Creates a new instance. Use {@link PackageManager#VERSION_CODE_HIGHEST}
+     * to refer to the highest version code of this package.
+     * @param packageName The package name.
+     * @param versionCode The version code.
+     */
+    public VersionedPackage(@NonNull String packageName,
+            @VersionCode long versionCode) {
+        mPackageName = packageName;
+        mVersionCode = versionCode;
+    }
+
     private VersionedPackage(Parcel parcel) {
         mPackageName = parcel.readString();
-        mVersionCode = parcel.readInt();
+        mVersionCode = parcel.readLong();
     }
 
     /**
@@ -62,11 +74,19 @@
     }
 
     /**
+     * @deprecated use {@link #getLongVersionCode()} instead.
+     */
+    @Deprecated
+    public @VersionCode int getVersionCode() {
+        return (int) (mVersionCode & 0x7fffffff);
+    }
+
+    /**
      * Gets the version code.
      *
      * @return The version code.
      */
-    public @VersionCode int getVersionCode() {
+    public @VersionCode long getLongVersionCode() {
         return mVersionCode;
     }
 
@@ -83,7 +103,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mPackageName);
-        parcel.writeInt(mVersionCode);
+        parcel.writeLong(mVersionCode);
     }
 
     public static final Creator<VersionedPackage> CREATOR = new Creator<VersionedPackage>() {
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
new file mode 100644
index 0000000..201cd8d
--- /dev/null
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Class for retrieving various kinds of information related to the runtime artifacts of
+ * packages that are currently installed on the device.
+ *
+ * @hide
+ */
+@SystemApi
+public class ArtManager {
+    private static final String TAG = "ArtManager";
+
+    /** The snapshot failed because the package was not found. */
+    public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0;
+    /** The snapshot failed because the package code path does not exist. */
+    public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1;
+    /** The snapshot failed because of an internal error (e.g. error during opening profiles). */
+    public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2;
+
+    private IArtManager mArtManager;
+
+    /**
+     * @hide
+     */
+    public ArtManager(@NonNull IArtManager manager) {
+        mArtManager = manager;
+    }
+
+    /**
+     * Snapshots the runtime profile for an apk belonging to the package {@code packageName}.
+     * The apk is identified by {@code codePath}. The calling process must have
+     * {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * The result will be posted on {@code handler} using the given {@code callback}.
+     * The profile being available as a read-only {@link android.os.ParcelFileDescriptor}.
+     *
+     * @param packageName the target package name
+     * @param codePath the code path for which the profile should be retrieved
+     * @param callback the callback which should be used for the result
+     * @param handler the handler which should be used to post the result
+     */
+    @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+    public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath,
+            @NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) {
+        Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
+
+        SnapshotRuntimeProfileCallbackDelegate delegate =
+                new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper());
+        try {
+            mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns true if runtime profiles are enabled, false otherwise.
+     *
+     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+    public boolean isRuntimeProfilingEnabled() {
+        try {
+            return mArtManager.isRuntimeProfilingEnabled();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Callback used for retrieving runtime profiles.
+     */
+    public abstract static class SnapshotRuntimeProfileCallback {
+        /**
+         * Called when the profile snapshot finished with success.
+         *
+         * @param profileReadFd the file descriptor that can be used to read the profile. Note that
+         *                      the file might be empty (which is valid profile).
+         */
+        public abstract void onSuccess(ParcelFileDescriptor profileReadFd);
+
+        /**
+         * Called when the profile snapshot finished with an error.
+         *
+         * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND,
+         *      SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}.
+         */
+        public abstract void onError(int errCode);
+    }
+
+    private static class SnapshotRuntimeProfileCallbackDelegate
+            extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub
+            implements Handler.Callback {
+        private static final int MSG_SNAPSHOT_OK = 1;
+        private static final int MSG_ERROR = 2;
+        private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
+        private final Handler mHandler;
+
+        private SnapshotRuntimeProfileCallbackDelegate(
+                ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) {
+            mCallback = callback;
+            mHandler = new Handler(looper, this);
+        }
+
+        @Override
+        public void onSuccess(ParcelFileDescriptor profileReadFd) {
+            mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget();
+        }
+
+        @Override
+        public void onError(int errCode) {
+            mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget();
+        }
+
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SNAPSHOT_OK:
+                    mCallback.onSuccess((ParcelFileDescriptor) msg.obj);
+                    break;
+                case MSG_ERROR:
+                    mCallback.onError(msg.arg1);
+                    break;
+                default: return false;
+            }
+            return true;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl
new file mode 100644
index 0000000..8cbb452
--- /dev/null
+++ b/core/java/android/content/pm/dex/IArtManager.aidl
@@ -0,0 +1,44 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm.dex;
+
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+
+/**
+ * A system service that provides access to runtime and compiler artifacts.
+ *
+ * @hide
+ */
+interface IArtManager {
+    /**
+     * Snapshots the runtime profile for an apk belonging to the package {@param packageName}.
+     * The apk is identified by {@param codePath}. The calling process must have
+     * {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * The result will be posted on {@param callback} with the profile being available as a
+     * read-only {@link android.os.ParcelFileDescriptor}.
+     */
+    oneway void snapshotRuntimeProfile(in String packageName,
+        in String codePath, in ISnapshotRuntimeProfileCallback callback);
+
+    /**
+     * Returns true if runtime profiles are enabled, false otherwise.
+     *
+     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     */
+    boolean isRuntimeProfilingEnabled();
+}
diff --git a/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl
new file mode 100644
index 0000000..3b4838f
--- /dev/null
+++ b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm.dex;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Callback used to post the result of a profile-snapshot operation.
+ *
+ * @hide
+ */
+oneway interface ISnapshotRuntimeProfileCallback {
+    void onSuccess(in ParcelFileDescriptor profileReadFd);
+    void onError(int errCode);
+}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index d6e62cf..f82627b 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -21,6 +21,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
 
 import java.lang.annotation.Retention;
@@ -34,6 +35,8 @@
  * Internet Protocol</a>
  */
 public final class IpSecAlgorithm implements Parcelable {
+    private static final String TAG = "IpSecAlgorithm";
+
     /**
      * AES-CBC Encryption/Ciphering Algorithm.
      *
@@ -45,6 +48,7 @@
      * MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
+     * <p>Keys for this algorithm must be 128 bits in length.
      * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
      */
     public static final String AUTH_HMAC_MD5 = "hmac(md5)";
@@ -53,6 +57,7 @@
      * SHA1 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
+     * <p>Keys for this algorithm must be 160 bits in length.
      * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
      */
     public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
@@ -60,6 +65,7 @@
     /**
      * SHA256 HMAC Authentication/Integrity Algorithm.
      *
+     * <p>Keys for this algorithm must be 256 bits in length.
      * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
      */
     public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -67,6 +73,7 @@
     /**
      * SHA384 HMAC Authentication/Integrity Algorithm.
      *
+     * <p>Keys for this algorithm must be 384 bits in length.
      * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
      */
     public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
@@ -74,6 +81,7 @@
     /**
      * SHA512 HMAC Authentication/Integrity Algorithm.
      *
+     * <p>Keys for this algorithm must be 512 bits in length.
      * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
      */
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
@@ -130,12 +138,10 @@
      * @param truncLenBits number of bits of output hash to use.
      */
     public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
-        if (!isTruncationLengthValid(algorithm, truncLenBits)) {
-            throw new IllegalArgumentException("Unknown algorithm or invalid length");
-        }
         mName = algorithm;
         mKey = key.clone();
-        mTruncLenBits = Math.min(truncLenBits, key.length * 8);
+        mTruncLenBits = truncLenBits;
+        checkValidOrThrow(mName, mKey.length * 8, mTruncLenBits);
     }
 
     /** Get the algorithm name */
@@ -169,7 +175,11 @@
     public static final Parcelable.Creator<IpSecAlgorithm> CREATOR =
             new Parcelable.Creator<IpSecAlgorithm>() {
                 public IpSecAlgorithm createFromParcel(Parcel in) {
-                    return new IpSecAlgorithm(in);
+                    final String name = in.readString();
+                    final byte[] key = in.createByteArray();
+                    final int truncLenBits = in.readInt();
+
+                    return new IpSecAlgorithm(name, key, truncLenBits);
                 }
 
                 public IpSecAlgorithm[] newArray(int size) {
@@ -177,30 +187,47 @@
                 }
             };
 
-    private IpSecAlgorithm(Parcel in) {
-        mName = in.readString();
-        mKey = in.createByteArray();
-        mTruncLenBits = in.readInt();
-    }
+    private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
+        boolean isValidLen = true;
+        boolean isValidTruncLen = true;
 
-    private static boolean isTruncationLengthValid(String algo, int truncLenBits) {
-        switch (algo) {
+        switch(name) {
             case CRYPT_AES_CBC:
-                return (truncLenBits == 128 || truncLenBits == 192 || truncLenBits == 256);
+                isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256;
+                break;
             case AUTH_HMAC_MD5:
-                return (truncLenBits >= 96 && truncLenBits <= 128);
+                isValidLen = keyLen == 128;
+                isValidTruncLen = truncLen >= 96 && truncLen <= 128;
+                break;
             case AUTH_HMAC_SHA1:
-                return (truncLenBits >= 96 && truncLenBits <= 160);
+                isValidLen = keyLen == 160;
+                isValidTruncLen = truncLen >= 96 && truncLen <= 160;
+                break;
             case AUTH_HMAC_SHA256:
-                return (truncLenBits >= 96 && truncLenBits <= 256);
+                isValidLen = keyLen == 256;
+                isValidTruncLen = truncLen >= 96 && truncLen <= 256;
+                break;
             case AUTH_HMAC_SHA384:
-                return (truncLenBits >= 192 && truncLenBits <= 384);
+                isValidLen = keyLen == 384;
+                isValidTruncLen = truncLen >= 192 && truncLen <= 384;
+                break;
             case AUTH_HMAC_SHA512:
-                return (truncLenBits >= 256 && truncLenBits <= 512);
+                isValidLen = keyLen == 512;
+                isValidTruncLen = truncLen >= 256 && truncLen <= 512;
+                break;
             case AUTH_CRYPT_AES_GCM:
-                return (truncLenBits == 64 || truncLenBits == 96 || truncLenBits == 128);
+                // The keying material for GCM is a key plus a 32-bit salt
+                isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+                break;
             default:
-                return false;
+                throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
+        }
+
+        if (!isValidLen) {
+            throw new IllegalArgumentException("Invalid key material keyLength: " + keyLen);
+        }
+        if (!isValidTruncLen) {
+            throw new IllegalArgumentException("Invalid truncation keyLength: " + truncLen);
         }
     }
 
@@ -217,8 +244,9 @@
                 .toString();
     }
 
-    /** package */
-    static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) {
+    /** @hide */
+    @VisibleForTesting
+    public static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) {
         if (lhs == null || rhs == null) return (lhs == rhs);
         return (lhs.mName.equals(rhs.mName)
                 && Arrays.equals(lhs.mKey, rhs.mKey)
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index f6a69ba..5620a62 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -16,17 +16,23 @@
 
 package android.net;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.BitUtils;
+import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Random;
-import java.util.StringJoiner;
 
 /**
- * Represents a mac address.
+ * Representation of a MAC address.
+ *
+ * This class only supports 48 bits long addresses and does not support 64 bits long addresses.
+ * Instances of this class are immutable.
  *
  * @hide
  */
@@ -35,76 +41,102 @@
     private static final int ETHER_ADDR_LEN = 6;
     private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
 
-    /** The broadcast mac address.  */
-    public static final MacAddress BROADCAST_ADDRESS = new MacAddress(ETHER_ADDR_BROADCAST);
+    /**
+     * The MacAddress representing the unique broadcast MAC address.
+     */
+    public static final MacAddress BROADCAST_ADDRESS = MacAddress.fromBytes(ETHER_ADDR_BROADCAST);
 
-    /** The zero mac address. */
+    /**
+     * The MacAddress zero MAC address.
+     * @hide
+     */
     public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
 
-    /** Represents categories of mac addresses. */
-    public enum MacAddressType {
-        UNICAST,
-        MULTICAST,
-        BROADCAST;
-    }
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_UNKNOWN,
+            TYPE_UNICAST,
+            TYPE_MULTICAST,
+            TYPE_BROADCAST,
+    })
+    public @interface MacAddressType { }
 
-    private static final long VALID_LONG_MASK = BROADCAST_ADDRESS.mAddr;
-    private static final long LOCALLY_ASSIGNED_MASK = new MacAddress("2:0:0:0:0:0").mAddr;
-    private static final long MULTICAST_MASK = new MacAddress("1:0:0:0:0:0").mAddr;
-    private static final long OUI_MASK = new MacAddress("ff:ff:ff:0:0:0").mAddr;
-    private static final long NIC_MASK = new MacAddress("0:0:0:ff:ff:ff").mAddr;
-    private static final MacAddress BASE_ANDROID_MAC = new MacAddress("da:a1:19:0:0:0");
+    /** 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;
+    /** Indicates a MAC address is a multicast address. */
+    public static final int TYPE_MULTICAST = 2;
+    /** Indicates a MAC address is the broadcast address. */
+    public static final int TYPE_BROADCAST = 3;
 
-    // Internal representation of the mac address as a single 8 byte long.
+    private static final long VALID_LONG_MASK = (1L << 48) - 1;
+    private static final long LOCALLY_ASSIGNED_MASK = MacAddress.fromString("2:0:0:0:0:0").mAddr;
+    private static final long MULTICAST_MASK = MacAddress.fromString("1:0:0:0:0:0").mAddr;
+    private static final long OUI_MASK = MacAddress.fromString("ff:ff:ff:0:0:0").mAddr;
+    private static final long NIC_MASK = MacAddress.fromString("0:0:0:ff:ff:ff").mAddr;
+    private static final MacAddress BASE_GOOGLE_MAC = MacAddress.fromString("da:a1:19:0:0:0");
+
+    // Internal representation of the MAC address as a single 8 byte long.
     // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
-    // mac address are encoded in the 6 least significant bytes of the long, where the first
+    // MAC address are encoded in the 6 least significant bytes of the long, where the first
     // byte of the array is mapped to the 3rd highest logical byte of the long, the second
     // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
     private final long mAddr;
 
     private MacAddress(long addr) {
-        mAddr = addr;
+        mAddr = (VALID_LONG_MASK & addr);
     }
 
-    /** Creates a MacAddress for the given byte representation. */
-    public MacAddress(byte[] addr) {
-        this(longAddrFromByteAddr(addr));
-    }
-
-    /** Creates a MacAddress for the given string representation. */
-    public MacAddress(String addr) {
-        this(longAddrFromByteAddr(byteAddrFromStringAddr(addr)));
-    }
-
-    /** Returns the MacAddressType of this MacAddress. */
-    public MacAddressType addressType() {
+    /**
+     * Returns the type of this address.
+     *
+     * @return the int constant representing the MAC address type of this MacAddress.
+     */
+    public @MacAddressType int addressType() {
         if (equals(BROADCAST_ADDRESS)) {
-            return MacAddressType.BROADCAST;
+            return TYPE_BROADCAST;
         }
         if (isMulticastAddress()) {
-            return MacAddressType.MULTICAST;
+            return TYPE_MULTICAST;
         }
-        return MacAddressType.UNICAST;
+        return TYPE_UNICAST;
     }
 
-    /** Returns true if this MacAddress corresponds to a multicast address. */
+    /**
+     * @return true if this MacAddress is a multicast address.
+     * @hide
+     */
     public boolean isMulticastAddress() {
         return (mAddr & MULTICAST_MASK) != 0;
     }
 
-    /** Returns true if this MacAddress corresponds to a locally assigned address. */
+    /**
+     * @return true if this MacAddress is a locally assigned address.
+     */
     public boolean isLocallyAssigned() {
         return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
     }
 
-    /** Returns a byte array representation of this MacAddress. */
+    /**
+     * @return a byte array representation of this MacAddress.
+     */
     public byte[] toByteArray() {
         return byteAddrFromLongAddr(mAddr);
     }
 
     @Override
     public String toString() {
-        return stringAddrFromByteAddr(byteAddrFromLongAddr(mAddr));
+        return stringAddrFromLongAddr(mAddr);
+    }
+
+    /**
+     * @return a String representation of the OUI part of this MacAddres,
+     * with the lower 3 bytes constituting the NIC part replaced with 0.
+     */
+    public String toSafeString() {
+        return stringAddrFromLongAddr(mAddr & OUI_MASK);
     }
 
     @Override
@@ -138,27 +170,50 @@
                 }
             };
 
-    /** Return true if the given byte array is not null and has the length of a mac address. */
+    /**
+     * Returns true if the given byte array is an valid MAC address.
+     * A valid byte array representation for a MacAddress is a non-null array of length 6.
+     *
+     * @param addr a byte array.
+     * @return true if the given byte array is not null and has the length of a MAC address.
+     *
+     * @hide
+     */
     public static boolean isMacAddress(byte[] addr) {
         return addr != null && addr.length == ETHER_ADDR_LEN;
     }
 
     /**
-     * Return the MacAddressType of the mac address represented by the given byte array,
-     * or null if the given byte array does not represent an mac address.
+     * Returns the MAC address type of the MAC address represented by the given byte array,
+     * or null if the given byte array does not represent a MAC address.
+     * A valid byte array representation for a MacAddress is a non-null array of length 6.
+     *
+     * @param addr a byte array representing a MAC address.
+     * @return the int constant representing the MAC address type of the MAC address represented
+     * by the given byte array, or type UNKNOWN if the byte array is not a valid MAC address.
+     *
+     * @hide
      */
-    public static MacAddressType macAddressType(byte[] addr) {
+    public static int macAddressType(byte[] addr) {
         if (!isMacAddress(addr)) {
-            return null;
+            return TYPE_UNKNOWN;
         }
-        return new MacAddress(addr).addressType();
+        return MacAddress.fromBytes(addr).addressType();
     }
 
-    /** DOCME */
+    /**
+     * Converts a String representation of a MAC address to a byte array representation.
+     * A valid String representation for a MacAddress is a series of 6 values in the
+     * range [0,ff] printed in hexadecimal and joined by ':' characters.
+     *
+     * @param addr a String representation of a MAC address.
+     * @return the byte representation of the MAC address.
+     * @throws IllegalArgumentException if the given String is not a valid representation.
+     *
+     * @hide
+     */
     public static byte[] byteAddrFromStringAddr(String addr) {
-        if (addr == null) {
-            throw new IllegalArgumentException("cannot convert the null String");
-        }
+        Preconditions.checkNotNull(addr);
         String[] parts = addr.split(":");
         if (parts.length != ETHER_ADDR_LEN) {
             throw new IllegalArgumentException(addr + " was not a valid MAC address");
@@ -174,20 +229,26 @@
         return bytes;
     }
 
-    /** DOCME */
+    /**
+     * Converts a byte array representation of a MAC address to a String representation made
+     * of 6 hexadecimal numbers in [0,ff] joined by ':' characters.
+     * A valid byte array representation for a MacAddress is a non-null array of length 6.
+     *
+     * @param addr a byte array representation of a MAC address.
+     * @return the String representation of the MAC address.
+     * @throws IllegalArgumentException if the given byte array is not a valid representation.
+     *
+     * @hide
+     */
     public static String stringAddrFromByteAddr(byte[] addr) {
         if (!isMacAddress(addr)) {
             return null;
         }
-        StringJoiner j = new StringJoiner(":");
-        for (byte b : addr) {
-            j.add(Integer.toHexString(BitUtils.uint8(b)));
-        }
-        return j.toString();
+        return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+                addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
     }
 
-    /** @hide */
-    public static byte[] byteAddrFromLongAddr(long addr) {
+    private static byte[] byteAddrFromLongAddr(long addr) {
         byte[] bytes = new byte[ETHER_ADDR_LEN];
         int index = ETHER_ADDR_LEN;
         while (index-- > 0) {
@@ -197,8 +258,8 @@
         return bytes;
     }
 
-    /** @hide */
-    public static long longAddrFromByteAddr(byte[] addr) {
+    private static long longAddrFromByteAddr(byte[] addr) {
+        Preconditions.checkNotNull(addr);
         if (!isMacAddress(addr)) {
             throw new IllegalArgumentException(
                     Arrays.toString(addr) + " was not a valid MAC address");
@@ -210,19 +271,17 @@
         return longAddr;
     }
 
-    /** @hide */
-    public static long longAddrFromStringAddr(String addr) {
-        if (addr == null) {
-            throw new IllegalArgumentException("cannot convert the null String");
-        }
+    // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
+    // that avoids the allocation of an intermediary byte[].
+    private static long longAddrFromStringAddr(String addr) {
+        Preconditions.checkNotNull(addr);
         String[] parts = addr.split(":");
         if (parts.length != ETHER_ADDR_LEN) {
             throw new IllegalArgumentException(addr + " was not a valid MAC address");
         }
         long longAddr = 0;
-        int index = ETHER_ADDR_LEN;
-        while (index-- > 0) {
-            int x = Integer.valueOf(parts[index], 16);
+        for (int i = 0; i < parts.length; i++) {
+            int x = Integer.valueOf(parts[i], 16);
             if (x < 0 || 0xff < x) {
                 throw new IllegalArgumentException(addr + "was not a valid MAC address");
             }
@@ -231,32 +290,74 @@
         return longAddr;
     }
 
-    /** @hide */
-    public static String stringAddrFromLongAddr(long addr) {
-        addr = Long.reverseBytes(addr) >> 16;
-        StringJoiner j = new StringJoiner(":");
-        for (int i = 0; i < ETHER_ADDR_LEN; i++) {
-            j.add(Integer.toHexString((byte) addr));
-            addr = addr >> 8;
-        }
-        return j.toString();
+    // Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
+    // that avoids the allocation of an intermediary byte[].
+    private static String stringAddrFromLongAddr(long addr) {
+        return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+                (addr >> 40) & 0xff,
+                (addr >> 32) & 0xff,
+                (addr >> 24) & 0xff,
+                (addr >> 16) & 0xff,
+                (addr >> 8) & 0xff,
+                addr & 0xff);
     }
 
     /**
-     * Returns a randomely generated mac address with the Android OUI value "DA-A1-19".
-     * The locally assigned bit is always set to 1.
+     * Creates a MacAddress from the given String representation. A valid String representation
+     * for a MacAddress is a series of 6 values in the range [0,ff] printed in hexadecimal
+     * and joined by ':' characters.
+     *
+     * @param addr a String representation of a MAC address.
+     * @return the MacAddress corresponding to the given String representation.
+     * @throws IllegalArgumentException if the given String is not a valid representation.
      */
-    public static MacAddress getRandomAddress() {
-        return getRandomAddress(BASE_ANDROID_MAC, new Random());
+    public static MacAddress fromString(String addr) {
+        return new MacAddress(longAddrFromStringAddr(addr));
     }
 
     /**
-     * Returns a randomely generated mac address using the given Random object and the same
-     * OUI values as the given MacAddress. The locally assigned bit is always set to 1.
+     * Creates a MacAddress from the given byte array representation.
+     * A valid byte array representation for a MacAddress is a non-null array of length 6.
+     *
+     * @param addr a byte array representation of a MAC address.
+     * @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 getRandomAddress(MacAddress base, Random r) {
-        long longAddr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong()) | LOCALLY_ASSIGNED_MASK;
-        return new MacAddress(longAddr);
+    public static MacAddress fromBytes(byte[] addr) {
+        return new MacAddress(longAddrFromByteAddr(addr));
+    }
+
+    /**
+     * Returns a generated MAC address whose 24 least significant bits constituting the
+     * NIC part of the address are randomly selected.
+     *
+     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+     *
+     * @return a random locally assigned MacAddress.
+     *
+     * @hide
+     */
+    public static MacAddress createRandomUnicastAddress() {
+        return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
+    }
+
+    /**
+     * Returns a randomly generated MAC address using the given Random object and the same
+     * OUI values as the given MacAddress.
+     *
+     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+     *
+     * @param base a base MacAddress whose OUI is used for generating the random address.
+     * @param r a standard Java Random object used for generating the random address.
+     * @return a random locally assigned MacAddress.
+     *
+     * @hide
+     */
+    public static 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;
+        return new MacAddress(addr);
     }
 
     // Convenience function for working around the lack of byte literals.
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index c339856..954e59c 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.app.DownloadManager;
 import android.app.backup.BackupManager;
@@ -30,6 +31,8 @@
 
 import dalvik.system.SocketTagger;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
 import java.net.DatagramSocket;
 import java.net.Socket;
 import java.net.SocketException;
@@ -264,14 +267,25 @@
     }
 
     /**
+     * Set specific UID to use when accounting {@link Socket} traffic
+     * originating from the current thread as the calling UID. Designed for use
+     * when another application is performing operations on your behalf.
+     * <p>
+     * Changes only take effect during subsequent calls to
+     * {@link #tagSocket(Socket)}.
+     */
+    public static void setThreadStatsUidSelf() {
+        setThreadStatsUid(android.os.Process.myUid());
+    }
+
+    /**
      * Clear any active UID set to account {@link Socket} traffic originating
      * from the current thread.
      *
      * @see #setThreadStatsUid(int)
-     * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @SuppressLint("Doclava125")
     public static void clearThreadStatsUid() {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
     }
@@ -316,6 +330,27 @@
     }
 
     /**
+     * Tag the given {@link FileDescriptor} socket with any statistics
+     * parameters active for the current thread. Subsequent calls always replace
+     * any existing parameters. When finished, call
+     * {@link #untagFileDescriptor(FileDescriptor)} to remove statistics
+     * parameters.
+     *
+     * @see #setThreadStatsTag(int)
+     */
+    public static void tagFileDescriptor(FileDescriptor fd) throws IOException {
+        SocketTagger.get().tag(fd);
+    }
+
+    /**
+     * Remove any statistics parameters from the given {@link FileDescriptor}
+     * socket.
+     */
+    public static void untagFileDescriptor(FileDescriptor fd) throws IOException {
+        SocketTagger.get().untag(fd);
+    }
+
+    /**
      * Start profiling data usage for current UID. Only one profiling session
      * can be active at a time.
      *
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 23c1f20..7277ba3 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -16,6 +16,7 @@
 
 package android.net.metrics;
 
+import android.net.MacAddress;
 import android.os.Process;
 import android.os.SystemClock;
 import android.util.SparseIntArray;
@@ -80,13 +81,13 @@
         }
 
         switch (ev.dstHwAddr.addressType()) {
-            case UNICAST:
+            case MacAddress.TYPE_UNICAST:
                 l2UnicastCount++;
                 break;
-            case MULTICAST:
+            case MacAddress.TYPE_MULTICAST:
                 l2MulticastCount++;
                 break;
-            case BROADCAST:
+            case MacAddress.TYPE_BROADCAST:
                 l2BroadcastCount++;
                 break;
             default:
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 811091e..948d4ce 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1180,7 +1180,7 @@
     public static final class PackageChange {
         public String mPackageName;
         public boolean mUpdate;
-        public int mVersionCode;
+        public long mVersionCode;
     }
 
     public static final class DailyItem {
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index b814b46..3db12ed 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -55,12 +55,12 @@
      * Inform statsd what the version and package are for each uid. Note that each array should
      * have the same number of elements, and version[i] and package[i] correspond to uid[i].
      */
-    oneway void informAllUidData(in int[] uid, in int[] version, in String[] app);
+    oneway void informAllUidData(in int[] uid, in long[] version, in String[] app);
 
     /**
      * Inform statsd what the uid and version are for one app that was updated.
      */
-    oneway void informOnePackage(in String app, in int uid, in int version);
+    oneway void informOnePackage(in String app, in int uid, in long version);
 
     /**
      * Inform stats that an app was removed.
@@ -69,11 +69,16 @@
 
     /**
      * Fetches data for the specified configuration key. Returns a byte array representing proto
-     * wire-encoded of ConfigMetricsReport.
+     * wire-encoded of ConfigMetricsReportList.
      */
     byte[] getData(in String key);
 
     /**
+     * Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
+     */
+    byte[] getMetadata();
+
+    /**
      * Sets a configuration with the specified config key and subscribes to updates for this
      * configuration key. Broadcasts will be sent if this configuration needs to be collected.
      * The configuration must be a wire-encoded StatsDConfig. The caller specifies the name of the
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 068f5f7..01fe5bf 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -680,6 +680,26 @@
      * as the user moves between applications and doesn't require a special permission.
      * </p>
      *
+     * <p>
+     * Recommended naming conventions for tags to make debugging easier:
+     * <ul>
+     * <li>use a unique prefix delimited by a colon for your app/library (e.g.
+     * gmail:mytag) to make it easier to understand where the wake locks comes
+     * from. This namespace will also avoid collision for tags inside your app
+     * coming from different libraries which will make debugging easier.
+     * <li>use constants (e.g. do not include timestamps in the tag) to make it
+     * easier for tools to aggregate similar wake locks. When collecting
+     * debugging data, the platform only monitors a finite number of tags,
+     * using constants will help tools to provide better debugging data.
+     * <li>avoid using Class#getName() or similar method since this class name
+     * can be transformed by java optimizer and obfuscator tools.
+     * <li>avoid wrapping the tag or a prefix to avoid collision with wake lock
+     * tags from the platform (e.g. *alarm*).
+     * <li>never include personnally identifiable information for privacy
+     * reasons.
+     * </ul>
+     * </p>
+     *
      * @param levelAndFlags Combination of wake lock level and flag values defining
      * the requested behavior of the WakeLock.
      * @param tag Your class name (or other tag) for debugging purposes.
@@ -1509,6 +1529,13 @@
          * cost of that work can be accounted to the application.
          * </p>
          *
+         * <p>
+         * Make sure to follow the tag naming convention when using WorkSource
+         * to make it easier for app developers to understand wake locks
+         * attributed to them. See {@link PowerManager#newWakeLock(int, String)}
+         * documentation.
+         * </p>
+         *
          * @param ws The work source, or null if none.
          */
         public void setWorkSource(WorkSource ws) {
diff --git a/core/java/android/print/IPrintClient.aidl b/core/java/android/print/IPrintClient.aidl
deleted file mode 100644
index 3f39d08..0000000
--- a/core/java/android/print/IPrintClient.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print;
-
-import android.content.IntentSender;
-
-/**
- * Interface for communication with a printing app.
- *
- * @see android.print.IPrintClientCallback
- *
- * @hide
- */
-oneway interface IPrintClient {
-    void startPrintJobConfigActivity(in IntentSender intent);
-}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index cc1c067..ec5b1c6 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemApi;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
@@ -42,10 +43,12 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.RemoteException;
+import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Pair;
 import android.view.View;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -4237,6 +4240,25 @@
          * current carrier. An allowed bitmask of {@link #CARRIER_PRESENCE}.
          */
         public static final int CARRIER_PRESENCE_VT_CAPABLE = 0x01;
+
+        /**
+         * The flattened {@link android.content.ComponentName} of a  {@link
+         * android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
+         * call the contact with. Used by {@link CommonDataKinds.Phone}.
+         *
+         * @see PhoneAccountHandle#getComponentName()
+         * @see ComponentName#flattenToString()
+         */
+        String PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME = "preferred_phone_account_component_name";
+
+        /**
+         * The ID of a  {@link
+         * android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
+         * call the contact with. Used by {@link CommonDataKinds.Phone}.
+         *
+         * @see PhoneAccountHandle#getId() ()
+         */
+        String PREFERRED_PHONE_ACCOUNT_ID = "preferred_phone_account_id";
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c4c6798..5505f59 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3123,6 +3123,10 @@
          * to dream after a period of inactivity.  This value is also known as the
          * user activity timeout period since the screen isn't necessarily turned off
          * when it expires.
+         *
+         * <p>
+         * This value is bounded by maximum timeout set by
+         * {@link android.app.admin.DevicePolicyManager#setMaximumTimeToLock(ComponentName, long)}.
          */
         public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
 
@@ -5333,6 +5337,42 @@
         public static final String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
 
         /**
+         * Experimental autofill feature.
+         *
+         * <p>TODO(b/67867469): document (or remove) once feature is finished
+         * @hide
+         */
+        public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
+                "autofill_user_data_max_user_data_size";
+
+        /**
+         * Experimental autofill feature.
+         *
+         * <p>TODO(b/67867469): document (or remove) once feature is finished
+         * @hide
+         */
+        public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
+                "autofill_user_data_max_field_classification_size";
+
+        /**
+         * Experimental autofill feature.
+         *
+         * <p>TODO(b/67867469): document (or remove) once feature is finished
+         * @hide
+         */
+        public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
+                "autofill_user_data_max_value_length";
+
+        /**
+         * Experimental autofill feature.
+         *
+         * <p>TODO(b/67867469): document (or remove) once feature is finished
+         * @hide
+         */
+        public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
+                "autofill_user_data_min_value_length";
+
+        /**
          * @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
          */
         @Deprecated
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 864a0fd..6be0e76 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -16,7 +16,6 @@
 
 package android.provider;
 
-import android.Manifest;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
@@ -50,7 +49,7 @@
  * </ul>
  *
  * <P> The minimum permission needed to access this content provider is
- * {@link Manifest.permission#ADD_VOICEMAIL}
+ * {@link android.Manifest.permission#ADD_VOICEMAIL}
  *
  * <P>Voicemails are inserted by what is called as a "voicemail source"
  * application, which is responsible for syncing voicemail data between a remote
@@ -293,11 +292,26 @@
          * Flag used to indicate that local, unsynced changes are present.
          * Currently, this is used to indicate that the voicemail was read or deleted.
          * The value will be 1 if dirty is true, 0 if false.
+         *
+         * <p>When a caller updates a voicemail row (either with {@link ContentResolver#update} or
+         * {@link ContentResolver#applyBatch}), and if the {@link ContentValues} doesn't contain
+         * this column, the voicemail provider implicitly sets it to 0 if the calling package is
+         * the {@link #SOURCE_PACKAGE} or to 1 otherwise. To prevent this behavior, explicitly set
+         * {@link #DIRTY_RETAIN} to this column in the {@link ContentValues}.
+         *
          * <P>Type: INTEGER (boolean)</P>
+         *
+         * @see #DIRTY_RETAIN
          */
         public static final String DIRTY = "dirty";
 
         /**
+         * Value of {@link #DIRTY} when updating to indicate that the value should not be updated
+         * during this operation.
+         */
+        public static final int DIRTY_RETAIN = -1;
+
+        /**
          * Flag used to indicate that the voicemail was deleted but not synced to the server.
          * A deleted row should be ignored.
          * The value will be 1 if deleted is true, 0 if false.
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
index 5a3f390..a93d1e1 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
+++ b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
@@ -28,7 +28,7 @@
  */
 public class KeyAttestationPackageInfo implements Parcelable {
     private final String mPackageName;
-    private final int mPackageVersionCode;
+    private final long mPackageVersionCode;
     private final Signature[] mPackageSignatures;
 
     /**
@@ -37,7 +37,7 @@
      * @param mPackageSignatures
      */
     public KeyAttestationPackageInfo(
-            String mPackageName, int mPackageVersionCode, Signature[] mPackageSignatures) {
+            String mPackageName, long mPackageVersionCode, Signature[] mPackageSignatures) {
         super();
         this.mPackageName = mPackageName;
         this.mPackageVersionCode = mPackageVersionCode;
@@ -52,7 +52,7 @@
     /**
      * @return the mPackageVersionCode
      */
-    public int getPackageVersionCode() {
+    public long getPackageVersionCode() {
         return mPackageVersionCode;
     }
     /**
@@ -70,7 +70,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mPackageName);
-        dest.writeInt(mPackageVersionCode);
+        dest.writeLong(mPackageVersionCode);
         dest.writeTypedArray(mPackageSignatures, flags);
     }
 
@@ -89,7 +89,7 @@
 
     private KeyAttestationPackageInfo(Parcel source) {
         mPackageName = source.readString();
-        mPackageVersionCode = source.readInt();
+        mPackageVersionCode = source.readLong();
         mPackageSignatures = source.createTypedArray(Signature.CREATOR);
     }
 }
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index cd362c7..1afa8b3 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -440,7 +440,6 @@
  *  save(username, password);
  * </pre>
  *
- *
  * <a name="Privacy"></a>
  * <h3>Privacy</h3>
  *
@@ -453,6 +452,13 @@
  * <p>Because this data could contain PII (Personally Identifiable Information, such as username or
  * email address), the service should only use it locally (i.e., in the app's process) for
  * heuristics purposes, but it should not be sent to external servers.
+ *
+ * <a name="FieldsClassification"></a>
+ * <h3>Metrics and fields classification</h3
+ *
+ * <p>TODO(b/67867469): document it or remove this section; in particular, document the relationship
+ * between set/getUserData(), FillResponse.setFieldClassificationIds(), and
+ * FillEventHistory.getFieldsClassification.
  */
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/FieldsDetection.java b/core/java/android/service/autofill/FieldsDetection.java
deleted file mode 100644
index 550ecf6..0000000
--- a/core/java/android/service/autofill/FieldsDetection.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.service.autofill;
-
-import android.annotation.TestApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.autofill.AutofillId;
-
-/**
- * Class by service to improve autofillable fields detection by tracking the meaning of fields
- * manually edited by the user (when they match values provided by the service).
- *
- * TODO(b/67867469):
- *  - proper javadoc
- *  - unhide / remove testApi
- *  - add FieldsDetection management so service can set it just once and reference it in further
- *    calls to improve performance (and also API to refresh it)
- *  - rename to FieldsDetectionInfo or FieldClassification? (same for CTS tests)
- *  - add FieldsDetectionUnitTest once API is well-defined
- * @hide
- */
-@TestApi
-public final class FieldsDetection implements Parcelable {
-
-    private final AutofillId mFieldId;
-    private final String mRemoteId;
-    private final String mValue;
-
-    /**
-     * Creates a field detection for just one field / value pair.
-     *
-     * @param fieldId autofill id of the field in the screen.
-     * @param remoteId id used by the service to identify the field later.
-     * @param value field value known to the service.
-     *
-     * TODO(b/67867469):
-     *  - proper javadoc
-     *  - change signature to allow more fields / values / match methods
-     *    - might also need to use a builder, where the constructor is the id for the fieldsdetector
-     *    - might need id for values as well
-     *  - add @NonNull / check it / add unit tests
-     *  - make 'value' input more generic so it can accept distance-based match and other matches
-     *  - throw exception if field value is less than X characters (somewhere between 7-10)
-     *  - make sure to limit total number of fields to around 10 or so
-     *  - use AutofillValue instead of String (so it can compare dates, for example)
-     */
-    public FieldsDetection(AutofillId fieldId, String remoteId, String value) {
-        mFieldId = fieldId;
-        mRemoteId = remoteId;
-        mValue = value;
-    }
-
-    /** @hide */
-    public AutofillId getFieldId() {
-        return mFieldId;
-    }
-
-    /** @hide */
-    public String getRemoteId() {
-        return mRemoteId;
-    }
-
-    /** @hide */
-    public String getValue() {
-        return mValue;
-    }
-
-    /////////////////////////////////////
-    // Object "contract" methods. //
-    /////////////////////////////////////
-    @Override
-    public String toString() {
-        // Cannot disclose remoteId or value because they could contain PII
-        return new StringBuilder("FieldsDetection: [field=").append(mFieldId)
-                .append(", remoteId_length=").append(mRemoteId.length())
-                .append(", value_length=").append(mValue.length())
-                .append("]").toString();
-    }
-
-    /////////////////////////////////////
-    // Parcelable "contract" methods. //
-    /////////////////////////////////////
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mFieldId, flags);
-        parcel.writeString(mRemoteId);
-        parcel.writeString(mValue);
-    }
-
-    public static final Parcelable.Creator<FieldsDetection> CREATOR =
-            new Parcelable.Creator<FieldsDetection>() {
-        @Override
-        public FieldsDetection createFromParcel(Parcel parcel) {
-            // TODO(b/67867469): remove comment below if it does not use a builder at the end
-            // Always go through the builder to ensure the data ingested by
-            // the system obeys the contract of the builder to avoid attacks
-            // using specially crafted parcels.
-            return new FieldsDetection(parcel.readParcelable(null), parcel.readString(),
-                    parcel.readString());
-        }
-
-        @Override
-        public FieldsDetection[] newArray(int size) {
-            return new FieldsDetection[size];
-        }
-    };
-}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 736d9ef..eedb972 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -58,11 +58,6 @@
  */
 public final class FillEventHistory implements Parcelable {
     /**
-     * Not in parcel. The UID of the {@link AutofillService} that created the {@link FillResponse}.
-     */
-    private final int mServiceUid;
-
-    /**
      * Not in parcel. The ID of the autofill session that created the {@link FillResponse}.
      */
     private final int mSessionId;
@@ -70,17 +65,6 @@
     @Nullable private final Bundle mClientState;
     @Nullable List<Event> mEvents;
 
-    /**
-     * Gets the UID of the {@link AutofillService} that created the {@link FillResponse}.
-     *
-     * @return The UID of the {@link AutofillService}
-     *
-     * @hide
-     */
-    public int getServiceUid() {
-        return mServiceUid;
-    }
-
     /** @hide */
     public int getSessionId() {
         return mSessionId;
@@ -123,9 +107,8 @@
     /**
      * @hide
      */
-    public FillEventHistory(int serviceUid, int sessionId, @Nullable Bundle clientState) {
+    public FillEventHistory(int sessionId, @Nullable Bundle clientState) {
         mClientState = clientState;
-        mServiceUid = serviceUid;
         mSessionId = sessionId;
     }
 
@@ -364,16 +347,17 @@
         }
 
         /**
-         * Gets the results of the last {@link FieldsDetection} request.
+         * Gets the results of the last fields classification request.
          *
          * @return map of edit-distance match ({@code 0} means full match,
-         * {@code 1} means 1 character different, etc...) by remote id (as set in the
-         * {@link FieldsDetection} constructor), or {@code null} if none of the user-input values
+         * {@code 1} means 1 character different, etc...) by remote id (as set on
+         * {@link UserData.Builder#add(String, android.view.autofill.AutofillValue)}),
+         * or {@code null} if none of the user-input values
          * matched the requested detection.
          *
          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
-         * service requested {@link FillResponse.Builder#setFieldsDetection(FieldsDetection) fields
-         * detection}.
+         * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
+         * fields detection}.
          *
          * TODO(b/67867469):
          *  - improve javadoc
@@ -382,11 +366,12 @@
          *  - unhide
          *  - unhide / remove testApi
          *  - add @NonNull / check it / add unit tests
+         *  - add link to AutofillService #FieldsClassification anchor
          *
          * @hide
          */
         @TestApi
-        @NonNull public Map<String, Integer> getDetectedFields() {
+        @NonNull public Map<String, Integer> getFieldsClassification() {
             if (mDetectedRemoteId == null || mDetectedFieldScore == -1) {
                 return Collections.emptyMap();
             }
@@ -534,7 +519,7 @@
             new Parcelable.Creator<FillEventHistory>() {
                 @Override
                 public FillEventHistory createFromParcel(Parcel parcel) {
-                    FillEventHistory selection = new FillEventHistory(0, 0, parcel.readBundle());
+                    FillEventHistory selection = new FillEventHistory(0, parcel.readBundle());
 
                     final int numEvents = parcel.readInt();
                     for (int i = 0; i < numEvents; i++) {
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 84a0974..06d2b07 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -72,11 +72,13 @@
     private final @Nullable SaveInfo mSaveInfo;
     private final @Nullable Bundle mClientState;
     private final @Nullable RemoteViews mPresentation;
+    private final @Nullable RemoteViews mHeader;
+    private final @Nullable RemoteViews mFooter;
     private final @Nullable IntentSender mAuthentication;
     private final @Nullable AutofillId[] mAuthenticationIds;
     private final @Nullable AutofillId[] mIgnoredIds;
     private final long mDisableDuration;
-    private final @Nullable FieldsDetection mFieldsDetection;
+    private final @Nullable AutofillId[] mFieldClassificationIds;
     private final int mFlags;
     private int mRequestId;
 
@@ -85,11 +87,13 @@
         mSaveInfo = builder.mSaveInfo;
         mClientState = builder.mClientState;
         mPresentation = builder.mPresentation;
+        mHeader = builder.mHeader;
+        mFooter = builder.mFooter;
         mAuthentication = builder.mAuthentication;
         mAuthenticationIds = builder.mAuthenticationIds;
         mIgnoredIds = builder.mIgnoredIds;
         mDisableDuration = builder.mDisableDuration;
-        mFieldsDetection = builder.mFieldsDetection;
+        mFieldClassificationIds = builder.mFieldClassificationIds;
         mFlags = builder.mFlags;
         mRequestId = INVALID_REQUEST_ID;
     }
@@ -115,6 +119,16 @@
     }
 
     /** @hide */
+    public @Nullable RemoteViews getHeader() {
+        return mHeader;
+    }
+
+    /** @hide */
+    public @Nullable RemoteViews getFooter() {
+        return mFooter;
+    }
+
+    /** @hide */
     public @Nullable IntentSender getAuthentication() {
         return mAuthentication;
     }
@@ -135,8 +149,8 @@
     }
 
     /** @hide */
-    public @Nullable FieldsDetection getFieldsDetection() {
-        return mFieldsDetection;
+    public @Nullable AutofillId[] getFieldClassificationIds() {
+        return mFieldClassificationIds;
     }
 
     /** @hide */
@@ -171,11 +185,13 @@
         private SaveInfo mSaveInfo;
         private Bundle mClientState;
         private RemoteViews mPresentation;
+        private RemoteViews mHeader;
+        private RemoteViews mFooter;
         private IntentSender mAuthentication;
         private AutofillId[] mAuthenticationIds;
         private AutofillId[] mIgnoredIds;
         private long mDisableDuration;
-        private FieldsDetection mFieldsDetection;
+        private AutofillId[] mFieldClassificationIds;
         private int mFlags;
         private boolean mDestroyed;
 
@@ -226,16 +242,24 @@
          * @param ids id of Views that when focused will display the authentication UI.
          *
          * @return This builder.
+
          * @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if
          * both {@code authentication} and {@code presentation} are {@code null}, or if
          * both {@code authentication} and {@code presentation} are non-{@code null}
          *
+         * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
+         * {@link #setFooter(RemoteViews) footer} are already set for this builder.
+         *
          * @see android.app.PendingIntent#getIntentSender()
          */
         public @NonNull Builder setAuthentication(@NonNull AutofillId[] ids,
                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
             throwIfDestroyed();
             throwIfDisableAutofillCalled();
+            if (mHeader != null || mFooter != null) {
+                throw new IllegalStateException("Already called #setHeader() or #setFooter()");
+            }
+
             if (ids == null || ids.length == 0) {
                 throw new IllegalArgumentException("ids cannot be null or empry");
             }
@@ -329,21 +353,29 @@
         }
 
         /**
+         * Sets which fields are used for <a href="#FieldsClassification">fields classification</a>
+         *
+         * @throws IllegalArgumentException is length of {@code ids} args is more than
+         * {@link UserData#getMaxFieldClassificationIdsSize()}.
+         * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
+         * already called.
+         * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
+         *
          * TODO(b/67867469):
-         *  - javadoc it
-         *  - javadoc how to check results
-         *  - unhide
+         *  - improve javadoc: explain relationship with UserData and how to check results
          *  - unhide / remove testApi
-         *  - throw exception (and document) if response has datasets or saveinfo
-         *  - throw exception (and document) if id on fieldsDetection is ignored
+         *  - implement multiple ids
          *
          * @hide
          */
         @TestApi
-        public Builder setFieldsDetection(@NonNull FieldsDetection fieldsDetection) {
+        public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
             throwIfDestroyed();
             throwIfDisableAutofillCalled();
-            mFieldsDetection = Preconditions.checkNotNull(fieldsDetection);
+            Preconditions.checkArrayElementsNotNull(ids, "ids");
+            Preconditions.checkArgumentInRange(ids.length, 1,
+                    UserData.getMaxFieldClassificationIdsSize(), "ids length");
+            mFieldClassificationIds = ids;
             return this;
         }
 
@@ -391,16 +423,17 @@
          * @throws IllegalArgumentException if {@code duration} is not a positive number.
          * @throws IllegalStateException if either {@link #addDataset(Dataset)},
          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
-         *       {@link #setSaveInfo(SaveInfo)}, or {@link #setClientState(Bundle)}
-         *       was already called.
+         *       {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
+         *       {link #setFieldClassificationIds(AutofillId...)} was already called.
          */
+        // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
         public Builder disableAutofill(long duration) {
             throwIfDestroyed();
             if (duration <= 0) {
                 throw new IllegalArgumentException("duration must be greater than 0");
             }
             if (mAuthentication != null || mDatasets != null || mSaveInfo != null
-                    || mFieldsDetection != null || mClientState != null) {
+                    || mFieldClassificationIds != null || mClientState != null) {
                 throw new IllegalStateException("disableAutofill() must be the only method called");
             }
 
@@ -409,6 +442,62 @@
         }
 
         /**
+         * Sets a header to be shown as the first element in the list of datasets.
+         *
+         * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
+         * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
+         * method should only be used on {@link FillResponse FillResponses} that do not require
+         * authentication (as the header could have been set directly in the main presentation in
+         * these cases).
+         *
+         * @param header a presentation to represent the header. This presentation is not clickable
+         * &mdash;calling
+         * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
+         * have no effect.
+         *
+         * @return this builder
+         *
+         * @throws IllegalStateException if an
+         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) authentication} was
+         * already set for this builder.
+         */
+        // TODO(b/69796626): make it sticky / update javadoc
+        public Builder setHeader(@NonNull RemoteViews header) {
+            throwIfDestroyed();
+            throwIfAuthenticationCalled();
+            mHeader = Preconditions.checkNotNull(header);
+            return this;
+        }
+
+        /**
+         * Sets a footer to be shown as the last element in the list of datasets.
+         *
+         * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
+         * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
+         * method should only be used on {@link FillResponse FillResponses} that do not require
+         * authentication (as the footer could have been set directly in the main presentation in
+         * these cases).
+         *
+         * @param footer a presentation to represent the footer. This presentation is not clickable
+         * &mdash;calling
+         * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
+         * have no effect.
+         *
+         * @return this builder
+         *
+         * @throws IllegalStateException if the FillResponse
+         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+         * requires authentication}.
+         */
+        // TODO(b/69796626): make it sticky / update javadoc
+        public Builder setFooter(@NonNull RemoteViews footer) {
+            throwIfDestroyed();
+            throwIfAuthenticationCalled();
+            mFooter = Preconditions.checkNotNull(footer);
+            return this;
+        }
+
+        /**
          * Builds a new {@link FillResponse} instance.
          *
          * @throws IllegalStateException if any of the following conditions occur:
@@ -417,19 +506,28 @@
          *   <li>No call was made to {@link #addDataset(Dataset)},
          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
          *       {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
-         *       or {@link #setClientState(Bundle)}.
+         *       {@link #setClientState(Bundle)},
+         *       or {link #setFieldClassificationIds(AutofillId...)}.
+         *   <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
+         *       without any previous calls to {@link #addDataset(Dataset)}.
          * </ol>
          *
          * @return A built response.
          */
+        // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
         public FillResponse build() {
             throwIfDestroyed();
             if (mAuthentication == null && mDatasets == null && mSaveInfo == null
-                    && mDisableDuration == 0 && mFieldsDetection == null && mClientState == null) {
+                    && mDisableDuration == 0 && mFieldClassificationIds == null
+                    && mClientState == null) {
                 throw new IllegalStateException("need to provide: at least one DataSet, or a "
                         + "SaveInfo, or an authentication with a presentation, "
                         + "or a FieldsDetection, or a client state, or disable autofill");
             }
+            if (mDatasets == null && (mHeader != null || mFooter != null)) {
+                throw new IllegalStateException(
+                        "must add at least 1 dataset when using header or footer");
+            }
             mDestroyed = true;
             return new FillResponse(this);
         }
@@ -445,6 +543,12 @@
                 throw new IllegalStateException("Already called #disableAutofill()");
             }
         }
+
+        private void throwIfAuthenticationCalled() {
+            if (mAuthentication != null) {
+                throw new IllegalStateException("Already called #setAuthentication()");
+            }
+        }
     }
 
     /////////////////////////////////////
@@ -461,12 +565,15 @@
                 .append(", saveInfo=").append(mSaveInfo)
                 .append(", clientState=").append(mClientState != null)
                 .append(", hasPresentation=").append(mPresentation != null)
+                .append(", hasHeader=").append(mHeader != null)
+                .append(", hasFooter=").append(mFooter != null)
                 .append(", hasAuthentication=").append(mAuthentication != null)
                 .append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds))
                 .append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
                 .append(", disableDuration=").append(mDisableDuration)
                 .append(", flags=").append(mFlags)
-                .append(", fieldDetection=").append(mFieldsDetection)
+                .append(", fieldClassificationIds=")
+                    .append(Arrays.toString(mFieldClassificationIds))
                 .append("]")
                 .toString();
     }
@@ -488,9 +595,11 @@
         parcel.writeParcelableArray(mAuthenticationIds, flags);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeParcelable(mPresentation, flags);
+        parcel.writeParcelable(mHeader, flags);
+        parcel.writeParcelable(mFooter, flags);
         parcel.writeParcelableArray(mIgnoredIds, flags);
         parcel.writeLong(mDisableDuration);
-        parcel.writeParcelable(mFieldsDetection, flags);
+        parcel.writeParcelableArray(mFieldClassificationIds, flags);
         parcel.writeInt(mFlags);
         parcel.writeInt(mRequestId);
     }
@@ -520,15 +629,24 @@
             if (authenticationIds != null) {
                 builder.setAuthentication(authenticationIds, authentication, presentation);
             }
+            final RemoteViews header = parcel.readParcelable(null);
+            if (header != null) {
+                builder.setHeader(header);
+            }
+            final RemoteViews footer = parcel.readParcelable(null);
+            if (footer != null) {
+                builder.setFooter(footer);
+            }
 
             builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
             final long disableDuration = parcel.readLong();
             if (disableDuration > 0) {
                 builder.disableAutofill(disableDuration);
             }
-            final FieldsDetection fieldsDetection = parcel.readParcelable(null);
-            if (fieldsDetection != null) {
-                builder.setFieldsDetection(fieldsDetection);
+            final AutofillId[] fieldClassifactionIds =
+                    parcel.readParcelableArray(null, AutofillId.class);
+            if (fieldClassifactionIds != null) {
+                builder.setFieldClassificationIds(fieldClassifactionIds);
             }
             builder.setFlags(parcel.readInt());
 
diff --git a/core/java/android/service/autofill/IAuthenticationCallback.aidl b/core/java/android/service/autofill/IAuthenticationCallback.aidl
deleted file mode 100644
index 36b989d..0000000
--- a/core/java/android/service/autofill/IAuthenticationCallback.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-import android.view.autofill.Dataset;
-import android.service.autofill.FillResponse;
-
-/**
- * Callback for delivering authentication result.
- *
- * {@hide}
- */
-interface IAutoFillAuthCallback {
-    void onSuccessForDataset(in Dataset dataset);
-    void onSuccessForFillResponse(in FillResponse response);
-    void onFailure(CharSequence message);
-}
diff --git a/telephony/java/com/android/internal/telephony/ISubscriptionListener.aidl b/core/java/android/service/autofill/UserData.aidl
similarity index 64%
rename from telephony/java/com/android/internal/telephony/ISubscriptionListener.aidl
rename to core/java/android/service/autofill/UserData.aidl
index 4ccdea5..76016de 100644
--- a/telephony/java/com/android/internal/telephony/ISubscriptionListener.aidl
+++ b/core/java/android/service/autofill/UserData.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
+/**
+ * Copyright (c) 2017, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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,
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony;
+package android.service.autofill;
 
-import android.telephony.SubscriptionInfo;
-
-oneway interface ISubscriptionListener {
-    void onSubscriptionInfoChanged();
-}
-
+parcelable UserData;
+parcelable UserData.Constraints;
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
new file mode 100644
index 0000000..16d8d4a
--- /dev/null
+++ b/core/java/android/service/autofill/UserData.java
@@ -0,0 +1,288 @@
+/*
+ * 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.service.autofill;
+
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.autofill.Helper;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Class used by service to improve autofillable fields detection by tracking the meaning of fields
+ * manually edited by the user (when they match values provided by the service).
+ *
+ * TODO(b/67867469):
+ *  - improve javadoc / add link to section on AutofillService
+ *  - unhide / remove testApi
+ * @hide
+ */
+@TestApi
+public final class UserData implements Parcelable {
+
+    private static final String TAG = "UserData";
+
+    private static final int DEFAULT_MAX_USER_DATA_SIZE = 10;
+    private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10;
+    private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
+    private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
+
+    private final String[] mRemoteIds;
+    private final String[] mValues;
+
+    private UserData(Builder builder) {
+        mRemoteIds = new String[builder.mRemoteIds.size()];
+        builder.mRemoteIds.toArray(mRemoteIds);
+        mValues = new String[builder.mValues.size()];
+        builder.mValues.toArray(mValues);
+    }
+
+    /** @hide */
+    public String[] getRemoteIds() {
+        return mRemoteIds;
+    }
+
+    /** @hide */
+    public String[] getValues() {
+        return mValues;
+    }
+
+    /** @hide */
+    public void dump(String prefix, PrintWriter pw) {
+        // Cannot disclose remote ids because they could contain PII
+        pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
+        for (int i = 0; i < mValues.length; i++) {
+            pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": "); pw.println(mValues[i]);
+        }
+    }
+
+    /** @hide */
+    public static void dumpConstraints(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("maxUserDataSize: "); pw.println(getMaxUserDataSize());
+        pw.print(prefix); pw.print("maxFieldClassificationIdsSize: ");
+        pw.println(getMaxFieldClassificationIdsSize());
+        pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength());
+        pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength());
+    }
+
+    /**
+     * A builder for {@link UserData} objects.
+     *
+     * TODO(b/67867469): unhide / remove testApi
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class Builder {
+        private final ArraySet<String> mRemoteIds;
+        private final ArrayList<String> mValues;
+        private boolean mDestroyed;
+
+        /**
+         * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
+         * classification</a>.
+         *
+         * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
+         * length of {@code value} is lower than {@link UserData#getMinValueLength()}
+         * or higher than {@link UserData#getMaxValueLength()}.
+         */
+        public Builder(@NonNull String remoteId, @NonNull String value) {
+            checkValidRemoteId(remoteId);
+            checkValidValue(value);
+            final int capacity = getMaxUserDataSize();
+            mRemoteIds = new ArraySet<>(capacity);
+            mValues = new ArrayList<>(capacity);
+            mRemoteIds.add(remoteId);
+            mValues.add(value);
+        }
+
+        /**
+         * Adds a new value for user data.
+         *
+         * @param remoteId unique string used to identify the user data.
+         * @param value value of the user data.
+         *
+         * @throws IllegalStateException if {@link #build()} or
+         * {@link #add(String, String)} with the same {@code remoteId} has already
+         * been called, or if the number of values add (i.e., calls made to this method plus
+         * constructor) is more than {@link UserData#getMaxUserDataSize()}.
+         *
+         * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
+         * length of {@code value} is lower than {@link UserData#getMinValueLength()}
+         * or higher than {@link UserData#getMaxValueLength()}.
+         */
+        public Builder add(@NonNull String remoteId, @NonNull String value) {
+            throwIfDestroyed();
+            checkValidRemoteId(remoteId);
+            checkValidValue(value);
+
+            Preconditions.checkState(!mRemoteIds.contains(remoteId),
+                    // Don't include remoteId on message because it could contain PII
+                    "already has entry with same remoteId");
+            Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
+                    "already added " + mRemoteIds.size() + " elements");
+            mRemoteIds.add(remoteId);
+            mValues.add(value);
+            return this;
+        }
+
+        private void checkValidRemoteId(@Nullable String remoteId) {
+            Preconditions.checkNotNull(remoteId);
+            Preconditions.checkArgument(!remoteId.isEmpty(), "remoteId cannot be empty");
+        }
+
+        private void checkValidValue(@Nullable String value) {
+            Preconditions.checkNotNull(value);
+            final int length = value.length();
+            Preconditions.checkArgumentInRange(length, getMinValueLength(),
+                    getMaxValueLength(), "value length (" + length + ")");
+        }
+
+        /**
+         * Creates a new {@link UserData} instance.
+         *
+         * <p>You should not interact with this builder once this method is called.
+         *
+         * @throws IllegalStateException if {@link #build()} was already called.
+         *
+         * @return The built dataset.
+         */
+        public UserData build() {
+            throwIfDestroyed();
+            mDestroyed = true;
+            return new UserData(this);
+        }
+
+        private void throwIfDestroyed() {
+            if (mDestroyed) {
+                throw new IllegalStateException("Already called #build()");
+            }
+        }
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        // Cannot disclose keys or values because they could contain PII
+        final StringBuilder builder = new StringBuilder("UserData: [remoteIds=");
+        Helper.appendRedacted(builder, mRemoteIds);
+        builder.append(", values=");
+        Helper.appendRedacted(builder, mValues);
+        return builder.append("]").toString();
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeStringArray(mRemoteIds);
+        parcel.writeStringArray(mValues);
+    }
+
+    public static final Parcelable.Creator<UserData> CREATOR =
+            new Parcelable.Creator<UserData>() {
+        @Override
+        public UserData createFromParcel(Parcel parcel) {
+            // Always go through the builder to ensure the data ingested by
+            // the system obeys the contract of the builder to avoid attacks
+            // using specially crafted parcels.
+            final String[] remoteIds = parcel.readStringArray();
+            final String[] values = parcel.readStringArray();
+            final Builder builder = new Builder(remoteIds[0], values[0]);
+            for (int i = 1; i < remoteIds.length; i++) {
+                builder.add(remoteIds[i], values[i]);
+            }
+            return builder.build();
+        }
+
+        @Override
+        public UserData[] newArray(int size) {
+            return new UserData[size];
+        }
+    };
+
+    /**
+     * Gets the maximum number of values that can be added to a {@link UserData}.
+     */
+    public static int getMaxUserDataSize() {
+        return getInt(AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE, DEFAULT_MAX_USER_DATA_SIZE);
+    }
+
+    /**
+     * Gets the maximum number of ids that can be passed to {@link
+     * FillResponse.Builder#setFieldClassificationIds(android.view.autofill.AutofillId...)}.
+     */
+    public static int getMaxFieldClassificationIdsSize() {
+        return getInt(AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
+            DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE);
+    }
+
+    /**
+     * Gets the minimum length of values passed to {@link Builder#Builder(String, String)}.
+     */
+    public static int getMinValueLength() {
+        return getInt(AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, DEFAULT_MIN_VALUE_LENGTH);
+    }
+
+    /**
+     * Gets the maximum length of values passed to {@link Builder#Builder(String, String)}.
+     */
+    public static int getMaxValueLength() {
+        return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
+    }
+
+    private static int getInt(String settings, int defaultValue) {
+        ContentResolver cr = null;
+        final ActivityThread at = ActivityThread.currentActivityThread();
+        if (at != null) {
+            cr = at.getApplication().getContentResolver();
+        }
+
+        if (cr == null) {
+            Log.w(TAG, "Could not read from " + settings + "; hardcoding " + defaultValue);
+            return defaultValue;
+        }
+        return Settings.Secure.getInt(cr, settings, defaultValue);
+    }
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 735b822..1ec2406 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -890,7 +890,17 @@
     }
 
     public static boolean isValidScheduleConditionId(Uri conditionId) {
-        return tryParseScheduleConditionId(conditionId) != null;
+        ScheduleInfo info;
+        try {
+            info = tryParseScheduleConditionId(conditionId);
+        } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
+            return false;
+        }
+
+        if (info == null || info.days == null || info.days.length == 0) {
+            return false;
+        }
+        return true;
     }
 
     public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
diff --git a/core/java/android/text/AutoGrowArray.java b/core/java/android/text/AutoGrowArray.java
deleted file mode 100644
index e428377..0000000
--- a/core/java/android/text/AutoGrowArray.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-
-import com.android.internal.util.ArrayUtils;
-
-import libcore.util.EmptyArray;
-
-/**
- * Implements a growing array of int primitives.
- *
- * These arrays are NOT thread safe.
- *
- * @hide
- */
-public final class AutoGrowArray {
-    private static final int MIN_CAPACITY_INCREMENT = 12;
-    private static final int MAX_CAPACITY_TO_BE_KEPT = 10000;
-
-    /**
-     * Returns next capacity size.
-     *
-     * The returned capacity is larger than requested capacity.
-     */
-    private static int computeNewCapacity(int currentSize, int requested) {
-        final int targetCapacity = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2)
-                ?  MIN_CAPACITY_INCREMENT : currentSize >> 1);
-        return targetCapacity > requested ? targetCapacity : requested;
-    }
-
-    /**
-     * An auto growing byte array.
-     */
-    public static class ByteArray {
-
-        private @NonNull byte[] mValues;
-        private @IntRange(from = 0) int mSize;
-
-        /**
-         * Creates an empty ByteArray with the default initial capacity.
-         */
-        public ByteArray() {
-            this(10);
-        }
-
-        /**
-         * Creates an empty ByteArray with the specified initial capacity.
-         */
-        public ByteArray(@IntRange(from = 0) int initialCapacity) {
-            if (initialCapacity == 0) {
-                mValues = EmptyArray.BYTE;
-            } else {
-                mValues = ArrayUtils.newUnpaddedByteArray(initialCapacity);
-            }
-            mSize = 0;
-        }
-
-        /**
-         * Changes the size of this ByteArray. If this ByteArray is shrinked, the backing array
-         * capacity is unchanged.
-         */
-        public void resize(@IntRange(from = 0) int newSize) {
-            if (newSize > mValues.length) {
-                ensureCapacity(newSize - mSize);
-            }
-            mSize = newSize;
-        }
-
-        /**
-         * Appends the specified value to the end of this array.
-         */
-        public void append(byte value) {
-            ensureCapacity(1);
-            mValues[mSize++] = value;
-        }
-
-        /**
-         * Ensures capacity to append at least <code>count</code> values.
-         */
-        private void ensureCapacity(@IntRange int count) {
-            final int requestedSize = mSize + count;
-            if (requestedSize >= mValues.length) {
-                final int newCapacity = computeNewCapacity(mSize, requestedSize);
-                final byte[] newValues = ArrayUtils.newUnpaddedByteArray(newCapacity);
-                System.arraycopy(mValues, 0, newValues, 0, mSize);
-                mValues = newValues;
-            }
-        }
-
-        /**
-         * Removes all values from this array.
-         */
-        public void clear() {
-            mSize = 0;
-        }
-
-        /**
-         * Removes all values from this array and release the internal array object if it is too
-         * large.
-         */
-        public void clearWithReleasingLargeArray() {
-            clear();
-            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
-                mValues = EmptyArray.BYTE;
-            }
-        }
-
-        /**
-         * Returns the value at the specified position in this array.
-         */
-        public byte get(@IntRange(from = 0) int index) {
-            return mValues[index];
-        }
-
-        /**
-         * Sets the value at the specified position in this array.
-         */
-        public void set(@IntRange(from = 0) int index, byte value) {
-            mValues[index] = value;
-        }
-
-        /**
-         * Returns the number of values in this array.
-         */
-        public @IntRange(from = 0) int size() {
-            return mSize;
-        }
-
-        /**
-         * Returns internal raw array.
-         *
-         * Note that this array may have larger size than you requested.
-         * Use size() instead for getting the actual array size.
-         */
-        public @NonNull byte[] getRawArray() {
-            return mValues;
-        }
-    }
-
-    /**
-     * An auto growing int array.
-     */
-    public static class IntArray {
-
-        private @NonNull int[] mValues;
-        private @IntRange(from = 0) int mSize;
-
-        /**
-         * Creates an empty IntArray with the default initial capacity.
-         */
-        public IntArray() {
-            this(10);
-        }
-
-        /**
-         * Creates an empty IntArray with the specified initial capacity.
-         */
-        public IntArray(@IntRange(from = 0) int initialCapacity) {
-            if (initialCapacity == 0) {
-                mValues = EmptyArray.INT;
-            } else {
-                mValues = ArrayUtils.newUnpaddedIntArray(initialCapacity);
-            }
-            mSize = 0;
-        }
-
-        /**
-         * Changes the size of this IntArray. If this IntArray is shrinked, the backing array
-         * capacity is unchanged.
-         */
-        public void resize(@IntRange(from = 0) int newSize) {
-            if (newSize > mValues.length) {
-                ensureCapacity(newSize - mSize);
-            }
-            mSize = newSize;
-        }
-
-        /**
-         * Appends the specified value to the end of this array.
-         */
-        public void append(int value) {
-            ensureCapacity(1);
-            mValues[mSize++] = value;
-        }
-
-        /**
-         * Ensures capacity to append at least <code>count</code> values.
-         */
-        private void ensureCapacity(@IntRange(from = 0) int count) {
-            final int requestedSize = mSize + count;
-            if (requestedSize >= mValues.length) {
-                final int newCapacity = computeNewCapacity(mSize, requestedSize);
-                final int[] newValues = ArrayUtils.newUnpaddedIntArray(newCapacity);
-                System.arraycopy(mValues, 0, newValues, 0, mSize);
-                mValues = newValues;
-            }
-        }
-
-        /**
-         * Removes all values from this array.
-         */
-        public void clear() {
-            mSize = 0;
-        }
-
-        /**
-         * Removes all values from this array and release the internal array object if it is too
-         * large.
-         */
-        public void clearWithReleasingLargeArray() {
-            clear();
-            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
-                mValues = EmptyArray.INT;
-            }
-        }
-
-        /**
-         * Returns the value at the specified position in this array.
-         */
-        public int get(@IntRange(from = 0) int index) {
-            return mValues[index];
-        }
-
-        /**
-         * Sets the value at the specified position in this array.
-         */
-        public void set(@IntRange(from = 0) int index, int value) {
-            mValues[index] = value;
-        }
-
-        /**
-         * Returns the number of values in this array.
-         */
-        public @IntRange(from = 0) int size() {
-            return mSize;
-        }
-
-        /**
-         * Returns internal raw array.
-         *
-         * Note that this array may have larger size than you requested.
-         * Use size() instead for getting the actual array size.
-         */
-        public @NonNull int[] getRawArray() {
-            return mValues;
-        }
-    }
-
-    /**
-     * An auto growing float array.
-     */
-    public static class FloatArray {
-
-        private @NonNull float[] mValues;
-        private @IntRange(from = 0) int mSize;
-
-        /**
-         * Creates an empty FloatArray with the default initial capacity.
-         */
-        public FloatArray() {
-            this(10);
-        }
-
-        /**
-         * Creates an empty FloatArray with the specified initial capacity.
-         */
-        public FloatArray(@IntRange(from = 0) int initialCapacity) {
-            if (initialCapacity == 0) {
-                mValues = EmptyArray.FLOAT;
-            } else {
-                mValues = ArrayUtils.newUnpaddedFloatArray(initialCapacity);
-            }
-            mSize = 0;
-        }
-
-        /**
-         * Changes the size of this FloatArray. If this FloatArray is shrinked, the backing array
-         * capacity is unchanged.
-         */
-        public void resize(@IntRange(from = 0) int newSize) {
-            if (newSize > mValues.length) {
-                ensureCapacity(newSize - mSize);
-            }
-            mSize = newSize;
-        }
-
-        /**
-         * Appends the specified value to the end of this array.
-         */
-        public void append(float value) {
-            ensureCapacity(1);
-            mValues[mSize++] = value;
-        }
-
-        /**
-         * Ensures capacity to append at least <code>count</code> values.
-         */
-        private void ensureCapacity(int count) {
-            final int requestedSize = mSize + count;
-            if (requestedSize >= mValues.length) {
-                final int newCapacity = computeNewCapacity(mSize, requestedSize);
-                final float[] newValues = ArrayUtils.newUnpaddedFloatArray(newCapacity);
-                System.arraycopy(mValues, 0, newValues, 0, mSize);
-                mValues = newValues;
-            }
-        }
-
-        /**
-         * Removes all values from this array.
-         */
-        public void clear() {
-            mSize = 0;
-        }
-
-        /**
-         * Removes all values from this array and release the internal array object if it is too
-         * large.
-         */
-        public void clearWithReleasingLargeArray() {
-            clear();
-            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
-                mValues = EmptyArray.FLOAT;
-            }
-        }
-
-        /**
-         * Returns the value at the specified position in this array.
-         */
-        public float get(@IntRange(from = 0) int index) {
-            return mValues[index];
-        }
-
-        /**
-         * Sets the value at the specified position in this array.
-         */
-        public void set(@IntRange(from = 0) int index, float value) {
-            mValues[index] = value;
-        }
-
-        /**
-         * Returns the number of values in this array.
-         */
-        public @IntRange(from = 0) int size() {
-            return mSize;
-        }
-
-        /**
-         * Returns internal raw array.
-         *
-         * Note that this array may have larger size than you requested.
-         * Use size() instead for getting the actual array size.
-         */
-        public @NonNull float[] getRawArray() {
-            return mValues;
-        }
-    }
-}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2a693a1..4d2a962 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1907,14 +1907,22 @@
 
     private static float measurePara(TextPaint paint, CharSequence text, int start, int end,
             TextDirectionHeuristic textDir) {
-        MeasuredText mt = null;
+        MeasuredText mt = MeasuredText.obtain();
         TextLine tl = TextLine.obtain();
         try {
-            mt = MeasuredText.buildForBidi(text, start, end, textDir, mt);
-            final char[] chars = mt.getChars();
-            final int len = chars.length;
-            final Directions directions = mt.getDirections(0, len);
-            final int dir = mt.getParagraphDir();
+            mt.setPara(text, start, end, textDir);
+            Directions directions;
+            int dir;
+            if (mt.mEasy) {
+                directions = DIRS_ALL_LEFT_TO_RIGHT;
+                dir = Layout.DIR_LEFT_TO_RIGHT;
+            } else {
+                directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
+                    0, mt.mChars, 0, mt.mLen);
+                dir = mt.mDir;
+            }
+            char[] chars = mt.mChars;
+            int len = mt.mLen;
             boolean hasTabs = false;
             TabStops tabStops = null;
             // leading margins should be taken into account when measuring a paragraph
@@ -1947,9 +1955,7 @@
             return margin + Math.abs(tl.metrics(null));
         } finally {
             TextLine.recycle(tl);
-            if (mt != null) {
-                mt.recycle();
-            }
+            MeasuredText.recycle(mt);
         }
     }
 
@@ -2266,11 +2272,6 @@
     private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
     private int mJustificationMode;
 
-    /** @hide */
-    @IntDef({DIR_LEFT_TO_RIGHT, DIR_RIGHT_TO_LEFT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Direction {}
-
     public static final int DIR_LEFT_TO_RIGHT = 1;
     public static final int DIR_RIGHT_TO_LEFT = -1;
 
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index ca31176..3d9fba7 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -16,384 +16,125 @@
 
 package android.text;
 
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.graphics.Paint;
-import android.text.AutoGrowArray.ByteArray;
-import android.text.AutoGrowArray.FloatArray;
-import android.text.AutoGrowArray.IntArray;
-import android.text.Layout.Directions;
 import android.text.style.MetricAffectingSpan;
 import android.text.style.ReplacementSpan;
-import android.util.Pools.SynchronizedPool;
+import android.util.Log;
 
-import java.util.Arrays;
+import com.android.internal.util.ArrayUtils;
 
 /**
- * MeasuredText provides text information for rendering purpose.
- *
- * The first motivation of this class is identify the text directions and retrieving individual
- * character widths. However retrieving character widths is slower than identifying text directions.
- * Thus, this class provides several builder methods for specific purposes.
- *
- * - buildForBidi:
- *   Compute only text directions.
- * - buildForMeasurement:
- *   Compute text direction and all character widths.
- * - buildForStaticLayout:
- *   This is bit special. StaticLayout also needs to know text direction and character widths for
- *   line breaking, but all things are done in native code. Similarly, text measurement is done
- *   in native code. So instead of storing result to Java array, this keeps the result in native
- *   code since there is no good reason to move the results to Java layer.
- *
- * In addition to the character widths, some additional information is computed for each purposes,
- * e.g. whole text length for measurement or font metrics for static layout.
- *
- * MeasuredText is NOT a thread safe object.
  * @hide
  */
 class MeasuredText {
-    private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
+    private static final boolean localLOGV = false;
+    CharSequence mText;
+    int mTextStart;
+    float[] mWidths;
+    char[] mChars;
+    byte[] mLevels;
+    int mDir;
+    boolean mEasy;
+    int mLen;
 
-    private MeasuredText() {}  // Use build static functions instead.
+    private int mPos;
+    private TextPaint mWorkPaint;
 
-    private static final SynchronizedPool<MeasuredText> sPool = new SynchronizedPool<>(1);
-
-    private static @NonNull MeasuredText obtain() { // Use build static functions instead.
-        final MeasuredText mt = sPool.acquire();
-        return mt != null ? mt : new MeasuredText();
+    private MeasuredText() {
+        mWorkPaint = new TextPaint();
     }
 
-    /**
-     * Recycle the MeasuredText.
-     *
-     * Do not call any methods after you call this method.
-     */
-    public void recycle() {
-        release();
-        sPool.release(this);
-    }
+    private static final Object[] sLock = new Object[0];
+    private static final MeasuredText[] sCached = new MeasuredText[3];
 
-    // The casted original text.
-    //
-    // This may be null if the passed text is not a Spanned.
-    private @Nullable Spanned mSpanned;
-
-    // The start offset of the target range in the original text (mSpanned);
-    private @IntRange(from = 0) int mTextStart;
-
-    // The length of the target range in the original text.
-    private @IntRange(from = 0) int mTextLength;
-
-    // The copied character buffer for measuring text.
-    //
-    // The length of this array is mTextLength.
-    private @Nullable char[] mCopiedBuffer;
-
-    // The whole paragraph direction.
-    private @Layout.Direction int mParaDir;
-
-    // True if the text is LTR direction and doesn't contain any bidi characters.
-    private boolean mLtrWithoutBidi;
-
-    // The bidi level for individual characters.
-    //
-    // This is empty if mLtrWithoutBidi is true.
-    private @NonNull ByteArray mLevels = new ByteArray();
-
-    // The whole width of the text.
-    // See getWholeWidth comments.
-    private @FloatRange(from = 0.0f) float mWholeWidth;
-
-    // Individual characters' widths.
-    // See getWidths comments.
-    private @Nullable FloatArray mWidths = new FloatArray();
-
-    // The span end positions.
-    // See getSpanEndCache comments.
-    private @Nullable IntArray mSpanEndCache = new IntArray(4);
-
-    // The font metrics.
-    // See getFontMetrics comments.
-    private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
-
-    // Following two objects are for avoiding object allocation.
-    private @NonNull TextPaint mCachedPaint = new TextPaint();
-    private @Nullable Paint.FontMetricsInt mCachedFm;
-
-    /**
-     * Releases internal buffers.
-     */
-    public void release() {
-        reset();
-        mLevels.clearWithReleasingLargeArray();
-        mWidths.clearWithReleasingLargeArray();
-        mFontMetrics.clearWithReleasingLargeArray();
-        mSpanEndCache.clearWithReleasingLargeArray();
-    }
-
-    /**
-     * Resets the internal state for starting new text.
-     */
-    private void reset() {
-        mSpanned = null;
-        mCopiedBuffer = null;
-        mWholeWidth = 0;
-        mLevels.clear();
-        mWidths.clear();
-        mFontMetrics.clear();
-        mSpanEndCache.clear();
-    }
-
-    /**
-     * Returns the characters to be measured.
-     *
-     * This is always available.
-     */
-    public @NonNull char[] getChars() {
-        return mCopiedBuffer;
-    }
-
-    /**
-     * Returns the paragraph direction.
-     *
-     * This is always available.
-     */
-    public @Layout.Direction int getParagraphDir() {
-        return mParaDir;
-    }
-
-    /**
-     * Returns the directions.
-     *
-     * This is always available.
-     */
-    public Directions getDirections(@IntRange(from = 0) int start,  // inclusive
-                                    @IntRange(from = 0) int end) {  // exclusive
-        if (mLtrWithoutBidi) {
-            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
-        }
-
-        final int length = end - start;
-        return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
-                length);
-    }
-
-    /**
-     * Returns the whole text width.
-     *
-     * This is available only if the MeasureText is computed with computeForMeasurement.
-     * Returns 0 in other cases.
-     */
-    public @FloatRange(from = 0.0f) float getWholeWidth() {
-        return mWholeWidth;
-    }
-
-    /**
-     * Returns the individual character's width.
-     *
-     * This is available only if the MeasureText is computed with computeForMeasurement.
-     * Returns empty array in other cases.
-     */
-    public @NonNull FloatArray getWidths() {
-        return mWidths;
-    }
-
-    /**
-     * Returns the MetricsAffectingSpan end indices.
-     *
-     * If the input text is not a spanned string, this has one value that is the length of the text.
-     *
-     * This is available only if the MeasureText is computed with computeForStaticLayout.
-     * Returns empty array in other cases.
-     */
-    public @NonNull IntArray getSpanEndCache() {
-        return mSpanEndCache;
-    }
-
-    /**
-     * Returns the int array which holds FontMetrics.
-     *
-     * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
-     *
-     * This is available only if the MeasureText is computed with computeForStaticLayout.
-     * Returns empty array in other cases.
-     */
-    public @NonNull IntArray getFontMetrics() {
-        return mFontMetrics;
-    }
-
-    /**
-     * Generates new MeasuredText for Bidi computation.
-     *
-     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
-     * result to recycle and returns recycle.
-     *
-     * @param text the character sequence to be measured
-     * @param start the inclusive start offset of the target region in the text
-     * @param end the exclusive end offset of the target region in the text
-     * @param textDir the text direction
-     * @param recycle pass existing MeasuredText if you want to recycle it.
-     *
-     * @return measured text
-     */
-    public static @NonNull MeasuredText buildForBidi(@NonNull CharSequence text,
-                                                     @IntRange(from = 0) int start,
-                                                     @IntRange(from = 0) int end,
-                                                     @NonNull TextDirectionHeuristic textDir,
-                                                     @Nullable MeasuredText recycle) {
-        final MeasuredText mt = recycle == null ? obtain() : recycle;
-        mt.resetAndAnalyzeBidi(text, start, end, textDir);
-        return mt;
-    }
-
-    /**
-     * Generates new MeasuredText for measuring texts.
-     *
-     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
-     * result to recycle and returns recycle.
-     *
-     * @param paint the paint to be used for rendering the text.
-     * @param text the character sequence to be measured
-     * @param start the inclusive start offset of the target region in the text
-     * @param end the exclusive end offset of the target region in the text
-     * @param textDir the text direction
-     * @param recycle pass existing MeasuredText if you want to recycle it.
-     *
-     * @return measured text
-     */
-    public static @NonNull MeasuredText buildForMeasurement(@NonNull TextPaint paint,
-                                                            @NonNull CharSequence text,
-                                                            @IntRange(from = 0) int start,
-                                                            @IntRange(from = 0) int end,
-                                                            @NonNull TextDirectionHeuristic textDir,
-                                                            @Nullable MeasuredText recycle) {
-        final MeasuredText mt = recycle == null ? obtain() : recycle;
-        mt.resetAndAnalyzeBidi(text, start, end, textDir);
-
-        mt.mWidths.resize(mt.mTextLength);
-        if (mt.mTextLength == 0) {
-            return mt;
-        }
-
-        if (mt.mSpanned == null) {
-            // No style change by MetricsAffectingSpan. Just measure all text.
-            mt.applyMetricsAffectingSpan(
-                    paint, null /* spans */, start, end, 0 /* native static layout ptr */);
-        } else {
-            // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
-            int spanEnd;
-            for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
-                spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
-                MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
-                        MetricAffectingSpan.class);
-                spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
-                mt.applyMetricsAffectingSpan(
-                        paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+    static MeasuredText obtain() {
+        MeasuredText mt;
+        synchronized (sLock) {
+            for (int i = sCached.length; --i >= 0;) {
+                if (sCached[i] != null) {
+                    mt = sCached[i];
+                    sCached[i] = null;
+                    return mt;
+                }
             }
         }
+        mt = new MeasuredText();
+        if (localLOGV) {
+            Log.v("MEAS", "new: " + mt);
+        }
         return mt;
     }
 
-    /**
-     * Generates new MeasuredText for StaticLayout.
-     *
-     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
-     * result to recycle and returns recycle.
-     *
-     * @param paint the paint to be used for rendering the text.
-     * @param text the character sequence to be measured
-     * @param start the inclusive start offset of the target region in the text
-     * @param end the exclusive end offset of the target region in the text
-     * @param textDir the text direction
-     * @param nativeStaticLayoutPtr the pointer to the native static layout object
-     * @param recycle pass existing MeasuredText if you want to recycle it.
-     *
-     * @return measured text
-     */
-    public static @NonNull MeasuredText buildForStaticLayout(
-            @NonNull TextPaint paint,
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int start,
-            @IntRange(from = 0) int end,
-            @NonNull TextDirectionHeuristic textDir,
-            /* Non-Zero */ long nativeStaticLayoutPtr,
-            @Nullable MeasuredText recycle) {
-        final MeasuredText mt = recycle == null ? obtain() : recycle;
-        mt.resetAndAnalyzeBidi(text, start, end, textDir);
-        if (mt.mTextLength == 0) {
-            return mt;
-        }
-
-        if (mt.mSpanned == null) {
-            // No style change by MetricsAffectingSpan. Just measure all text.
-            mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end,
-                    nativeStaticLayoutPtr);
-            mt.mSpanEndCache.append(end);
-        } else {
-            // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
-            int spanEnd;
-            for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
-                spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
-                MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
-                        MetricAffectingSpan.class);
-                spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
-                mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
-                        nativeStaticLayoutPtr);
-                mt.mSpanEndCache.append(spanEnd);
+    static MeasuredText recycle(MeasuredText mt) {
+        mt.finish();
+        synchronized(sLock) {
+            for (int i = 0; i < sCached.length; ++i) {
+                if (sCached[i] == null) {
+                    sCached[i] = mt;
+                    mt.mText = null;
+                    break;
+                }
             }
         }
+        return null;
+    }
 
-        return mt;
+    void finish() {
+        mText = null;
+        if (mLen > 1000) {
+            mWidths = null;
+            mChars = null;
+            mLevels = null;
+        }
     }
 
     /**
-     * Reset internal state and analyzes text for bidirectional runs.
-     *
-     * @param text the character sequence to be measured
-     * @param start the inclusive start offset of the target region in the text
-     * @param end the exclusive end offset of the target region in the text
-     * @param textDir the text direction
+     * Analyzes text for bidirectional runs.  Allocates working buffers.
      */
-    private void resetAndAnalyzeBidi(@NonNull CharSequence text,
-                                     @IntRange(from = 0) int start,  // inclusive
-                                     @IntRange(from = 0) int end,  // exclusive
-                                     @NonNull TextDirectionHeuristic textDir) {
-        reset();
-        mSpanned = text instanceof Spanned ? (Spanned) text : null;
+    void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
+        mText = text;
         mTextStart = start;
-        mTextLength = end - start;
 
-        if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
-            mCopiedBuffer = new char[mTextLength];
+        int len = end - start;
+        mLen = len;
+        mPos = 0;
+
+        if (mWidths == null || mWidths.length < len) {
+            mWidths = ArrayUtils.newUnpaddedFloatArray(len);
         }
-        TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
+        if (mChars == null || mChars.length != len) {
+            mChars = new char[len];
+        }
+        TextUtils.getChars(text, start, end, mChars, 0);
 
-        // Replace characters associated with ReplacementSpan to U+FFFC.
-        if (mSpanned != null) {
-            ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
+        if (text instanceof Spanned) {
+            Spanned spanned = (Spanned) text;
+            ReplacementSpan[] spans = spanned.getSpans(start, end,
+                    ReplacementSpan.class);
 
             for (int i = 0; i < spans.length; i++) {
-                int startInPara = mSpanned.getSpanStart(spans[i]) - start;
-                int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
-                // The span interval may be larger and must be restricted to [start, end)
+                int startInPara = spanned.getSpanStart(spans[i]) - start;
+                int endInPara = spanned.getSpanEnd(spans[i]) - start;
+                // The span interval may be larger and must be restricted to [start, end[
                 if (startInPara < 0) startInPara = 0;
-                if (endInPara > mTextLength) endInPara = mTextLength;
-                Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
+                if (endInPara > len) endInPara = len;
+                for (int j = startInPara; j < endInPara; j++) {
+                    mChars[j] = '\uFFFC'; // object replacement character
+                }
             }
         }
 
         if ((textDir == TextDirectionHeuristics.LTR ||
                 textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR ||
                 textDir == TextDirectionHeuristics.ANYRTL_LTR) &&
-                TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
-            mLevels.clear();
-            mParaDir = Layout.DIR_LEFT_TO_RIGHT;
-            mLtrWithoutBidi = true;
+                TextUtils.doesNotNeedBidi(mChars, 0, len)) {
+            mDir = Layout.DIR_LEFT_TO_RIGHT;
+            mEasy = true;
         } else {
-            final int bidiRequest;
+            if (mLevels == null || mLevels.length < len) {
+                mLevels = ArrayUtils.newUnpaddedByteArray(len);
+            }
+            int bidiRequest;
             if (textDir == TextDirectionHeuristics.LTR) {
                 bidiRequest = Layout.DIR_REQUEST_LTR;
             } else if (textDir == TextDirectionHeuristics.RTL) {
@@ -403,146 +144,122 @@
             } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
                 bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
             } else {
-                final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
+                boolean isRtl = textDir.isRtl(mChars, 0, len);
                 bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
             }
-            mLevels.resize(mTextLength);
-            mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
-            mLtrWithoutBidi = false;
-        }
-    }
-
-    private void applyReplacementRun(@NonNull ReplacementSpan replacement,
-                                     @IntRange(from = 0) int start,  // inclusive, in copied buffer
-                                     @IntRange(from = 0) int end,  // exclusive, in copied buffer
-                                     /* Maybe Zero */ long nativeStaticLayoutPtr) {
-        // Use original text. Shouldn't matter.
-        // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
-        //       backward compatibility? or Should we initialize them for getFontMetricsInt?
-        final float width = replacement.getSize(
-                mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
-        if (nativeStaticLayoutPtr == 0) {
-            // Assigns all width to the first character. This is the same behavior as minikin.
-            mWidths.set(start, width);
-            if (end > start + 1) {
-                Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
-            }
-            mWholeWidth += width;
-        } else {
-            StaticLayout.addReplacementRun(nativeStaticLayoutPtr, mCachedPaint, start, end, width);
-        }
-    }
-
-    private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
-                               @IntRange(from = 0) int end,  // exclusive, in copied buffer
-                               /* Maybe Zero */ long nativeStaticLayoutPtr) {
-        if (nativeStaticLayoutPtr != 0) {
-            mCachedPaint.getFontMetricsInt(mCachedFm);
-        }
-
-        if (mLtrWithoutBidi) {
-            // If the whole text is LTR direction, just apply whole region.
-            if (nativeStaticLayoutPtr == 0) {
-                mWholeWidth += mCachedPaint.getTextRunAdvances(
-                        mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
-                        mWidths.getRawArray(), start);
-            } else {
-                StaticLayout.addStyleRun(nativeStaticLayoutPtr, mCachedPaint, start, end,
-                        false /* isRtl */);
-            }
-        } else {
-            // If there is multiple bidi levels, split into individual bidi level and apply style.
-            byte level = mLevels.get(start);
-            // Note that the empty text or empty range won't reach this method.
-            // Safe to search from start + 1.
-            for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
-                if (levelEnd == end || mLevels.get(levelEnd) != level) {  // transition point
-                    final boolean isRtl = (level & 0x1) != 0;
-                    if (nativeStaticLayoutPtr == 0) {
-                        final int levelLength = levelEnd - levelStart;
-                        mWholeWidth += mCachedPaint.getTextRunAdvances(
-                                mCopiedBuffer, levelStart, levelLength, levelStart, levelEnd, isRtl,
-                                mWidths.getRawArray(), levelStart);
-                    } else {
-                        StaticLayout.addStyleRun(
-                                nativeStaticLayoutPtr, mCachedPaint, levelStart, levelEnd, isRtl);
-                    }
-                    if (levelEnd == end) {
-                        break;
-                    }
-                    levelStart = levelEnd;
-                    level = mLevels.get(levelEnd);
-                }
-            }
-        }
-    }
-
-    private void applyMetricsAffectingSpan(
-            @NonNull TextPaint paint,
-            @Nullable MetricAffectingSpan[] spans,
-            @IntRange(from = 0) int start,  // inclusive, in original text buffer
-            @IntRange(from = 0) int end,  // exclusive, in original text buffer
-            /* Maybe Zero */ long nativeStaticLayoutPtr) {
-        mCachedPaint.set(paint);
-        // XXX paint should not have a baseline shift, but...
-        mCachedPaint.baselineShift = 0;
-
-        final boolean needFontMetrics = nativeStaticLayoutPtr != 0;
-
-        if (needFontMetrics && mCachedFm == null) {
-            mCachedFm = new Paint.FontMetricsInt();
-        }
-
-        ReplacementSpan replacement = null;
-        if (spans != null) {
-            for (int i = 0; i < spans.length; i++) {
-                MetricAffectingSpan span = spans[i];
-                if (span instanceof ReplacementSpan) {
-                    // The last ReplacementSpan is effective for backward compatibility reasons.
-                    replacement = (ReplacementSpan) span;
-                } else {
-                    // TODO: No need to call updateMeasureState for ReplacementSpan as well?
-                    span.updateMeasureState(mCachedPaint);
-                }
-            }
-        }
-
-        final int startInCopiedBuffer = start - mTextStart;
-        final int endInCopiedBuffer = end - mTextStart;
-
-        if (replacement != null) {
-            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
-                    nativeStaticLayoutPtr);
-        } else {
-            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeStaticLayoutPtr);
-        }
-
-        if (needFontMetrics) {
-            if (mCachedPaint.baselineShift < 0) {
-                mCachedFm.ascent += mCachedPaint.baselineShift;
-                mCachedFm.top += mCachedPaint.baselineShift;
-            } else {
-                mCachedFm.descent += mCachedPaint.baselineShift;
-                mCachedFm.bottom += mCachedPaint.baselineShift;
-            }
-
-            mFontMetrics.append(mCachedFm.top);
-            mFontMetrics.append(mCachedFm.bottom);
-            mFontMetrics.append(mCachedFm.ascent);
-            mFontMetrics.append(mCachedFm.descent);
+            mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels);
+            mEasy = false;
         }
     }
 
     /**
-     * Returns the maximum index that the accumulated width not exceeds the width.
+     * Apply the style.
      *
-     * If forward=false is passed, returns the minimum index from the end instead.
-     *
-     * This only works if the MeasuredText is computed with computeForMeasurement.
-     * Undefined behavior in other case.
+     * If nativeStaticLayoutPtr is 0, this method measures the styled text width.
+     * If nativeStaticLayoutPtr is not 0, this method just passes the style information to native
+     * code by calling StaticLayout.addstyleRun() and returns 0.
      */
-    @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
-        float[] w = mWidths.getRawArray();
+    float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm,
+            long nativeStaticLayoutPtr) {
+        if (fm != null) {
+            paint.getFontMetricsInt(fm);
+        }
+
+        final int p = mPos;
+        mPos = p + len;
+
+        if (mEasy) {
+            final boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT;
+            if (nativeStaticLayoutPtr == 0) {
+                return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p);
+            } else {
+                StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, p, p + len, isRtl);
+                return 0.0f;  // Builder.addStyleRun doesn't return the width.
+            }
+        }
+
+        float totalAdvance = 0;
+        int level = mLevels[p];
+        for (int q = p, i = p + 1, e = p + len;; ++i) {
+            if (i == e || mLevels[i] != level) {
+                final boolean isRtl = (level & 0x1) != 0;
+                if (nativeStaticLayoutPtr == 0) {
+                    totalAdvance +=
+                            paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q);
+                } else {
+                    // Builder.addStyleRun doesn't return the width.
+                    StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, q, i, isRtl);
+                }
+                if (i == e) {
+                    break;
+                }
+                q = i;
+                level = mLevels[i];
+            }
+        }
+        return totalAdvance;  // If nativeStaticLayoutPtr is 0, the result is zero.
+    }
+
+    float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
+        return addStyleRun(paint, len, fm, 0 /* native ptr */);
+    }
+
+    float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
+            Paint.FontMetricsInt fm, long nativeStaticLayoutPtr) {
+
+        TextPaint workPaint = mWorkPaint;
+        workPaint.set(paint);
+        // XXX paint should not have a baseline shift, but...
+        workPaint.baselineShift = 0;
+
+        ReplacementSpan replacement = null;
+        for (int i = 0; i < spans.length; i++) {
+            MetricAffectingSpan span = spans[i];
+            if (span instanceof ReplacementSpan) {
+                replacement = (ReplacementSpan)span;
+            } else {
+                span.updateMeasureState(workPaint);
+            }
+        }
+
+        float wid;
+        if (replacement == null) {
+            wid = addStyleRun(workPaint, len, fm, nativeStaticLayoutPtr);
+        } else {
+            // Use original text.  Shouldn't matter.
+            wid = replacement.getSize(workPaint, mText, mTextStart + mPos,
+                    mTextStart + mPos + len, fm);
+            if (nativeStaticLayoutPtr == 0) {
+                float[] w = mWidths;
+                w[mPos] = wid;
+                for (int i = mPos + 1, e = mPos + len; i < e; i++)
+                    w[i] = 0;
+            } else {
+                StaticLayout.addReplacementRun(nativeStaticLayoutPtr, paint, mPos, mPos + len, wid);
+            }
+            mPos += len;
+        }
+
+        if (fm != null) {
+            if (workPaint.baselineShift < 0) {
+                fm.ascent += workPaint.baselineShift;
+                fm.top += workPaint.baselineShift;
+            } else {
+                fm.descent += workPaint.baselineShift;
+                fm.bottom += workPaint.baselineShift;
+            }
+        }
+
+        return wid;
+    }
+
+    float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
+            Paint.FontMetricsInt fm) {
+        return addStyleRun(paint, spans, len, fm, 0 /* native ptr */);
+    }
+
+    int breakText(int limit, boolean forwards, float width) {
+        float[] w = mWidths;
         if (forwards) {
             int i = 0;
             while (i < limit) {
@@ -550,7 +267,7 @@
                 if (width < 0.0f) break;
                 i++;
             }
-            while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
+            while (i > 0 && mChars[i - 1] == ' ') i--;
             return i;
         } else {
             int i = limit - 1;
@@ -559,22 +276,16 @@
                 if (width < 0.0f) break;
                 i--;
             }
-            while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
+            while (i < limit - 1 && (mChars[i + 1] == ' ' || w[i + 1] == 0.0f)) {
                 i++;
             }
             return limit - i - 1;
         }
     }
 
-    /**
-     * Returns the length of the substring.
-     *
-     * This only works if the MeasuredText is computed with computeForMeasurement.
-     * Undefined behavior in other case.
-     */
-    @FloatRange(from = 0.0f) float measure(int start, int limit) {
+    float measure(int start, int limit) {
         float width = 0;
-        float[] w = mWidths.getRawArray();
+        float[] w = mWidths;
         for (int i = start; i < limit; ++i) {
             width += w[i];
         }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 81c82c9..c0fc44f 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,10 +21,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Paint;
-import android.text.AutoGrowArray.FloatArray;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
 import android.text.style.LineHeightSpan;
+import android.text.style.MetricAffectingSpan;
 import android.text.style.TabStopSpan;
 import android.util.Log;
 import android.util.Pools.SynchronizedPool;
@@ -99,6 +99,8 @@
             b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
             b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
             b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
+
+            b.mMeasuredText = MeasuredText.obtain();
             return b;
         }
 
@@ -109,6 +111,8 @@
         private static void recycle(@NonNull Builder b) {
             b.mPaint = null;
             b.mText = null;
+            MeasuredText.recycle(b.mMeasuredText);
+            b.mMeasuredText = null;
             b.mLeftIndents = null;
             b.mRightIndents = null;
             b.mLeftPaddings = null;
@@ -124,6 +128,7 @@
             mRightIndents = null;
             mLeftPaddings = null;
             mRightPaddings = null;
+            mMeasuredText.finish();
         }
 
         public Builder setText(CharSequence source) {
@@ -439,6 +444,9 @@
 
         private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
+        // This will go away and be subsumed by native builder code
+        private MeasuredText mMeasuredText;
+
         private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
     }
 
@@ -610,7 +618,11 @@
         TextUtils.TruncateAt ellipsize = b.mEllipsize;
         final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
         LineBreaks lineBreaks = new LineBreaks();  // TODO: move to builder to avoid allocation costs
-        FloatArray widths = new FloatArray();
+        // store span end locations
+        int[] spanEndCache = new int[4];
+        // store fontMetrics per span range
+        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
+        int[] fmCache = new int[4 * 4];
 
         mLineCount = 0;
         mEllipsized = false;
@@ -622,6 +634,8 @@
         Paint.FontMetricsInt fm = b.mFontMetricsInt;
         int[] chooseHtv = null;
 
+        MeasuredText measured = b.mMeasuredText;
+
         Spanned spanned = null;
         if (source instanceof Spanned)
             spanned = (Spanned) source;
@@ -648,7 +662,6 @@
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                 indents, mLeftPaddings, mRightPaddings);
 
-        MeasuredText measured = null;
         try {
             int paraEnd;
             for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
@@ -708,6 +721,13 @@
                     }
                 }
 
+                measured.setPara(source, paraStart, paraEnd, textDir);
+                char[] chs = measured.mChars;
+                float[] widths = measured.mWidths;
+                byte[] chdirs = measured.mLevels;
+                int dir = measured.mDir;
+                boolean easy = measured.mEasy;
+
                 // tab stop locations
                 int[] variableTabStops = null;
                 if (spanned != null) {
@@ -723,16 +743,50 @@
                     }
                 }
 
-                measured = MeasuredText.buildForStaticLayout(
-                        paint, source, paraStart, paraEnd, textDir, nativePtr, measured);
-                final char[] chs = measured.getChars();
-                final int[] spanEndCache = measured.getSpanEndCache().getRawArray();
-                final int[] fmCache = measured.getFontMetrics().getRawArray();
-                widths.resize(chs.length);
-
                 // measurement has to be done before performing line breaking
                 // but we don't want to recompute fontmetrics or span ranges the
                 // second time, so we cache those and then use those stored values
+                int fmCacheCount = 0;
+                int spanEndCacheCount = 0;
+                for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
+                    if (fmCacheCount * 4 >= fmCache.length) {
+                        int[] grow = new int[fmCacheCount * 4 * 2];
+                        System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
+                        fmCache = grow;
+                    }
+
+                    if (spanEndCacheCount >= spanEndCache.length) {
+                        int[] grow = new int[spanEndCacheCount * 2];
+                        System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
+                        spanEndCache = grow;
+                    }
+
+                    if (spanned == null) {
+                        spanEnd = paraEnd;
+                        int spanLen = spanEnd - spanStart;
+                        measured.addStyleRun(paint, spanLen, fm, nativePtr);
+                    } else {
+                        spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
+                                MetricAffectingSpan.class);
+                        int spanLen = spanEnd - spanStart;
+                        MetricAffectingSpan[] spans =
+                                spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
+                        spans = TextUtils.removeEmptySpans(spans, spanned,
+                                MetricAffectingSpan.class);
+                        measured.addStyleRun(paint, spans, spanLen, fm, nativePtr);
+                    }
+
+                    // the order of storage here (top, bottom, ascent, descent) has to match the
+                    // code below where these values are retrieved
+                    fmCache[fmCacheCount * 4 + 0] = fm.top;
+                    fmCache[fmCacheCount * 4 + 1] = fm.bottom;
+                    fmCache[fmCacheCount * 4 + 2] = fm.ascent;
+                    fmCache[fmCacheCount * 4 + 3] = fm.descent;
+                    fmCacheCount++;
+
+                    spanEndCache[spanEndCacheCount] = spanEnd;
+                    spanEndCacheCount++;
+                }
 
                 int breakCount = nComputeLineBreaks(
                         nativePtr,
@@ -755,7 +809,7 @@
                         lineBreaks.ascents,
                         lineBreaks.descents,
                         lineBreaks.flags,
-                        widths.getRawArray());
+                        widths);
 
                 final int[] breaks = lineBreaks.breaks;
                 final float[] lineWidths = lineBreaks.widths;
@@ -778,7 +832,7 @@
                             width += lineWidths[i];
                         } else {
                             for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
-                                width += widths.get(j);
+                                width += widths[j];
                             }
                         }
                         flag |= flags[i] & TAB_MASK;
@@ -842,10 +896,10 @@
                         v = out(source, here, endPos,
                                 ascent, descent, fmTop, fmBottom,
                                 v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
-                                flags[breakIndex], needMultiply, measured, bufEnd,
-                                includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
-                                paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
-                                paint, moreChars);
+                                flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd,
+                                includepad, trackpad, addLastLineSpacing, chs, widths, paraStart,
+                                ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint,
+                                moreChars);
 
                         if (endPos < spanEnd) {
                             // preserve metrics for current span
@@ -873,8 +927,7 @@
 
             if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
                     && mLineCount < mMaximumVisibleLineCount) {
-                measured = MeasuredText.buildForStaticLayout(
-                        paint, source, bufEnd, bufEnd, textDir, nativePtr, measured);
+                measured.setPara(source, bufEnd, bufEnd, textDir);
 
                 paint.getFontMetricsInt(fm);
 
@@ -884,15 +937,12 @@
                         v,
                         spacingmult, spacingadd, null,
                         null, fm, 0,
-                        needMultiply, measured, bufEnd,
+                        needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
                         includepad, trackpad, addLastLineSpacing, null,
                         null, bufStart, ellipsize,
                         ellipsizedWidth, 0, paint, false);
             }
         } finally {
-            if (measured != null) {
-                measured.recycle();
-            }
             nFinish(nativePtr);
         }
     }
@@ -902,8 +952,8 @@
     private int out(final CharSequence text, final int start, final int end, int above, int below,
             int top, int bottom, int v, final float spacingmult, final float spacingadd,
             final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
-            final int flags, final boolean needMultiply, final MeasuredText measured,
-            final int bufEnd, final boolean includePad, final boolean trackPad,
+            final int flags, final boolean needMultiply, final byte[] chdirs, final int dir,
+            final boolean easy, final int bufEnd, final boolean includePad, final boolean trackPad,
             final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
             final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
             final float textWidth, final TextPaint paint, final boolean moreChars) {
@@ -911,7 +961,6 @@
         final int off = j * mColumns;
         final int want = off + mColumns + TOP;
         int[] lines = mLines;
-        final int dir = measured.getParagraphDir();
 
         if (want >= lines.length) {
             final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want));
@@ -937,8 +986,17 @@
         // one bit for start field
         lines[off + TAB] |= flags & TAB_MASK;
         lines[off + HYPHEN] = flags;
+
         lines[off + DIR] |= dir << DIR_SHIFT;
-        mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
+        // easy means all chars < the first RTL, so no emoji, no nothing
+        // XXX a run with no text or all spaces is easy but might be an empty
+        // RTL paragraph.  Make sure easy is false if this is the case.
+        if (easy) {
+            mLineDirections[j] = DIRS_ALL_LEFT_TO_RIGHT;
+        } else {
+            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
+                    start - widthStart, end - start);
+        }
 
         final boolean firstLine = (j == 0);
         final boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index af66157..cbdaa69 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -42,6 +42,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LocaleSpan;
+import android.text.style.MetricAffectingSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.QuoteSpan;
 import android.text.style.RelativeSizeSpan;
@@ -1250,11 +1251,10 @@
             @NonNull String ellipsis) {
 
         final int len = text.length();
-        MeasuredText mt = null;
+        final MeasuredText mt = MeasuredText.obtain();
         MeasuredText resultMt = null;
         try {
-            mt = MeasuredText.buildForMeasurement(paint, text, 0, text.length(), textDir, mt);
-            float width = mt.getWholeWidth();
+            float width = setPara(mt, paint, text, 0, text.length(), textDir);
 
             if (width <= avail) {
                 if (callback != null) {
@@ -1263,6 +1263,7 @@
                 return text;
             }
 
+            resultMt = MeasuredText.obtain();
             // First estimate of effective width of ellipsis.
             float ellipsisWidth = paint.measureText(ellipsis);
             int numberOfTries = 0;
@@ -1289,7 +1290,7 @@
                     }
                 }
 
-                final char[] buf = mt.getChars();
+                final char[] buf = mt.mChars;
                 final Spanned sp = text instanceof Spanned ? (Spanned) text : null;
 
                 final int removed = end - start;
@@ -1332,9 +1333,7 @@
                 if (remaining == 0) { // All text is gone.
                     textFits = true;
                 } else {
-                    resultMt = MeasuredText.buildForMeasurement(
-                            paint, result, 0, result.length(), textDir, resultMt);
-                    width = resultMt.getWholeWidth();
+                    width = setPara(resultMt, paint, result, 0, result.length(), textDir);
                     if (width <= avail) {
                         textFits = true;
                     } else {
@@ -1358,11 +1357,9 @@
             }
             return result;
         } finally {
-            if (mt != null) {
-                mt.recycle();
-            }
+            MeasuredText.recycle(mt);
             if (resultMt != null) {
-                resultMt.recycle();
+                MeasuredText.recycle(resultMt);
             }
         }
     }
@@ -1479,17 +1476,15 @@
     public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
          float avail, String oneMore, String more, TextDirectionHeuristic textDir) {
 
-        MeasuredText mt = null;
-        MeasuredText tempMt = null;
+        MeasuredText mt = MeasuredText.obtain();
         try {
             int len = text.length();
-            mt = MeasuredText.buildForMeasurement(p, text, 0, len, textDir, mt);
-            float width = mt.getWholeWidth();
+            float width = setPara(mt, p, text, 0, len, textDir);
             if (width <= avail) {
                 return text;
             }
 
-            char[] buf = mt.getChars();
+            char[] buf = mt.mChars;
 
             int commaCount = 0;
             for (int i = 0; i < len; i++) {
@@ -1505,8 +1500,9 @@
 
             int w = 0;
             int count = 0;
-            float[] widths = mt.getWidths().getRawArray();
+            float[] widths = mt.mWidths;
 
+            MeasuredText tempMt = MeasuredText.obtain();
             for (int i = 0; i < len; i++) {
                 w += widths[i];
 
@@ -1523,9 +1519,8 @@
                     }
 
                     // XXX this is probably ok, but need to look at it more
-                    tempMt = MeasuredText.buildForMeasurement(
-                            p, format, 0, format.length(), textDir, tempMt);
-                    float moreWid = tempMt.getWholeWidth();
+                    tempMt.setPara(format, 0, format.length(), textDir);
+                    float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null);
 
                     if (w + moreWid <= avail) {
                         ok = i + 1;
@@ -1533,18 +1528,40 @@
                     }
                 }
             }
+            MeasuredText.recycle(tempMt);
 
             SpannableStringBuilder out = new SpannableStringBuilder(okFormat);
             out.insert(0, text, 0, ok);
             return out;
         } finally {
-            if (mt != null) {
-                mt.recycle();
-            }
-            if (tempMt != null) {
-                tempMt.recycle();
+            MeasuredText.recycle(mt);
+        }
+    }
+
+    private static float setPara(MeasuredText mt, TextPaint paint,
+            CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
+
+        mt.setPara(text, start, end, textDir);
+
+        float width;
+        Spanned sp = text instanceof Spanned ? (Spanned) text : null;
+        int len = end - start;
+        if (sp == null) {
+            width = mt.addStyleRun(paint, len, null);
+        } else {
+            width = 0;
+            int spanEnd;
+            for (int spanStart = 0; spanStart < len; spanStart = spanEnd) {
+                spanEnd = sp.nextSpanTransition(spanStart, len,
+                        MetricAffectingSpan.class);
+                MetricAffectingSpan[] spans = sp.getSpans(
+                        spanStart, spanEnd, MetricAffectingSpan.class);
+                spans = TextUtils.removeEmptySpans(spans, sp, MetricAffectingSpan.class);
+                width += mt.addStyleRun(paint, spans, spanEnd - spanStart, null);
             }
         }
+
+        return width;
     }
 
     // Returns true if the character's presence could affect RTL layout.
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index 2bcd863..26a3c36 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -93,10 +93,11 @@
     }
 
     /**
-     * Clients can request data with a binder call.
+     * Clients can request data with a binder call. This getter is destructive and also clears
+     * the retrieved metrics from statsd memory.
      *
      * @param configKey Configuration key to retrieve data from.
-     * @return Serialized ConfigMetricsReport proto. Returns null on failure.
+     * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
      */
     @RequiresPermission(Manifest.permission.DUMP)
     public byte[] getData(String configKey) {
@@ -115,6 +116,30 @@
         }
     }
 
+    /**
+     * Clients can request metadata for statsd. Will contain stats across all configurations but not
+     * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
+     * This getter is not destructive and will not reset any metrics/counters.
+     *
+     * @return Serialized StatsdStatsReport proto. Returns null on failure.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public byte[] getMetadata() {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.d(TAG, "Failed to find statsd when getting metadata");
+                    return null;
+                }
+                return service.getMetadata();
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Failed to connecto statsd when getting metadata");
+                return null;
+            }
+        }
+    }
+
     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3d01ec2..e3da757 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -16,6 +16,9 @@
 
 package android.view;
 
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
 import android.annotation.Size;
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
@@ -23,6 +26,8 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
@@ -36,12 +41,14 @@
  * SurfaceControl
  *  @hide
  */
-public class SurfaceControl {
+public class SurfaceControl implements Parcelable {
     private static final String TAG = "SurfaceControl";
 
     private static native long nativeCreate(SurfaceSession session, String name,
             int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid)
             throws OutOfResourcesException;
+    private static native long nativeReadFromParcel(Parcel in);
+    private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeDestroy(long nativeObject);
     private static native void nativeDisconnect(long nativeObject);
@@ -55,8 +62,6 @@
     private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
             Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
             boolean allLayers, boolean useIdentityTransform);
-    private static native void nativeCaptureLayers(IBinder layerHandleToken, Surface consumer,
-            Rect sourceCrop, float frameScale);
     private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
             Rect sourceCrop, float frameScale);
 
@@ -576,6 +581,37 @@
         mCloseGuard.open("release");
     }
 
+    private SurfaceControl(Parcel in) {
+        mName = in.readString();
+        mNativeObject = nativeReadFromParcel(in);
+        if (mNativeObject == 0) {
+            throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
+        }
+        mCloseGuard.open("release");
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mName);
+        nativeWriteToParcel(mNativeObject, dest);
+    }
+
+    public static final Creator<SurfaceControl> CREATOR
+            = new Creator<SurfaceControl>() {
+        public SurfaceControl createFromParcel(Parcel in) {
+            return new SurfaceControl(in);
+        }
+
+        public SurfaceControl[] newArray(int size) {
+            return new SurfaceControl[size];
+        }
+    };
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -1144,22 +1180,35 @@
 
     /**
      * Like {@link SurfaceControl#screenshot(int, int, int, int, boolean)} but
-     * includes all Surfaces in the screenshot.
+     * includes all Surfaces in the screenshot. This will also update the orientation so it
+     * sends the correct coordinates to SF based on the rotation value.
      *
+     * @param sourceCrop The portion of the screen to capture into the Bitmap;
+     * caller may pass in 'new Rect()' if no cropping is desired.
      * @param width The desired width of the returned bitmap; the raw
      * screen will be scaled down to this size.
      * @param height The desired height of the returned bitmap; the raw
      * screen will be scaled down to this size.
+     * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
+     * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take
+     * screenshots in its native portrait orientation by default, so this is
+     * useful for returning screenshots that are independent of device
+     * orientation.
      * @return Returns a Bitmap containing the screen contents, or null
      * if an error occurs. Make sure to call Bitmap.recycle() as soon as
      * possible, once its content is not needed anymore.
      */
-    public static Bitmap screenshot(int width, int height) {
+    public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
         // TODO: should take the display as a parameter
         IBinder displayToken = SurfaceControl.getBuiltInDisplay(
                 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
-        return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true,
-                false, Surface.ROTATION_0);
+        if (rotation == ROTATION_90 || rotation == ROTATION_270) {
+            rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90;
+        }
+
+        SurfaceControl.rotateCropForSF(sourceCrop, rotation);
+        return nativeScreenshot(displayToken, sourceCrop, width, height, 0, 0, true,
+                false, rotation);
     }
 
     private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop,
@@ -1175,26 +1224,29 @@
                 minLayer, maxLayer, allLayers, useIdentityTransform);
     }
 
+    private static void rotateCropForSF(Rect crop, int rot) {
+        if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+            int tmp = crop.top;
+            crop.top = crop.left;
+            crop.left = tmp;
+            tmp = crop.right;
+            crop.right = crop.bottom;
+            crop.bottom = tmp;
+        }
+    }
+
     /**
      * Captures a layer and its children into the provided {@link Surface}.
      *
      * @param layerHandleToken The root layer to capture.
-     * @param consumer         The {@link Surface} to capture the layer into.
      * @param sourceCrop       The portion of the root surface to capture; caller may pass in 'new
      *                         Rect()' or null if no cropping is desired.
      * @param frameScale       The desired scale of the returned buffer; the raw
      *                         screen will be scaled up/down.
+     *
+     * @return Returns a GraphicBuffer that contains the layer capture.
      */
-    public static void captureLayers(IBinder layerHandleToken, Surface consumer, Rect sourceCrop,
-            float frameScale) {
-        nativeCaptureLayers(layerHandleToken, consumer, sourceCrop, frameScale);
-    }
-
-    /**
-     * Same as {@link #captureLayers(IBinder, Surface, Rect, float)} except this
-     * captures to a {@link GraphicBuffer} instead of a {@link Surface}.
-     */
-    public static GraphicBuffer captureLayersToBuffer(IBinder layerHandleToken, Rect sourceCrop,
+    public static GraphicBuffer captureLayers(IBinder layerHandleToken, Rect sourceCrop,
             float frameScale) {
         return nativeCaptureLayers(layerHandleToken, sourceCrop, frameScale);
     }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 547e0db..9a99e53 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -24,6 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -36,6 +37,7 @@
 import android.os.RemoteException;
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillEventHistory;
+import android.service.autofill.UserData;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -1007,6 +1009,54 @@
     }
 
     /**
+     * Gets the user data used for <a href="#FieldsClassification">fields classification</a>.
+     *
+     * <p><b>Note:</b> This method should only be called by an app providing an autofill service.
+     *
+     * TODO(b/67867469):
+     *  - proper javadoc
+     *  - unhide / remove testApi
+     *
+     * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
+     * reset or if the caller currently does not have an enabled autofill service for the user.
+     *
+     * @hide
+     */
+    @TestApi
+    @Nullable public UserData getUserData() {
+        try {
+            return mService.getUserData();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Sets the user data used for <a href="#FieldsClassification">fields classification</a>.
+     *
+     * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+     * and it's ignored if the caller currently doesn't have an enabled autofill service for
+     * the user.
+     *
+     * TODO(b/67867469):
+     *  - proper javadoc
+     *  - unhide / remove testApi
+     *  - add unit tests:
+     *    - call set / get / verify
+     *
+     * @hide
+     */
+    @TestApi
+    public void setUserData(@Nullable UserData userData) {
+        try {
+            mService.setUserData(userData);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns {@code true} if autofill is supported by the current device and
      * is supported for this user.
      *
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 3beae11..8e649de 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -177,7 +177,7 @@
                 .append("[type=").append(mType)
                 .append(", value=");
         if (isText()) {
-            string.append(((CharSequence) mValue).length()).append("_chars");
+            Helper.appendRedacted(string, (CharSequence) mValue);
         } else {
             string.append(mValue);
         }
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index 829e7f3..b95704a 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -16,6 +16,8 @@
 
 package android.view.autofill;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Bundle;
 
 import java.util.Arrays;
@@ -50,6 +52,35 @@
         return builder;
     }
 
+    /**
+     * Appends {@code value} to the {@code builder} redacting its contents.
+     */
+    public static void appendRedacted(@NonNull StringBuilder builder,
+            @Nullable CharSequence value) {
+        if (value == null) {
+            builder.append("null");
+        } else {
+            builder.append(value.length()).append("_chars");
+        }
+    }
+
+    /**
+     * Appends {@code values} to the {@code builder} redacting its contents.
+     */
+    public static void appendRedacted(@NonNull StringBuilder builder, @Nullable String[] values) {
+        if (values == null) {
+            builder.append("N/A");
+            return;
+        }
+        builder.append("[");
+        for (String value : values) {
+            builder.append(" '");
+            appendRedacted(builder, value);
+            builder.append("'");
+        }
+        builder.append(" ]");
+    }
+
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index d6db3fe..7d6a19f 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.service.autofill.FillEventHistory;
+import android.service.autofill.UserData;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
@@ -53,4 +54,6 @@
     boolean isServiceSupported(int userId);
     boolean isServiceEnabled(int userId, String packageName);
     void onPendingSaveUi(int operation, IBinder token);
+    UserData getUserData();
+    void setUserData(in UserData userData);
 }
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 8916323..7ffbf63 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -39,7 +39,8 @@
  * Information for generating a widget to handle classified text.
  *
  * <p>A TextClassification object contains icons, labels, onClickListeners and intents that may
- * be used to build a widget that can be used to act on classified text.
+ * be used to build a widget that can be used to act on classified text. There is the concept of a
+ * <i>primary action</i> and other <i>secondary actions</i>.
  *
  * <p>e.g. building a view that, when clicked, shares the classified text with the preferred app:
  *
@@ -64,11 +65,18 @@
  *   view.startActionMode(new ActionMode.Callback() {
  *
  *       public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- *           for (int i = 0; i < classification.getActionCount(); i++) {
- *               if (thisAppHasPermissionToInvokeIntent(classification.getIntent(i))) {
- *                   menu.add(Menu.NONE, i, 20, classification.getLabel(i))
- *                      .setIcon(classification.getIcon(i))
- *                      .setIntent(classification.getIntent(i));
+ *           // Add the "primary" action.
+ *           if (thisAppHasPermissionToInvokeIntent(classification.getIntent())) {
+ *              menu.add(Menu.NONE, 0, 20, classification.getLabel())
+ *                 .setIcon(classification.getIcon())
+ *                 .setIntent(classification.getIntent());
+ *           }
+ *           // Add the "secondary" actions.
+ *           for (int i = 0; i < classification.getSecondaryActionsCount(); i++) {
+ *               if (thisAppHasPermissionToInvokeIntent(classification.getSecondaryIntent(i))) {
+ *                   menu.add(Menu.NONE, i + 1, 20, classification.getSecondaryLabel(i))
+ *                      .setIcon(classification.getSecondaryIcon(i))
+ *                      .setIntent(classification.getSecondaryIntent(i));
  *               }
  *           }
  *           return true;
@@ -92,34 +100,43 @@
     static final TextClassification EMPTY = new TextClassification.Builder().build();
 
     @NonNull private final String mText;
-    @NonNull private final List<Drawable> mIcons;
-    @NonNull private final List<String> mLabels;
-    @NonNull private final List<Intent> mIntents;
-    @NonNull private final List<OnClickListener> mOnClickListeners;
+    @Nullable private final Drawable mPrimaryIcon;
+    @Nullable private final String mPrimaryLabel;
+    @Nullable private final Intent mPrimaryIntent;
+    @Nullable private final OnClickListener mPrimaryOnClickListener;
+    @NonNull private final List<Drawable> mSecondaryIcons;
+    @NonNull private final List<String> mSecondaryLabels;
+    @NonNull private final List<Intent> mSecondaryIntents;
+    @NonNull private final List<OnClickListener> mSecondaryOnClickListeners;
     @NonNull private final EntityConfidence<String> mEntityConfidence;
-    private int mLogType;
-    @NonNull private final String mVersionInfo;
+    @NonNull private final String mSignature;
 
     private TextClassification(
             @Nullable String text,
-            @NonNull List<Drawable> icons,
-            @NonNull List<String> labels,
-            @NonNull List<Intent> intents,
-            @NonNull List<OnClickListener> onClickListeners,
+            @Nullable Drawable primaryIcon,
+            @Nullable String primaryLabel,
+            @Nullable Intent primaryIntent,
+            @Nullable OnClickListener primaryOnClickListener,
+            @NonNull List<Drawable> secondaryIcons,
+            @NonNull List<String> secondaryLabels,
+            @NonNull List<Intent> secondaryIntents,
+            @NonNull List<OnClickListener> secondaryOnClickListeners,
             @NonNull Map<String, Float> entityConfidence,
-            int logType,
-            @NonNull String versionInfo) {
-        Preconditions.checkArgument(labels.size() == intents.size());
-        Preconditions.checkArgument(icons.size() == intents.size());
-        Preconditions.checkArgument(onClickListeners.size() == intents.size());
+            @NonNull String signature) {
+        Preconditions.checkArgument(secondaryLabels.size() == secondaryIntents.size());
+        Preconditions.checkArgument(secondaryIcons.size() == secondaryIntents.size());
+        Preconditions.checkArgument(secondaryOnClickListeners.size() == secondaryIntents.size());
         mText = text;
-        mIcons = icons;
-        mLabels = labels;
-        mIntents = intents;
-        mOnClickListeners = onClickListeners;
+        mPrimaryIcon = primaryIcon;
+        mPrimaryLabel = primaryLabel;
+        mPrimaryIntent = primaryIntent;
+        mPrimaryOnClickListener = primaryOnClickListener;
+        mSecondaryIcons = secondaryIcons;
+        mSecondaryLabels = secondaryLabels;
+        mSecondaryIntents = secondaryIntents;
+        mSecondaryOnClickListeners = secondaryOnClickListeners;
         mEntityConfidence = new EntityConfidence<>(entityConfidence);
-        mLogType = logType;
-        mVersionInfo = versionInfo;
+        mSignature = signature;
     }
 
     /**
@@ -161,130 +178,160 @@
     }
 
     /**
-     * Returns the number of actions that are available to act on the classified text.
-     * @see #getIntent(int)
-     * @see #getLabel(int)
-     * @see #getIcon(int)
-     * @see #getOnClickListener(int)
+     * Returns the number of <i>secondary</i> actions that are available to act on the classified
+     * text.
+     *
+     * <p><strong>Note: </strong> that there may or may not be a <i>primary</i> action.
+     *
+     * @see #getSecondaryIntent(int)
+     * @see #getSecondaryLabel(int)
+     * @see #getSecondaryIcon(int)
+     * @see #getSecondaryOnClickListener(int)
      */
     @IntRange(from = 0)
-    public int getActionCount() {
-        return mIntents.size();
+    public int getSecondaryActionsCount() {
+        return mSecondaryIntents.size();
     }
 
     /**
-     * Returns one of the icons that maybe rendered on a widget used to act on the classified text.
+     * Returns one of the <i>secondary</i> icons that maybe rendered on a widget used to act on the
+     * classified text.
+     *
      * @param index Index of the action to get the icon for.
+     *
      * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getActionCount() for the number of entities available.
-     * @see #getIntent(int)
-     * @see #getLabel(int)
-     * @see #getOnClickListener(int)
+     *
+     * @see #getSecondaryActionsCount() for the number of actions available.
+     * @see #getSecondaryIntent(int)
+     * @see #getSecondaryLabel(int)
+     * @see #getSecondaryOnClickListener(int)
+     * @see #getIcon()
      */
     @Nullable
-    public Drawable getIcon(int index) {
-        return mIcons.get(index);
+    public Drawable getSecondaryIcon(int index) {
+        return mSecondaryIcons.get(index);
     }
 
     /**
-     * Returns an icon for the default intent that may be rendered on a widget used to act on the
-     * classified text.
+     * Returns an icon for the <i>primary</i> intent that may be rendered on a widget used to act
+     * on the classified text.
+     *
+     * @see #getSecondaryIcon(int)
      */
     @Nullable
     public Drawable getIcon() {
-        return mIcons.isEmpty() ? null : mIcons.get(0);
+        return mPrimaryIcon;
     }
 
     /**
-     * Returns one of the labels that may be rendered on a widget used to act on the classified
-     * text.
+     * Returns one of the <i>secondary</i> labels that may be rendered on a widget used to act on
+     * the classified text.
+     *
      * @param index Index of the action to get the label for.
+     *
      * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getActionCount()
-     * @see #getIntent(int)
-     * @see #getIcon(int)
-     * @see #getOnClickListener(int)
+     *
+     * @see #getSecondaryActionsCount()
+     * @see #getSecondaryIntent(int)
+     * @see #getSecondaryIcon(int)
+     * @see #getSecondaryOnClickListener(int)
+     * @see #getLabel()
      */
     @Nullable
-    public CharSequence getLabel(int index) {
-        return mLabels.get(index);
+    public CharSequence getSecondaryLabel(int index) {
+        return mSecondaryLabels.get(index);
     }
 
     /**
-     * Returns a label for the default intent that may be rendered on a widget used to act on the
-     * classified text.
+     * Returns a label for the <i>primary</i> intent that may be rendered on a widget used to act
+     * on the classified text.
+     *
+     * @see #getSecondaryLabel(int)
      */
     @Nullable
     public CharSequence getLabel() {
-        return mLabels.isEmpty() ? null : mLabels.get(0);
+        return mPrimaryLabel;
     }
 
     /**
-     * Returns one of the intents that may be fired to act on the classified text.
+     * Returns one of the <i>secondary</i> intents that may be fired to act on the classified text.
+     *
      * @param index Index of the action to get the intent for.
+     *
      * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getActionCount()
-     * @see #getLabel(int)
-     * @see #getIcon(int)
-     * @see #getOnClickListener(int)
+     *
+     * @see #getSecondaryActionsCount()
+     * @see #getSecondaryLabel(int)
+     * @see #getSecondaryIcon(int)
+     * @see #getSecondaryOnClickListener(int)
+     * @see #getIntent()
      */
     @Nullable
-    public Intent getIntent(int index) {
-        return mIntents.get(index);
+    public Intent getSecondaryIntent(int index) {
+        return mSecondaryIntents.get(index);
     }
 
     /**
-     * Returns the default intent that may be fired to act on the classified text.
+     * Returns the <i>primary</i> intent that may be fired to act on the classified text.
+     *
+     * @see #getSecondaryIntent(int)
      */
     @Nullable
     public Intent getIntent() {
-        return mIntents.isEmpty() ? null : mIntents.get(0);
+        return mPrimaryIntent;
     }
 
     /**
-     * Returns one of the OnClickListeners that may be triggered to act on the classified text.
+     * Returns one of the <i>secondary</i> OnClickListeners that may be triggered to act on the
+     * classified text.
+     *
      * @param index Index of the action to get the click listener for.
+     *
      * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getActionCount()
-     * @see #getIntent(int)
-     * @see #getLabel(int)
-     * @see #getIcon(int)
+     *
+     * @see #getSecondaryActionsCount()
+     * @see #getSecondaryIntent(int)
+     * @see #getSecondaryLabel(int)
+     * @see #getSecondaryIcon(int)
+     * @see #getOnClickListener()
      */
     @Nullable
-    public OnClickListener getOnClickListener(int index) {
-        return mOnClickListeners.get(index);
+    public OnClickListener getSecondaryOnClickListener(int index) {
+        return mSecondaryOnClickListeners.get(index);
     }
 
     /**
-     * Returns the default OnClickListener that may be triggered to act on the classified text.
+     * Returns the <i>primary</i> OnClickListener that may be triggered to act on the classified
+     * text.
+     *
+     * @see #getSecondaryOnClickListener(int)
      */
     @Nullable
     public OnClickListener getOnClickListener() {
-        return mOnClickListeners.isEmpty() ? null : mOnClickListeners.get(0);
+        return mPrimaryOnClickListener;
     }
 
     /**
-     * Returns the MetricsLogger subtype for the action that is performed for this result.
-     * @hide
-     */
-    public int getLogType() {
-        return mLogType;
-    }
-
-    /**
-     * Returns information about the classifier model used to generate this TextClassification.
-     * @hide
+     * Returns the signature for this object.
+     * The TextClassifier that generates this object may use it as a way to internally identify
+     * this object.
      */
     @NonNull
-    public String getVersionInfo() {
-        return mVersionInfo;
+    public String getSignature() {
+        return mSignature;
     }
 
     @Override
     public String toString() {
-        return String.format(Locale.US,
-                "TextClassification {text=%s, entities=%s, labels=%s, intents=%s}",
-                mText, mEntityConfidence, mLabels, mIntents);
+        return String.format(Locale.US, "TextClassification {"
+                        + "text=%s, entities=%s, "
+                        + "primaryLabel=%s, secondaryLabels=%s, "
+                        + "primaryIntent=%s, secondaryIntents=%s, "
+                        + "signature=%s}",
+                mText, mEntityConfidence,
+                mPrimaryLabel, mSecondaryLabels,
+                mPrimaryIntent, mSecondaryIntents,
+                mSignature);
     }
 
     /**
@@ -303,17 +350,33 @@
 
     /**
      * Builder for building {@link TextClassification} objects.
+     *
+     * <p>e.g.
+     *
+     * <pre>{@code
+     *   TextClassification classification = new TextClassification.Builder()
+     *          .setText(classifiedText)
+     *          .setEntityType(TextClassifier.TYPE_EMAIL, 0.9)
+     *          .setEntityType(TextClassifier.TYPE_OTHER, 0.1)
+     *          .setPrimaryAction(intent, label, icon, onClickListener)
+     *          .addSecondaryAction(intent1, label1, icon1, onClickListener1)
+     *          .addSecondaryAction(intent2, label2, icon2, onClickListener2)
+     *          .build();
+     * }</pre>
      */
     public static final class Builder {
 
         @NonNull private String mText;
-        @NonNull private final List<Drawable> mIcons = new ArrayList<>();
-        @NonNull private final List<String> mLabels = new ArrayList<>();
-        @NonNull private final List<Intent> mIntents = new ArrayList<>();
-        @NonNull private final List<OnClickListener> mOnClickListeners = new ArrayList<>();
+        @NonNull private final List<Drawable> mSecondaryIcons = new ArrayList<>();
+        @NonNull private final List<String> mSecondaryLabels = new ArrayList<>();
+        @NonNull private final List<Intent> mSecondaryIntents = new ArrayList<>();
+        @NonNull private final List<OnClickListener> mSecondaryOnClickListeners = new ArrayList<>();
         @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        private int mLogType;
-        @NonNull private String mVersionInfo = "";
+        @Nullable Drawable mPrimaryIcon;
+        @Nullable String mPrimaryLabel;
+        @Nullable Intent mPrimaryIntent;
+        @Nullable OnClickListener mPrimaryOnClickListener;
+        @NonNull private String mSignature = "";
 
         /**
          * Sets the classified text.
@@ -325,6 +388,8 @@
 
         /**
          * Sets an entity type for the classification result and assigns a confidence score.
+         * If a confidence score had already been set for the specified entity type, this will
+         * override that score.
          *
          * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
          *      0 implies the entity does not exist for the classified text.
@@ -338,105 +403,123 @@
         }
 
         /**
-         * Adds an action that may be performed on the classified text. The label and icon are used
-         * for rendering of widgets that offer the intent. Actions should be added in order of
-         * priority and the first one will be treated as the default.
+         * Adds an <i>secondary</i> action that may be performed on the classified text.
+         * Secondary actions are in addition to the <i>primary</i> action which may or may not
+         * exist.
+         *
+         * <p>The label and icon are used for rendering of widgets that offer the intent.
+         * Actions should be added in order of priority.
+         *
+         * <p><stong>Note: </stong> If all input parameters are set to null, this method will be a
+         * no-op.
+         *
+         * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
          */
-        public Builder addAction(
-                Intent intent, @Nullable String label, @Nullable Drawable icon,
+        public Builder addSecondaryAction(
+                @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon,
                 @Nullable OnClickListener onClickListener) {
-            mIntents.add(intent);
-            mLabels.add(label);
-            mIcons.add(icon);
-            mOnClickListeners.add(onClickListener);
+            if (intent != null || label != null || icon != null || onClickListener != null) {
+                mSecondaryIntents.add(intent);
+                mSecondaryLabels.add(label);
+                mSecondaryIcons.add(icon);
+                mSecondaryOnClickListeners.add(onClickListener);
+            }
             return this;
         }
 
         /**
-         * Removes all actions.
+         * Removes all the <i>secondary</i> actions.
          */
-        public Builder clearActions() {
-            mIntents.clear();
-            mOnClickListeners.clear();
-            mLabels.clear();
-            mIcons.clear();
+        public Builder clearSecondaryActions() {
+            mSecondaryIntents.clear();
+            mSecondaryOnClickListeners.clear();
+            mSecondaryLabels.clear();
+            mSecondaryIcons.clear();
             return this;
         }
 
         /**
-         * Sets the icon for the default action that may be rendered on a widget used to act on the
-         * classified text.
+         * Sets the <i>primary</i> action that may be performed on the classified text. This is
+         * equivalent to calling {@code
+         * setIntent(intent).setLabel(label).setIcon(icon).setOnClickListener(onClickListener)}.
+         *
+         * <p><strong>Note: </strong>If all input parameters are null, there will be no
+         * <i>primary</i> action but there may still be <i>secondary</i> actions.
+         *
+         * @see #addSecondaryAction(Intent, String, Drawable, OnClickListener)
+         */
+        public Builder setPrimaryAction(
+                @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon,
+                @Nullable OnClickListener onClickListener) {
+            return setIntent(intent).setLabel(label).setIcon(icon)
+                    .setOnClickListener(onClickListener);
+        }
+
+        /**
+         * Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
+         * on the classified text.
+         *
+         * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
          */
         public Builder setIcon(@Nullable Drawable icon) {
-            ensureDefaultActionAvailable();
-            mIcons.set(0, icon);
+            mPrimaryIcon = icon;
             return this;
         }
 
         /**
-         * Sets the label for the default action that may be rendered on a widget used to act on the
-         * classified text.
+         * Sets the label for the <i>primary</i> action that may be rendered on a widget used to
+         * act on the classified text.
+         *
+         * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
          */
         public Builder setLabel(@Nullable String label) {
-            ensureDefaultActionAvailable();
-            mLabels.set(0, label);
+            mPrimaryLabel = label;
             return this;
         }
 
         /**
-         * Sets the intent for the default action that may be fired to act on the classified text.
+         * Sets the intent for the <i>primary</i> action that may be fired to act on the classified
+         * text.
+         *
+         * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
          */
         public Builder setIntent(@Nullable Intent intent) {
-            ensureDefaultActionAvailable();
-            mIntents.set(0, intent);
+            mPrimaryIntent = intent;
             return this;
         }
 
         /**
-         * Sets the MetricsLogger subtype for the action that is performed for this result.
-         * @hide
-         */
-        public Builder setLogType(int type) {
-            mLogType = type;
-            return this;
-        }
-
-        /**
-         * Sets the OnClickListener for the default action that may be triggered to act on the
-         * classified text.
+         * Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
+         * the classified text.
+         *
+         * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
          */
         public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
-            ensureDefaultActionAvailable();
-            mOnClickListeners.set(0, onClickListener);
+            mPrimaryOnClickListener = onClickListener;
             return this;
         }
 
         /**
-         * Sets information about the classifier model used to generate this TextClassification.
-         * @hide
+         * Sets a signature for the TextClassification object.
+         * The TextClassifier that generates the TextClassification object may use it as a way to
+         * internally identify the TextClassification object.
          */
-        Builder setVersionInfo(@NonNull String versionInfo) {
-            mVersionInfo = Preconditions.checkNotNull(versionInfo);
+        public Builder setSignature(@NonNull String signature) {
+            mSignature = Preconditions.checkNotNull(signature);
             return this;
         }
 
         /**
-         * Ensures that we have storage for the default action.
-         */
-        private void ensureDefaultActionAvailable() {
-            if (mIntents.isEmpty()) mIntents.add(null);
-            if (mLabels.isEmpty()) mLabels.add(null);
-            if (mIcons.isEmpty()) mIcons.add(null);
-            if (mOnClickListeners.isEmpty()) mOnClickListeners.add(null);
-        }
-
-        /**
          * Builds and returns a {@link TextClassification} object.
          */
         public TextClassification build() {
             return new TextClassification(
-                    mText, mIcons, mLabels, mIntents, mOnClickListeners, mEntityConfidence,
-                    mLogType, mVersionInfo);
+                    mText,
+                    mPrimaryIcon, mPrimaryLabel,
+                    mPrimaryIntent, mPrimaryOnClickListener,
+                    mSecondaryIcons, mSecondaryLabels,
+                    mSecondaryIntents, mSecondaryOnClickListeners,
+                    mEntityConfidence, mSignature);
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 5aaa5ad..f4cbc54 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -37,7 +37,7 @@
 public interface TextClassifier {
 
     /** @hide */
-    String DEFAULT_LOG_TAG = "TextClassifierImpl";
+    String DEFAULT_LOG_TAG = "androidtc";
 
     String TYPE_UNKNOWN = "";
     String TYPE_OTHER = "other";
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index df5e35f..6cf6b69 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -32,7 +32,7 @@
 import android.provider.Settings;
 import android.text.util.Linkify;
 import android.util.Patterns;
-import android.widget.TextViewMetrics;
+import android.view.View.OnClickListener;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -121,8 +121,8 @@
                         tsBuilder.setEntityType(results[i].mCollection, results[i].mScore);
                     }
                     return tsBuilder
-                            .setLogSource(LOG_TAG)
-                            .setVersionInfo(getVersionInfo())
+                            .setSignature(
+                                    getSignature(string, selectionStartIndex, selectionEndIndex))
                             .build();
                 } else {
                     // We can not trust the result. Log the issue and ignore the result.
@@ -154,8 +154,7 @@
                                 getHintFlags(string, startIndex, endIndex));
                 if (results.length > 0) {
                     final TextClassification classificationResult =
-                            createClassificationResult(
-                                    results, string.subSequence(startIndex, endIndex));
+                            createClassificationResult(results, string, startIndex, endIndex);
                     return classificationResult;
                 }
             }
@@ -229,13 +228,13 @@
         }
     }
 
-    @NonNull
-    private String getVersionInfo() {
+    private String getSignature(String text, int start, int end) {
         synchronized (mSmartSelectionLock) {
-            if (mLocale != null) {
-                return String.format("%s_v%d", mLocale.toLanguageTag(), mVersion);
-            }
-            return "";
+            final String versionInfo = (mLocale != null)
+                    ? String.format(Locale.US, "%s_v%d", mLocale.toLanguageTag(), mVersion)
+                    : "";
+            final int hash = Objects.hash(text, start, end, mContext.getPackageName());
+            return String.format(Locale.US, "%s|%s|%d", LOG_TAG, versionInfo, hash);
         }
     }
 
@@ -371,9 +370,11 @@
     }
 
     private TextClassification createClassificationResult(
-            SmartSelection.ClassificationResult[] classifications, CharSequence text) {
+            SmartSelection.ClassificationResult[] classifications,
+            String text, int start, int end) {
+        final String classifiedText = text.substring(start, end);
         final TextClassification.Builder builder = new TextClassification.Builder()
-                .setText(text.toString());
+                .setText(classifiedText);
 
         final int size = classifications.length;
         for (int i = 0; i < size; i++) {
@@ -381,50 +382,54 @@
         }
 
         final String type = getHighestScoringType(classifications);
-        builder.setLogType(IntentFactory.getLogType(type));
+        addActions(builder, IntentFactory.create(mContext, type, text));
 
-        final List<Intent> intents = IntentFactory.create(mContext, type, text.toString());
-        for (Intent intent : intents) {
-            extendClassificationWithIntent(intent, builder);
-        }
-
-        return builder.setVersionInfo(getVersionInfo()).build();
+        return builder.setSignature(getSignature(text, start, end)).build();
     }
 
-    /** Extends the classification with the intent if it can be resolved. */
-    private void extendClassificationWithIntent(Intent intent, TextClassification.Builder builder) {
-        final PackageManager pm;
-        final ResolveInfo resolveInfo;
-        if (intent != null) {
-            pm = mContext.getPackageManager();
-            resolveInfo = pm.resolveActivity(intent, 0);
-        } else {
-            pm = null;
-            resolveInfo = null;
-        }
-        if (resolveInfo != null && resolveInfo.activityInfo != null) {
-            final String packageName = resolveInfo.activityInfo.packageName;
-            CharSequence label;
-            Drawable icon;
-            if ("android".equals(packageName)) {
-                // Requires the chooser to find an activity to handle the intent.
-                label = IntentFactory.getLabel(mContext, intent);
-                icon = null;
+    /** Extends the classification with the intents that can be resolved. */
+    private void addActions(
+            TextClassification.Builder builder, List<Intent> intents) {
+        final PackageManager pm = mContext.getPackageManager();
+        final int size = intents.size();
+        for (int i = 0; i < size; i++) {
+            final Intent intent = intents.get(i);
+            final ResolveInfo resolveInfo;
+            if (intent != null) {
+                resolveInfo = pm.resolveActivity(intent, 0);
             } else {
-                // A default activity will handle the intent.
-                intent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
-                icon = resolveInfo.activityInfo.loadIcon(pm);
-                if (icon == null) {
-                    icon = resolveInfo.loadIcon(pm);
+                resolveInfo = null;
+            }
+            if (resolveInfo != null && resolveInfo.activityInfo != null) {
+                final String packageName = resolveInfo.activityInfo.packageName;
+                CharSequence label;
+                Drawable icon;
+                if ("android".equals(packageName)) {
+                    // Requires the chooser to find an activity to handle the intent.
+                    label = IntentFactory.getLabel(mContext, intent);
+                    icon = null;
+                } else {
+                    // A default activity will handle the intent.
+                    intent.setComponent(
+                            new ComponentName(packageName, resolveInfo.activityInfo.name));
+                    icon = resolveInfo.activityInfo.loadIcon(pm);
+                    if (icon == null) {
+                        icon = resolveInfo.loadIcon(pm);
+                    }
+                    label = resolveInfo.activityInfo.loadLabel(pm);
+                    if (label == null) {
+                        label = resolveInfo.loadLabel(pm);
+                    }
                 }
-                label = resolveInfo.activityInfo.loadLabel(pm);
-                if (label == null) {
-                    label = resolveInfo.loadLabel(pm);
+                final String labelString = (label != null) ? label.toString() : null;
+                final OnClickListener onClickListener =
+                        TextClassification.createStartActivityOnClickListener(mContext, intent);
+                if (i == 0) {
+                    builder.setPrimaryAction(intent, labelString, icon, onClickListener);
+                } else {
+                    builder.addSecondaryAction(intent, labelString, icon, onClickListener);
                 }
             }
-            builder.addAction(
-                    intent, label != null ? label.toString() : null, icon,
-                    TextClassification.createStartActivityOnClickListener(mContext, intent));
         }
     }
 
@@ -557,22 +562,5 @@
                     return null;
             }
         }
-
-        @Nullable
-        public static int getLogType(String type) {
-            type = type.trim().toLowerCase(Locale.ENGLISH);
-            switch (type) {
-                case TextClassifier.TYPE_EMAIL:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_EMAIL;
-                case TextClassifier.TYPE_PHONE:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_PHONE;
-                case TextClassifier.TYPE_ADDRESS:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_ADDRESS;
-                case TextClassifier.TYPE_URL:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_URL;
-                default:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_OTHER;
-            }
-        }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index ced4018..25e9e7e 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -37,17 +37,15 @@
     private final int mStartIndex;
     private final int mEndIndex;
     @NonNull private final EntityConfidence<String> mEntityConfidence;
-    @NonNull private final String mLogSource;
-    @NonNull private final String mVersionInfo;
+    @NonNull private final String mSignature;
 
     private TextSelection(
-            int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence,
-            @NonNull String logSource, @NonNull String versionInfo) {
+            int startIndex, int endIndex, @NonNull Map<String, Float> entityConfidence,
+            @NonNull String signature) {
         mStartIndex = startIndex;
         mEndIndex = endIndex;
         mEntityConfidence = new EntityConfidence<>(entityConfidence);
-        mLogSource = logSource;
-        mVersionInfo = versionInfo;
+        mSignature = signature;
     }
 
     /**
@@ -95,27 +93,21 @@
     }
 
     /**
-     * Returns a tag for the source classifier used to generate this result.
-     * @hide
+     * Returns the signature for this object.
+     * The TextClassifier that generates this object may use it as a way to internally identify
+     * this object.
      */
     @NonNull
-    public String getSourceClassifier() {
-        return mLogSource;
-    }
-
-    /**
-     * Returns information about the classifier model used to generate this TextSelection.
-     * @hide
-     */
-    @NonNull
-    public String getVersionInfo() {
-        return mVersionInfo;
+    public String getSignature() {
+        return mSignature;
     }
 
     @Override
     public String toString() {
-        return String.format(Locale.US,
-                "TextSelection {%d, %d, %s}", mStartIndex, mEndIndex, mEntityConfidence);
+        return String.format(
+                Locale.US,
+                "TextSelection {startIndex=%d, endIndex=%d, entities=%s, signature=%s}",
+                mStartIndex, mEndIndex, mEntityConfidence, mSignature);
     }
 
     /**
@@ -126,8 +118,7 @@
         private final int mStartIndex;
         private final int mEndIndex;
         @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        @NonNull private String mLogSource = "";
-        @NonNull private String mVersionInfo = "";
+        @NonNull private String mSignature = "";
 
         /**
          * Creates a builder used to build {@link TextSelection} objects.
@@ -157,20 +148,13 @@
         }
 
         /**
-         * Sets a tag for the source classifier used to generate this result.
-         * @hide
+         * Sets a signature for the TextSelection object.
+         *
+         * The TextClassifier that generates the TextSelection object may use it as a way to
+         * internally identify the TextSelection object.
          */
-        Builder setLogSource(@NonNull String logSource) {
-            mLogSource = Preconditions.checkNotNull(logSource);
-            return this;
-        }
-
-        /**
-         * Sets information about the classifier model used to generate this TextSelection.
-         * @hide
-         */
-        Builder setVersionInfo(@NonNull String versionInfo) {
-            mVersionInfo = Preconditions.checkNotNull(versionInfo);
+        public Builder setSignature(@NonNull String signature) {
+            mSignature = Preconditions.checkNotNull(signature);
             return this;
         }
 
@@ -179,8 +163,7 @@
          */
         public TextSelection build() {
             return new TextSelection(
-                    mStartIndex, mEndIndex, new EntityConfidence<>(mEntityConfidence),  mLogSource,
-                    mVersionInfo);
+                    mStartIndex, mEndIndex, mEntityConfidence, mSignature);
         }
     }
 
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 2833564..157b3d8 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -473,7 +473,7 @@
             final String entityType = classification.getEntityCount() > 0
                     ? classification.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = classification.getVersionInfo();
+            final String versionTag = getVersionInfo(classification.getSignature());
             return new SelectionEvent(
                     start, end, EventType.SELECTION_MODIFIED, entityType, versionTag);
         }
@@ -489,7 +489,7 @@
          */
         public static SelectionEvent selectionModified(
                 int start, int end, @NonNull TextSelection selection) {
-            final boolean smartSelection = selection.getSourceClassifier()
+            final boolean smartSelection = getSourceClassifier(selection.getSignature())
                     .equals(TextClassifier.DEFAULT_LOG_TAG);
             final int eventType;
             if (smartSelection) {
@@ -503,7 +503,7 @@
             final String entityType = selection.getEntityCount() > 0
                     ? selection.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = selection.getVersionInfo();
+            final String versionTag = getVersionInfo(selection.getSignature());
             return new SelectionEvent(start, end, eventType, entityType, versionTag);
         }
 
@@ -538,26 +538,25 @@
             final String entityType = classification.getEntityCount() > 0
                     ? classification.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = classification.getVersionInfo();
+            final String versionTag = getVersionInfo(classification.getSignature());
             return new SelectionEvent(start, end, actionType, entityType, versionTag);
         }
 
-        private boolean isActionType() {
-            switch (mEventType) {
-                case ActionType.OVERTYPE:  // fall through
-                case ActionType.COPY:  // fall through
-                case ActionType.PASTE:  // fall through
-                case ActionType.CUT:  // fall through
-                case ActionType.SHARE:  // fall through
-                case ActionType.SMART_SHARE:  // fall through
-                case ActionType.DRAG:  // fall through
-                case ActionType.ABANDON:  // fall through
-                case ActionType.SELECT_ALL:  // fall through
-                case ActionType.RESET:  // fall through
-                    return true;
-                default:
-                    return false;
+        private static String getVersionInfo(String signature) {
+            final int start = signature.indexOf("|");
+            final int end = signature.indexOf("|", start);
+            if (start >= 0 && end >= start) {
+                return signature.substring(start, end);
             }
+            return "";
+        }
+
+        private static String getSourceClassifier(String signature) {
+            final int end = signature.indexOf("|");
+            if (end >= 0) {
+                return signature.substring(0, end);
+            }
+            return "";
         }
 
         private boolean isTerminal() {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 797bdfb..9db0e8d 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -265,10 +265,10 @@
                     + "packageName mismatch, expected: "
                     + chosen.packageName + " actual: " + toUse.packageName);
         }
-        if (chosen.versionCode > toUse.versionCode) {
+        if (chosen.getLongVersionCode() > toUse.getLongVersionCode()) {
             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
-                    + "version code is lower than expected: " + chosen.versionCode
-                    + " actual: " + toUse.versionCode);
+                    + "version code is lower than expected: " + chosen.getLongVersionCode()
+                    + " actual: " + toUse.getLongVersionCode());
         }
         if (getWebViewLibrary(toUse.applicationInfo) == null) {
             throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
@@ -401,7 +401,7 @@
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
             Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
-                    sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
+                    sPackageInfo.versionName + " (code " + sPackageInfo.getLongVersionCode() + ")");
 
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
             try {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 05cba1e..df97112 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -41,7 +41,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.metrics.LogMaker;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
@@ -3982,31 +3981,39 @@
             }
             final TextClassification textClassification =
                     getSelectionActionModeHelper().getTextClassification();
-            final int count = textClassification != null ? textClassification.getActionCount() : 0;
+            if (textClassification == null) {
+                return;
+            }
+            if (isValidAssistMenuItem(
+                    textClassification.getIcon(),
+                    textClassification.getLabel(),
+                    textClassification.getOnClickListener(),
+                    textClassification.getIntent())) {
+                final MenuItem item = menu.add(
+                        TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST,
+                        textClassification.getLabel())
+                        .setIcon(textClassification.getIcon())
+                        .setIntent(textClassification.getIntent());
+                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+                mAssistClickHandlers.put(item, textClassification.getOnClickListener());
+            }
+            final int count = textClassification.getSecondaryActionsCount();
             for (int i = 0; i < count; i++) {
-                if (!isValidAssistMenuItem(i)) {
+                if (!isValidAssistMenuItem(
+                        textClassification.getSecondaryIcon(i),
+                        textClassification.getSecondaryLabel(i),
+                        textClassification.getSecondaryOnClickListener(i),
+                        textClassification.getSecondaryIntent(i))) {
                     continue;
                 }
-                final int groupId = TextView.ID_ASSIST;
-                final int order = (i == 0)
-                        ? MENU_ITEM_ORDER_ASSIST
-                        : MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i;
-                final int id = (i == 0) ? TextView.ID_ASSIST : Menu.NONE;
-                final int showAsFlag = (i == 0)
-                        ? MenuItem.SHOW_AS_ACTION_ALWAYS
-                        : MenuItem.SHOW_AS_ACTION_NEVER;
+                final int order = MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i;
                 final MenuItem item = menu.add(
-                        groupId, id, order, textClassification.getLabel(i))
-                        .setIcon(textClassification.getIcon(i))
-                        .setIntent(textClassification.getIntent(i));
-                item.setShowAsAction(showAsFlag);
-                mAssistClickHandlers.put(item, textClassification.getOnClickListener(i));
-                if (id == TextView.ID_ASSIST) {
-                    mMetricsLogger.write(
-                            new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
-                                    .setType(MetricsEvent.TYPE_OPEN)
-                                    .setSubtype(textClassification.getLogType()));
-                }
+                        TextView.ID_ASSIST, Menu.NONE, order,
+                        textClassification.getSecondaryLabel(i))
+                        .setIcon(textClassification.getSecondaryIcon(i))
+                        .setIntent(textClassification.getSecondaryIntent(i));
+                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+                mAssistClickHandlers.put(item, textClassification.getSecondaryOnClickListener(i));
             }
         }
 
@@ -4022,18 +4029,9 @@
             }
         }
 
-        private boolean isValidAssistMenuItem(int index) {
-            final TextClassification textClassification =
-                    getSelectionActionModeHelper().getTextClassification();
-            if (!mTextView.isDeviceProvisioned() || textClassification == null
-                    || index < 0 || index >= textClassification.getActionCount()) {
-                return false;
-            }
-            final Drawable icon = textClassification.getIcon(index);
-            final CharSequence label = textClassification.getLabel(index);
+        private boolean isValidAssistMenuItem(
+                Drawable icon, CharSequence label, OnClickListener onClick, Intent intent) {
             final boolean hasUi = icon != null || !TextUtils.isEmpty(label);
-            final OnClickListener onClick = textClassification.getOnClickListener(index);
-            final Intent intent = textClassification.getIntent(index);
             final boolean hasAction = onClick != null || isSupportedIntent(intent);
             return hasUi && hasAction;
         }
@@ -4079,11 +4077,6 @@
             if (onClickListener != null) {
                 onClickListener.onClick(mTextView);
                 stopTextActionMode();
-                if (assistMenuItem.getItemId() == TextView.ID_ASSIST) {
-                    mMetricsLogger.action(
-                            MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST,
-                            textClassification.getLogType());
-                }
             }
             // We tried our best.
             return true;
@@ -6594,7 +6587,7 @@
                         Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
                         getLabel(resolveInfo))
                         .setIntent(createProcessTextIntentForResolveInfo(resolveInfo))
-                        .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+                        .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
             }
         }
 
diff --git a/core/java/android/widget/TextViewMetrics.java b/core/java/android/widget/TextViewMetrics.java
index 96d1794..738a574 100644
--- a/core/java/android/widget/TextViewMetrics.java
+++ b/core/java/android/widget/TextViewMetrics.java
@@ -37,29 +37,4 @@
      * Long press on TextView - drag and drop started.
      */
     public static final int SUBTYPE_LONG_PRESS_DRAG_AND_DROP = 2;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: other.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_OTHER = 0;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: email.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_EMAIL = 1;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: phone.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_PHONE = 2;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: address.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_ADDRESS = 3;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: url.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_URL = 4;
 }
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index fbdf17d..6fb02b1 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -28,6 +28,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -137,7 +138,7 @@
     private final String mName;
     private final String mPackage;
     private final int mUid;
-    private final int mVersion;
+    private final long mVersion;
     private final DurationsTable mDurations;
     private final PssTable mPssTable;
 
@@ -170,7 +171,7 @@
      * Create a new top-level process state, for the initial case where there is only
      * a single package running in a process.  The initial state is not running.
      */
-    public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) {
+    public ProcessState(ProcessStats processStats, String pkg, int uid, long vers, String name) {
         mStats = processStats;
         mName = name;
         mCommonProcess = this;
@@ -186,7 +187,7 @@
      * state.  The current running state of the top-level process is also copied,
      * marked as started running at 'now'.
      */
-    public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name,
+    public ProcessState(ProcessState commonProcess, String pkg, int uid, long vers, String name,
             long now) {
         mStats = commonProcess.mStats;
         mName = name;
@@ -238,7 +239,7 @@
         return mUid;
     }
 
-    public int getVersion() {
+    public long getVersion() {
         return mVersion;
     }
 
@@ -546,7 +547,7 @@
             // The array map is still pointing to a common process state
             // that is now shared across packages.  Update it to point to
             // the new per-package state.
-            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
+            LongSparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
             if (vpkg == null) {
                 throw new IllegalStateException("Didn't find package " + pkgName
                         + " / " + mUid);
@@ -584,7 +585,7 @@
             // The array map is still pointing to a common process state
             // that is now shared across packages.  Update it to point to
             // the new per-package state.
-            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
+            LongSparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
                     proc.mUid);
             if (vpkg == null) {
                 throw new IllegalStateException("No existing package "
@@ -1037,7 +1038,7 @@
         }
     }
 
-    public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, int vers,
+    public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, long vers,
             String itemName, long now) {
         pw.print("pkgproc,");
         pw.print(pkgName);
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 14f5e5b..2ce7936 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -28,6 +28,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -157,7 +158,7 @@
     };
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 21;
+    private static final int PARCEL_VERSION = 22;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -165,9 +166,8 @@
     public String mTimePeriodStartClockStr;
     public int mFlags;
 
-    public final ProcessMap<SparseArray<PackageState>> mPackages
-            = new ProcessMap<SparseArray<PackageState>>();
-    public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
+    public final ProcessMap<LongSparseArray<PackageState>> mPackages = new ProcessMap<>();
+    public final ProcessMap<ProcessState> mProcesses = new ProcessMap<>();
 
     public final long[] mMemFactorDurations = new long[ADJ_COUNT];
     public int mMemFactor = STATE_NOTHING;
@@ -218,15 +218,16 @@
     }
 
     public void add(ProcessStats other) {
-        ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap();
+        ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                other.mPackages.getMap();
         for (int ip=0; ip<pkgMap.size(); ip++) {
             final String pkgName = pkgMap.keyAt(ip);
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=0; iu<uids.size(); iu++) {
                 final int uid = uids.keyAt(iu);
-                final SparseArray<PackageState> versions = uids.valueAt(iu);
+                final LongSparseArray<PackageState> versions = uids.valueAt(iu);
                 for (int iv=0; iv<versions.size(); iv++) {
-                    final int vers = versions.keyAt(iv);
+                    final long vers = versions.keyAt(iv);
                     final PackageState otherState = versions.valueAt(iv);
                     final int NPROCS = otherState.mProcesses.size();
                     final int NSRVS = otherState.mServices.size();
@@ -269,7 +270,7 @@
                 ProcessState otherProc = uids.valueAt(iu);
                 final String name = otherProc.getName();
                 final String pkg = otherProc.getPackage();
-                final int vers = otherProc.getVersion();
+                final long vers = otherProc.getVersion();
                 ProcessState thisProc = mProcesses.get(name, uid);
                 if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + name);
                 if (thisProc == null) {
@@ -420,11 +421,12 @@
 
         // Next reset or prune all per-package processes, and for the ones that are reset
         // track this back to the common processes.
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         for (int ip=pkgMap.size()-1; ip>=0; ip--) {
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=uids.size()-1; iu>=0; iu--) {
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 for (int iv=vpkgs.size()-1; iv>=0; iv--) {
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
@@ -727,13 +729,14 @@
                 uids.valueAt(iu).commitStateTime(now);
             }
         }
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         final int NPKG = pkgMap.size();
         for (int ip=0; ip<NPKG; ip++) {
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             final int NUID = uids.size();
             for (int iu=0; iu<NUID; iu++) {
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 final int NVERS = vpkgs.size();
                 for (int iv=0; iv<NVERS; iv++) {
                     PackageState pkgState = vpkgs.valueAt(iv);
@@ -781,23 +784,23 @@
                 out.writeInt(uids.keyAt(iu));
                 final ProcessState proc = uids.valueAt(iu);
                 writeCommonString(out, proc.getPackage());
-                out.writeInt(proc.getVersion());
+                out.writeLong(proc.getVersion());
                 proc.writeToParcel(out, now);
             }
         }
         out.writeInt(NPKG);
         for (int ip=0; ip<NPKG; ip++) {
             writeCommonString(out, pkgMap.keyAt(ip));
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             final int NUID = uids.size();
             out.writeInt(NUID);
             for (int iu=0; iu<NUID; iu++) {
                 out.writeInt(uids.keyAt(iu));
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 final int NVERS = vpkgs.size();
                 out.writeInt(NVERS);
                 for (int iv=0; iv<NVERS; iv++) {
-                    out.writeInt(vpkgs.keyAt(iv));
+                    out.writeLong(vpkgs.keyAt(iv));
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     out.writeInt(NPROCS);
@@ -963,7 +966,7 @@
                     mReadError = "bad process package name";
                     return;
                 }
-                final int vers = in.readInt();
+                final long vers = in.readLong();
                 ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
                 if (proc != null) {
                     if (!proc.readFromParcel(in, false)) {
@@ -1014,11 +1017,11 @@
                 }
                 while (NVERS > 0) {
                     NVERS--;
-                    final int vers = in.readInt();
+                    final long vers = in.readLong();
                     PackageState pkgState = new PackageState(pkgName, uid);
-                    SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
+                    LongSparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
                     if (vpkg == null) {
-                        vpkg = new SparseArray<PackageState>();
+                        vpkg = new LongSparseArray<>();
                         mPackages.put(pkgName, uid, vpkg);
                     }
                     vpkg.put(vers, pkgState);
@@ -1117,10 +1120,10 @@
         if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!");
     }
 
-    public PackageState getPackageStateLocked(String packageName, int uid, int vers) {
-        SparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
+    public PackageState getPackageStateLocked(String packageName, int uid, long vers) {
+        LongSparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
         if (vpkg == null) {
-            vpkg = new SparseArray<PackageState>();
+            vpkg = new LongSparseArray<PackageState>();
             mPackages.put(packageName, uid, vpkg);
         }
         PackageState as = vpkg.get(vers);
@@ -1132,7 +1135,7 @@
         return as;
     }
 
-    public ProcessState getProcessStateLocked(String packageName, int uid, int vers,
+    public ProcessState getProcessStateLocked(String packageName, int uid, long vers,
             String processName) {
         final PackageState pkgState = getPackageStateLocked(packageName, uid, vers);
         ProcessState ps = pkgState.mProcesses.get(processName);
@@ -1202,7 +1205,7 @@
         return ps;
     }
 
-    public ServiceState getServiceStateLocked(String packageName, int uid, int vers,
+    public ServiceState getServiceStateLocked(String packageName, int uid, long vers,
             String processName, String className) {
         final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers);
         ServiceState ss = as.mServices.get(className);
@@ -1228,16 +1231,16 @@
             mSysMemUsage.dump(pw, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
             sepNeeded = true;
         }
-        ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = mPackages.getMap();
         boolean printedHeader = false;
         for (int ip=0; ip<pkgMap.size(); ip++) {
             final String pkgName = pkgMap.keyAt(ip);
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=0; iu<uids.size(); iu++) {
                 final int uid = uids.keyAt(iu);
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 for (int iv=0; iv<vpkgs.size(); iv++) {
-                    final int vers = vpkgs.keyAt(iv);
+                    final long vers = vpkgs.keyAt(iv);
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     final int NSRVS = pkgState.mServices.size();
@@ -1531,12 +1534,13 @@
             int[] procStates, int sortProcStates[], long now, String reqPackage,
             boolean activeOnly) {
         final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         for (int ip=0; ip<pkgMap.size(); ip++) {
             final String pkgName = pkgMap.keyAt(ip);
-            final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> procs = pkgMap.valueAt(ip);
             for (int iu=0; iu<procs.size(); iu++) {
-                final SparseArray<PackageState> vpkgs = procs.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = procs.valueAt(iu);
                 final int NVERS = vpkgs.size();
                 for (int iv=0; iv<NVERS; iv++) {
                     final PackageState state = vpkgs.valueAt(iv);
@@ -1571,7 +1575,8 @@
 
     public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
         final long now = SystemClock.uptimeMillis();
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         pw.println("vers,5");
         pw.print("period,"); pw.print(mTimePeriodStartClockStr);
         pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
@@ -1602,12 +1607,12 @@
             if (reqPackage != null && !reqPackage.equals(pkgName)) {
                 continue;
             }
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=0; iu<uids.size(); iu++) {
                 final int uid = uids.keyAt(iu);
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 for (int iv=0; iv<vpkgs.size(); iv++) {
-                    final int vers = vpkgs.keyAt(iv);
+                    final long vers = vpkgs.keyAt(iv);
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     final int NSRVS = pkgState.mServices.size();
@@ -1709,7 +1714,8 @@
     }
 
     public void toProto(ProtoOutputStream proto, long now) {
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
 
         proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
         proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
@@ -1750,10 +1756,10 @@
     }
 
     final public static class ProcessStateHolder {
-        public final int appVersion;
+        public final long appVersion;
         public ProcessState state;
 
-        public ProcessStateHolder(int _appVersion) {
+        public ProcessStateHolder(long _appVersion) {
             appVersion = _appVersion;
         }
     }
diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java
index 2e11c43..650de2ea 100644
--- a/core/java/com/android/internal/app/procstats/ServiceState.java
+++ b/core/java/com/android/internal/app/procstats/ServiceState.java
@@ -441,7 +441,7 @@
         return totalTime;
     }
 
-    public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers,
+    public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
             String serviceName, long now) {
         dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
                 ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
@@ -454,7 +454,7 @@
     }
 
     private void dumpTimeCheckin(PrintWriter pw, String label, String packageName,
-            int uid, int vers, String serviceName, int serviceType, int opCount,
+            int uid, long vers, String serviceName, int serviceType, int opCount,
             int curState, long curStartTime, long now) {
         if (opCount <= 0) {
             return;
diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags
index 93d5a03..a440ee4 100644
--- a/core/java/com/android/internal/logging/EventLogTags.logtags
+++ b/core/java/com/android/internal/logging/EventLogTags.logtags
@@ -8,3 +8,8 @@
 524292 sysui_multi_action (content|4)
 524290 sysui_count (name|3),(increment|1)
 524291 sysui_histogram (name|3),(bucket|1)
+
+# ---------------------------
+# LatencyTracker.java
+# ---------------------------
+36070 sysui_latency (action|1|6),(latency|1|3)
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3d49072..a050a3c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -120,7 +120,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 169 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 170 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -4501,11 +4501,12 @@
         }
     }
 
-    public void notePackageInstalledLocked(String pkgName, int versionCode) {
+    public void notePackageInstalledLocked(String pkgName, long versionCode) {
         final long elapsedRealtime = mClocks.elapsedRealtime();
         final long uptime = mClocks.uptimeMillis();
+        // XXX need to figure out what to do with long version codes.
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED,
-                pkgName, versionCode);
+                pkgName, (int)versionCode);
         PackageChange pc = new PackageChange();
         pc.mPackageName = pkgName;
         pc.mUpdate = true;
@@ -9283,7 +9284,7 @@
                     if (pc.mUpdate) {
                         out.startTag(null, "upd");
                         out.attribute(null, "pkg", pc.mPackageName);
-                        out.attribute(null, "ver", Integer.toString(pc.mVersionCode));
+                        out.attribute(null, "ver", Long.toString(pc.mVersionCode));
                         out.endTag(null, "upd");
                     } else {
                         out.startTag(null, "rem");
@@ -9412,7 +9413,7 @@
                 pc.mUpdate = true;
                 pc.mPackageName = parser.getAttributeValue(null, "pkg");
                 String verStr = parser.getAttributeValue(null, "ver");
-                pc.mVersionCode = verStr != null ? Integer.parseInt(verStr) : 0;
+                pc.mVersionCode = verStr != null ? Long.parseLong(verStr) : 0;
                 dit.mPackageChanges.add(pc);
                 XmlUtils.skipCurrentTag(parser);
             } else if (tagName.equals("rem")) {
@@ -12113,7 +12114,7 @@
                 PackageChange pc = new PackageChange();
                 pc.mPackageName = in.readString();
                 pc.mUpdate = in.readInt() != 0;
-                pc.mVersionCode = in.readInt();
+                pc.mVersionCode = in.readLong();
                 mDailyPackageChanges.add(pc);
             }
         } else {
@@ -12538,7 +12539,7 @@
                 PackageChange pc = mDailyPackageChanges.get(i);
                 out.writeString(pc.mPackageName);
                 out.writeInt(pc.mUpdate ? 1 : 0);
-                out.writeInt(pc.mVersionCode);
+                out.writeLong(pc.mVersionCode);
             }
         } else {
             out.writeInt(0);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 22bfcc3..9336ddd 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -425,14 +425,17 @@
      * Adds value to given array if not already present, providing set-like
      * behavior.
      */
-    public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
+    public static @NonNull long[] appendLong(@Nullable long[] cur, long val,
+            boolean allowDuplicates) {
         if (cur == null) {
             return new long[] { val };
         }
         final int N = cur.length;
-        for (int i = 0; i < N; i++) {
-            if (cur[i] == val) {
-                return cur;
+        if (!allowDuplicates) {
+            for (int i = 0; i < N; i++) {
+                if (cur[i] == val) {
+                    return cur;
+                }
             }
         }
         long[] ret = new long[N + 1];
@@ -442,6 +445,14 @@
     }
 
     /**
+     * Adds value to given array if not already present, providing set-like
+     * behavior.
+     */
+    public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
+        return appendLong(cur, val, false);
+    }
+
+    /**
      * Removes value from given array if present, providing set-like behavior.
      */
     public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
similarity index 86%
rename from packages/SystemUI/src/com/android/keyguard/LatencyTracker.java
rename to core/java/com/android/internal/util/LatencyTracker.java
index cee0afc..72cd248 100644
--- a/packages/SystemUI/src/com/android/keyguard/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -1,20 +1,18 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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.keyguard;
+package com.android.internal.util;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -28,7 +26,7 @@
 import android.util.Log;
 import android.util.SparseLongArray;
 
-import com.android.systemui.EventLogTags;
+import com.android.internal.logging.EventLogTags;
 
 /**
  * Class to track various latencies in SystemUI. It then outputs the latency to logcat so these
@@ -76,13 +74,19 @@
      */
     public static final int ACTION_TURN_ON_SCREEN = 5;
 
+    /**
+     * Time it takes to rotate the screen.
+     */
+    public static final int ACTION_ROTATE_SCREEN = 6;
+
     private static final String[] NAMES = new String[] {
             "expand panel",
             "toggle recents",
             "fingerprint wake-and-unlock",
             "check credential",
             "check credential unlocked",
-            "turn on screen" };
+            "turn on screen",
+            "rotate the screen"};
 
     private static LatencyTracker sLatencyTracker;
 
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 408a4e9..7abc76a 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -9556,7 +9556,7 @@
             if (vScroll == 0 && hScroll == 0) {
                 return false;
             }
-            mRecyclerView.smoothScrollBy(hScroll, vScroll);
+            mRecyclerView.scrollBy(hScroll, vScroll);
             return true;
         }
 
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 17c7ebd..7635a72 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -81,6 +81,7 @@
     private int mCollapsibleHeightReserved;
 
     private int mTopOffset;
+    private boolean mShowAtTop;
 
     private boolean mIsDragging;
     private boolean mOpenOnClick;
@@ -134,6 +135,7 @@
         mMaxCollapsedHeightSmall = a.getDimensionPixelSize(
                 R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall,
                 mMaxCollapsedHeight);
+        mShowAtTop = a.getBoolean(R.styleable.ResolverDrawerLayout_showAtTop, false);
         a.recycle();
 
         mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
@@ -162,6 +164,16 @@
         return mCollapseOffset > 0;
     }
 
+    public void setShowAtTop(boolean showOnTop) {
+        mShowAtTop = showOnTop;
+        invalidate();
+        requestLayout();
+    }
+
+    public boolean getShowAtTop() {
+        return mShowAtTop;
+    }
+
     public void setCollapsed(boolean collapsed) {
         if (!isLaidOut()) {
             mOpenOnLayout = collapsed;
@@ -206,6 +218,12 @@
             return false;
         }
 
+        if (getShowAtTop()) {
+            // Keep the drawer fully open.
+            mCollapseOffset = 0;
+            return false;
+        }
+
         if (isLaidOut()) {
             final boolean isCollapsedOld = mCollapseOffset != 0;
             if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight
@@ -372,14 +390,23 @@
                 mVelocityTracker.computeCurrentVelocity(1000);
                 final float yvel = mVelocityTracker.getYVelocity(mActivePointerId);
                 if (Math.abs(yvel) > mMinFlingVelocity) {
-                    if (isDismissable()
-                            && yvel > 0 && mCollapseOffset > mCollapsibleHeight) {
-                        smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel);
-                        mDismissOnScrollerFinished = true;
+                    if (getShowAtTop()) {
+                        if (isDismissable() && yvel < 0) {
+                            abortAnimation();
+                            dismiss();
+                        } else {
+                            smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
+                        }
                     } else {
-                        smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
+                        if (isDismissable()
+                                && yvel > 0 && mCollapseOffset > mCollapsibleHeight) {
+                            smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel);
+                            mDismissOnScrollerFinished = true;
+                        } else {
+                            smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
+                        }
                     }
-                } else {
+                }else {
                     smoothScrollTo(
                             mCollapseOffset < mCollapsibleHeight / 2 ? 0 : mCollapsibleHeight, 0);
                 }
@@ -421,6 +448,11 @@
         mVelocityTracker.clear();
     }
 
+    private void dismiss() {
+        mRunOnDismissedListener = new RunOnDismissedListener();
+        post(mRunOnDismissedListener);
+    }
+
     @Override
     public void computeScroll() {
         super.computeScroll();
@@ -430,8 +462,7 @@
             if (keepGoing) {
                 postInvalidateOnAnimation();
             } else if (mDismissOnScrollerFinished && mOnDismissedListener != null) {
-                mRunOnDismissedListener = new RunOnDismissedListener();
-                post(mRunOnDismissedListener);
+                dismiss();
             }
         }
     }
@@ -443,6 +474,10 @@
     }
 
     private float performDrag(float dy) {
+        if (getShowAtTop()) {
+            return 0;
+        }
+
         final float newPos = Math.max(0, Math.min(mCollapseOffset + dy,
                 mCollapsibleHeight + mUncollapsibleHeight));
         if (newPos != mCollapseOffset) {
@@ -656,7 +691,7 @@
 
     @Override
     public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
-        if (velocityY > mMinFlingVelocity && mCollapseOffset != 0) {
+        if (!getShowAtTop() && velocityY > mMinFlingVelocity && mCollapseOffset != 0) {
             smoothScrollTo(0, velocityY);
             return true;
         }
@@ -666,12 +701,21 @@
     @Override
     public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
         if (!consumed && Math.abs(velocityY) > mMinFlingVelocity) {
-            if (isDismissable()
-                    && velocityY < 0 && mCollapseOffset > mCollapsibleHeight) {
-                smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, velocityY);
-                mDismissOnScrollerFinished = true;
+            if (getShowAtTop()) {
+                if (isDismissable() && velocityY > 0) {
+                    abortAnimation();
+                    dismiss();
+                } else {
+                    smoothScrollTo(velocityY < 0 ? mCollapsibleHeight : 0, velocityY);
+                }
             } else {
-                smoothScrollTo(velocityY > 0 ? 0 : mCollapsibleHeight, velocityY);
+                if (isDismissable()
+                        && velocityY < 0 && mCollapseOffset > mCollapsibleHeight) {
+                    smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, velocityY);
+                    mDismissOnScrollerFinished = true;
+                } else {
+                    smoothScrollTo(velocityY > 0 ? 0 : mCollapsibleHeight, velocityY);
+                }
             }
             return true;
         }
@@ -794,7 +838,11 @@
 
         updateCollapseOffset(oldCollapsibleHeight, !isDragging());
 
-        mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset;
+        if (getShowAtTop()) {
+            mTopOffset = 0;
+        } else {
+            mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset;
+        }
 
         setMeasuredDimension(sourceWidth, heightSize);
     }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index b5031f2..b7a6719 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -146,6 +146,9 @@
     final ArrayMap<String, ArraySet<String>> mPrivAppPermissions = new ArrayMap<>();
     final ArrayMap<String, ArraySet<String>> mPrivAppDenyPermissions = new ArrayMap<>();
 
+    final ArrayMap<String, ArraySet<String>> mVendorPrivAppPermissions = new ArrayMap<>();
+    final ArrayMap<String, ArraySet<String>> mVendorPrivAppDenyPermissions = new ArrayMap<>();
+
     final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>();
 
     public static SystemConfig getInstance() {
@@ -229,6 +232,14 @@
         return mPrivAppDenyPermissions.get(packageName);
     }
 
+    public ArraySet<String> getVendorPrivAppPermissions(String packageName) {
+        return mVendorPrivAppPermissions.get(packageName);
+    }
+
+    public ArraySet<String> getVendorPrivAppDenyPermissions(String packageName) {
+        return mVendorPrivAppDenyPermissions.get(packageName);
+    }
+
     public Map<String, Boolean> getOemPermissions(String packageName) {
         final Map<String, Boolean> oemPermissions = mOemPermissions.get(packageName);
         if (oemPermissions != null) {
@@ -248,7 +259,7 @@
 
         // Allow Vendor to customize system configs around libs, features, permissions and apps
         int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
-                ALLOW_APP_CONFIGS;
+                ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
         readPermissions(Environment.buildPath(
                 Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
         readPermissions(Environment.buildPath(
@@ -587,7 +598,19 @@
                     }
                     XmlUtils.skipCurrentTag(parser);
                 } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
-                    readPrivAppPermissions(parser);
+                    // privapp permissions from system and vendor partitions are stored
+                    // separately. This is to prevent xml files in the vendor partition from
+                    // granting permissions to priv apps in the system partition and vice
+                    // versa.
+                    boolean vendor = permFile.toPath().startsWith(
+                            Environment.getVendorDirectory().toPath());
+                    if (vendor) {
+                        readPrivAppPermissions(parser, mVendorPrivAppPermissions,
+                                mVendorPrivAppDenyPermissions);
+                    } else {
+                        readPrivAppPermissions(parser, mPrivAppPermissions,
+                                mPrivAppDenyPermissions);
+                    }
                 } else if ("oem-permissions".equals(name) && allowOemPermissions) {
                     readOemPermissions(parser);
                 } else {
@@ -674,7 +697,10 @@
         }
     }
 
-    void readPrivAppPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
+    private void readPrivAppPermissions(XmlPullParser parser,
+            ArrayMap<String, ArraySet<String>> grantMap,
+            ArrayMap<String, ArraySet<String>> denyMap)
+            throws IOException, XmlPullParserException {
         String packageName = parser.getAttributeValue(null, "package");
         if (TextUtils.isEmpty(packageName)) {
             Slog.w(TAG, "package is required for <privapp-permissions> in "
@@ -682,11 +708,11 @@
             return;
         }
 
-        ArraySet<String> permissions = mPrivAppPermissions.get(packageName);
+        ArraySet<String> permissions = grantMap.get(packageName);
         if (permissions == null) {
             permissions = new ArraySet<>();
         }
-        ArraySet<String> denyPermissions = mPrivAppDenyPermissions.get(packageName);
+        ArraySet<String> denyPermissions = denyMap.get(packageName);
         int depth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, depth)) {
             String name = parser.getName();
@@ -711,9 +737,9 @@
                 denyPermissions.add(permName);
             }
         }
-        mPrivAppPermissions.put(packageName, permissions);
+        grantMap.put(packageName, permissions);
         if (denyPermissions != null) {
-            mPrivAppDenyPermissions.put(packageName, denyPermissions);
+            denyMap.put(packageName, denyPermissions);
         }
     }
 
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 31567f7..5eecd9c 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -26,12 +26,13 @@
 bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
         int height, int* offsets, int jpegQuality) {
     jpeg_compress_struct    cinfo;
-    skjpeg_error_mgr        sk_err;
+    jpeg_error_mgr          err;
     skjpeg_destination_mgr  sk_wstream(stream);
 
-    cinfo.err = jpeg_std_error(&sk_err);
-    sk_err.error_exit = skjpeg_error_exit;
-    if (setjmp(sk_err.fJmpBuf)) {
+    cinfo.err = jpeg_std_error(&err);
+    err.error_exit = skjpeg_error_exit;
+    jmp_buf jmp;
+    if (setjmp(jmp)) {
         return false;
     }
     jpeg_create_compress(&cinfo);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3ad4da6..421e0de 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -517,23 +517,7 @@
         jobject graphicBuffer) {
     Surface* surface = reinterpret_cast<Surface*>(nativeObject);
     sp<GraphicBuffer> bp = graphicBufferForJavaObject(env, graphicBuffer);
-    if (bp == nullptr) {
-        return BAD_VALUE;
-    }
-    int err = ((ANativeWindow*)surface)->perform(surface, NATIVE_WINDOW_API_CONNECT,
-            NATIVE_WINDOW_API_CPU);
-    if (err != OK) {
-        return err;
-    }
-    err = surface->attachBuffer(bp->getNativeBuffer());
-    if (err != OK) {
-        return err;
-    }
-    err = ((ANativeWindow*)surface)->queueBuffer(surface, bp->getNativeBuffer(), -1);
-    if (err != OK) {
-        return err;
-    }
-    err = surface->disconnect(NATIVE_WINDOW_API_CPU);
+    int err = Surface::attachAndQueueBuffer(surface, bp);
     return err;
 }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f77e6c4..5e5d59b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -167,7 +167,7 @@
         maxLayer = INT32_MAX;
     }
     sp<GraphicBuffer> buffer;
-    status_t res = ScreenshotClient::captureToBuffer(displayToken,
+    status_t res = ScreenshotClient::capture(displayToken,
             sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform,
             rotation, &buffer);
     if (res != NO_ERROR) {
@@ -201,15 +201,18 @@
         maxLayer = INT32_MAX;
     }
 
-    res = screenshot->update(displayToken, sourceCrop, width, height,
-        minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation));
+    sp<GraphicBuffer> buffer;
+    res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
+        minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
 
     SkColorType colorType;
     SkAlphaType alphaType;
-    switch (screenshot->getFormat()) {
+
+    PixelFormat format = buffer->getPixelFormat();
+    switch (format) {
         case PIXEL_FORMAT_RGBX_8888: {
             colorType = kRGBA_8888_SkColorType;
             alphaType = kOpaque_SkAlphaType;
@@ -235,66 +238,20 @@
         }
     }
 
-    sk_sp<SkColorSpace> colorSpace;
-    if (screenshot->getDataSpace() == HAL_DATASPACE_DISPLAY_P3) {
-        colorSpace = SkColorSpace::MakeRGB(
-                SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
-    } else {
-        colorSpace = SkColorSpace::MakeSRGB();
-    }
+    SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
+                                         colorType, alphaType,
+                                         SkColorSpace::MakeSRGB());
 
-    SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(),
-                                                   screenshot->getHeight(),
-                                                   colorType,
-                                                   alphaType,
-                                                   colorSpace);
-
-    const size_t rowBytes =
-            screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
-
-    if (!screenshotInfo.width() || !screenshotInfo.height()) {
-        return NULL;
-    }
-
-    auto bitmap = new Bitmap(
-            (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
-            screenshotInfo, rowBytes);
-    screenshot.release();
-    bitmap->setImmutable();
-    return bitmap::createBitmap(env, bitmap,
-            android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
+    auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info));
+    return bitmap::createBitmap(env, bitmap.release(),
+                                android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
 }
 
 static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
         jobject surfaceObj, jobject sourceCropObj, jint width, jint height,
         jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
-    if (displayToken != NULL) {
-        sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
-        if (consumer != NULL) {
-            int left = env->GetIntField(sourceCropObj, gRectClassInfo.left);
-            int top = env->GetIntField(sourceCropObj, gRectClassInfo.top);
-            int right = env->GetIntField(sourceCropObj, gRectClassInfo.right);
-            int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
-            Rect sourceCrop(left, top, right, bottom);
-
-            if (allLayers) {
-                minLayer = INT32_MIN;
-                maxLayer = INT32_MAX;
-            }
-            ScreenshotClient::capture(displayToken,
-                    consumer->getIGraphicBufferProducer(), sourceCrop,
-                    width, height, minLayer, maxLayer,
-                    useIdentityTransform);
-        }
-    }
-}
-
-static void nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
-        jobject surfaceObj, jobject sourceCropObj, jfloat frameScale) {
-
-    sp<IBinder> layerHandle = ibinderForJavaObject(env, layerHandleToken);
-    if (layerHandle == NULL) {
+    if (displayToken == NULL) {
         return;
     }
 
@@ -308,11 +265,19 @@
         sourceCrop = rectFromObj(env, sourceCropObj);
     }
 
-    ScreenshotClient::captureLayers(layerHandle, consumer->getIGraphicBufferProducer(), sourceCrop,
-            frameScale);
+    if (allLayers) {
+        minLayer = INT32_MIN;
+        maxLayer = INT32_MAX;
+    }
+
+    sp<GraphicBuffer> buffer;
+    ScreenshotClient::capture(displayToken, sourceCrop, width, height, minLayer, maxLayer,
+                              useIdentityTransform, 0, &buffer);
+
+    Surface::attachAndQueueBuffer(consumer.get(), buffer);
 }
 
-static jobject nativeCaptureLayersToBuffer(JNIEnv* env, jclass clazz, jobject layerHandleToken,
+static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
         jobject sourceCropObj, jfloat frameScale) {
 
     sp<IBinder> layerHandle = ibinderForJavaObject(env, layerHandleToken);
@@ -326,8 +291,7 @@
     }
 
     sp<GraphicBuffer> buffer;
-    status_t res = ScreenshotClient::captureLayersToBuffer(layerHandle, sourceCrop, frameScale,
-            &buffer);
+    status_t res = ScreenshotClient::captureLayers(layerHandle, sourceCrop, frameScale, &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
@@ -904,11 +868,40 @@
             capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance());
 }
 
+static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    if (parcel == NULL) {
+        doThrowNPE(env);
+        return 0;
+    }
+    sp<SurfaceControl> surface = SurfaceControl::readFromParcel(parcel);
+    if (surface == nullptr) {
+        return 0;
+    }
+    surface->incStrong((void *)nativeCreate);
+    return reinterpret_cast<jlong>(surface.get());
+}
+
+static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
+        jlong nativeObject, jobject parcelObj) {
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    if (parcel == NULL) {
+        doThrowNPE(env);
+        return;
+    }
+    SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject);
+    self->writeToParcel(parcel);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod sSurfaceControlMethods[] = {
     {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJII)J",
             (void*)nativeCreate },
+    {"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
+            (void*)nativeReadFromParcel },
+    {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
+            (void*)nativeWriteToParcel },
     {"nativeRelease", "(J)V",
             (void*)nativeRelease },
     {"nativeDestroy", "(J)V",
@@ -1010,10 +1003,8 @@
     {"nativeScreenshotToBuffer",
      "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;",
      (void*)nativeScreenshotToBuffer },
-    {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;F)V",
-            (void*)nativeCaptureLayers },
     {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
-            (void*)nativeCaptureLayersToBuffer },
+            (void*)nativeCaptureLayers },
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 870a0c2..519a885 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1043,7 +1043,22 @@
     { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText },
 };
 
+static JavaVM* mJvm = nullptr;
+
+static void attachRenderThreadToJvm() {
+    LOG_ALWAYS_FATAL_IF(!mJvm, "No jvm but we set the hook??");
+
+    JavaVMAttachArgs args;
+    args.version = JNI_VERSION_1_4;
+    args.name = (char*) "RenderThread";
+    args.group = NULL;
+    JNIEnv* env;
+    mJvm->AttachCurrentThreadAsDaemon(&env, (void*) &args);
+}
+
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
+    env->GetJavaVM(&mJvm);
+    RenderThread::setOnStartHook(&attachRenderThreadToJvm);
     jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver");
     gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie(
             env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;");
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 73a9fbb..8ed958d 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -18,8 +18,6 @@
 option java_multiple_files = true;
 option java_outer_classname = "IncidentProtoMetadata";
 
-import "frameworks/base/libs/incident/proto/android/privacy.proto";
-import "frameworks/base/libs/incident/proto/android/section.proto";
 import "frameworks/base/core/proto/android/os/cpufreq.proto";
 import "frameworks/base/core/proto/android/os/cpuinfo.proto";
 import "frameworks/base/core/proto/android/os/incidentheader.proto";
@@ -42,6 +40,8 @@
 import "frameworks/base/core/proto/android/service/package.proto";
 import "frameworks/base/core/proto/android/service/print.proto";
 import "frameworks/base/core/proto/android/service/procstats.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+import "frameworks/base/libs/incident/proto/android/section.proto";
 
 package android.os;
 
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 9945f2e..f684c84 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -18,12 +18,15 @@
 option java_multiple_files = true;
 option java_outer_classname = "ProcrankProto";
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/tools/streaming_proto/stream.proto";
 
 package android.os;
 
 //Memory usage of running processes
 message Procrank {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Currently running process
     repeated ProcessProto processes = 1;
 
@@ -34,6 +37,7 @@
 // Next Tag: 11
 message ProcessProto {
     option (stream_proto.stream_msg).enable_fields_mapping = true;
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     // ID of the process
     optional int32 pid = 1;
@@ -68,6 +72,8 @@
 
 // Next Tag: 3
 message SummaryProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional ProcessProto total = 1;
 
     optional ZramProto zram = 2;
@@ -77,9 +83,13 @@
 
 // TODO: sync on how to use these values
 message ZramProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string raw_text = 1;
 }
 
 message RamProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string raw_text = 1;
 }
diff --git a/core/res/Android.bp b/core/res/Android.bp
new file mode 100644
index 0000000..e66f1a2
--- /dev/null
+++ b/core/res/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_app {
+    name: "framework-res",
+    no_framework_libs: true,
+    certificate: "platform",
+
+    // Soong special-cases framework-res to install this alongside
+    // the libraries at /system/framework/framework-res.apk.
+
+    // Generate private symbols into the com.android.internal.R class
+    // so they are not accessible to 3rd party apps.
+    aaptflags: [
+        "--private-symbols",
+        "com.android.internal",
+
+        // Framework doesn't need versioning since it IS the platform.
+        "--no-auto-version",
+
+        // Allow overlay to add resource
+        "--auto-add-overlay",
+    ],
+
+    // Create package-export.apk, which other packages can use to get
+    // PRODUCT-agnostic resource data like IDs and type definitions.
+    export_package_resources: true,
+}
diff --git a/core/res/Android.mk b/core/res/Android.mk
deleted file mode 100644
index 370a01f..0000000
--- a/core/res/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_USE_AAPT2 := true
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_PACKAGE_NAME := framework-res
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := optional
-
-# Generate private symbols into the com.android.internal.R class
-# so they are not accessible to 3rd party apps.
-LOCAL_AAPT_FLAGS += --private-symbols com.android.internal
-
-# Framework doesn't need versioning since it IS the platform.
-LOCAL_AAPT_FLAGS += --no-auto-version
-
-# Allow overlay to add resource
-LOCAL_AAPT_FLAGS += --auto-add-overlay
-
-# Install this alongside the libraries.
-LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
-
-# Create package-export.apk, which other packages can use to get
-# PRODUCT-agnostic resource data like IDs and type definitions.
-LOCAL_EXPORT_PACKAGE_RESOURCES := true
-
-include $(BUILD_PACKAGE)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5906b27..0920426 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1740,7 +1740,7 @@
          @hide
     -->
     <permission android:name="android.permission.BIND_IMS_SERVICE"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|vendorPrivileged" />
 
     <!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
          EuiccManager APIs.
@@ -3616,6 +3616,11 @@
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_RUNTIME_PROFILES"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/drawable/ic_logout.xml b/core/res/res/drawable/ic_logout.xml
new file mode 100644
index 0000000..c016ec8
--- /dev/null
+++ b/core/res/res/drawable/ic_logout.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <group>
+        <clip-path android:pathData="M0,0h24v24H0V0z M 0,0" />
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.0,7.0l-1.4099998,1.4099998l2.58,2.5900002l-10.17,0.0l0.0,2.0l10.17,0.0l-2.58,2.58l1.4099998,1.4200001l5.0,-5.0z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,5h8V3H4C2.9,3,2,3.9,2,5v14c0,1.1,0.9,2,2,2h8v-2H4V5z"/>
+    </group>
+</vector>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index da8a9cc..3b49d9d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8538,6 +8538,8 @@
         <attr name="maxWidth" />
         <attr name="maxCollapsedHeight" format="dimension" />
         <attr name="maxCollapsedHeightSmall" format="dimension" />
+        <!-- Whether the Drawer should be positioned at the top rather than at the bottom. -->
+        <attr name="showAtTop" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="MessagingLinearLayout">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 4d410e5..e87b9d8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -262,6 +262,9 @@
              and the OEM has white-listed the app to receive this permission by the OEM.
          -->
         <flag name="oem" value="0x4000" />
+        <!-- Additional flag from base permission type: this permission can be granted to
+             privileged apps in vendor partition. -->
+        <flag name="vendorPrivileged" value="0x8000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
@@ -317,6 +320,13 @@
          as multiple split APKs, each APK must have the exact same versionCode. -->
     <attr name="versionCode" format="integer" />
 
+    <!-- Internal major version code.  This is essentially additional high bits
+         for the base version code; it has no other meaning than
+         that higher numbers are more recent.  This is not a version
+         number generally shown to the user, that is usually supplied
+         with {@link android.R.attr#versionName}. -->
+    <attr name="versionCodeMajor" format="integer" />
+
     <!-- Internal revision code.  This number is the number used to determine
          whether one APK is more recent than another: it has no other meaning
          than that higher numbers are more recent.  This value is only meaningful
@@ -1386,6 +1396,7 @@
          {@link #AndroidManifestUsesFeature uses-feature}.  -->
     <declare-styleable name="AndroidManifest">
         <attr name="versionCode" />
+        <attr name="versionCodeMajor" />
         <attr name="versionName" />
         <attr name="revisionCode" />
         <attr name="sharedUserId" />
@@ -1784,6 +1795,10 @@
         <attr name="name" />
         <!-- Required specific library version. -->
         <attr name="version" />
+        <!-- Required specific library major version code.  This matches
+             android:versionCodeMajor of the library. -->
+        <!-- Required specific library version. -->
+        <attr name="versionMajor" format="integer" />
     </declare-styleable>
 
     <!-- The <code>uses-libraries</code> specifies a shared library that this
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc64918..0b38d1b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2441,10 +2441,12 @@
          "users" = list of users
          "restart" = restart device
          "lockdown" = Lock down device until the user authenticates
+         "logout" =  Logout the current user
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
         <item>restart</item>
+        <item>logout</item>
         <item>bugreport</item>
         <item>users</item>
         <item>lockdown</item>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3dbb89c..43e1f73 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2854,6 +2854,8 @@
       <public name="compileSdkVersionCodename" />
       <public name="screenReaderFocusable" />
       <public name="buttonCornerRadius" />
+      <public name="versionCodeMajor" />
+      <public name="versionMajor" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 93bd4ec..8958af4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -471,6 +471,9 @@
     <!-- label for item that generates a bug report in the phone options dialog -->
     <string name="global_action_bug_report">Bug report</string>
 
+    <!-- label for item that logouts the current user -->
+    <string name="global_action_logout">End session</string>
+
     <!-- Take bug report menu title [CHAR LIMIT=NONE] -->
     <string name="bugreport_title">Take bug report</string>
     <!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d944e4b..cbd34f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2932,8 +2932,8 @@
   <!-- com.android.server.autofill -->
   <java-symbol type="layout" name="autofill_save"/>
   <java-symbol type="layout" name="autofill_dataset_picker"/>
-  <java-symbol type="id" name="autofill_dataset_picker"/>
   <java-symbol type="id" name="autofill_dataset_list"/>
+  <java-symbol type="id" name="autofill_dataset_picker"/>
   <java-symbol type="id" name="autofill" />
   <java-symbol type="id" name="autofill_save_custom_subtitle" />
   <java-symbol type="id" name="autofill_save_icon" />
@@ -3163,4 +3163,7 @@
   <java-symbol type="string" name="unsupported_compile_sdk_check_update" />
 
   <java-symbol type="string" name="battery_saver_warning_title" />
+
+  <java-symbol type="string" name="global_action_logout" />
+  <java-symbol type="drawable" name="ic_logout" />
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 4e0e5ed..0367275 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -418,7 +418,13 @@
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                 // TODO(b/67867469): Move autofill settings below to
+                 // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
                  Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
+                 Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
                  Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
                  Settings.Secure.ALWAYS_ON_VPN_APP,
                  Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index d31da71..7875c17 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -204,7 +204,7 @@
         int lineCount = layout.getLineCount();
         boolean hyphenationHappend = false;
         for (int i = 0; i < lineCount; ++i) {
-            if (layout.getHyphen(i) != 1) {
+            if (layout.getHyphen(i) == 0) {
                 continue;  // Hyphantion does not happen.
             }
             hyphenationHappend = true;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 55e804b..9fcb06e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.widget.ResolverDrawerLayout;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -33,6 +34,8 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.RelativeLayout;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -100,6 +103,34 @@
     }
 
     @Test
+    public void setShowAtTopToTrue() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        waitForIdle();
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        final View resolverList = activity.findViewById(R.id.resolver_list);
+        final RelativeLayout profileView =
+            (RelativeLayout) activity.findViewById(R.id.profile_button).getParent();
+        assertThat("Drawer should show at bottom by default",
+                profileView.getBottom() == resolverList.getTop() && profileView.getTop() > 0);
+
+        activity.runOnUiThread(() -> {
+            ResolverDrawerLayout layout = (ResolverDrawerLayout)
+                    activity.findViewById(
+                            R.id.contentPanel);
+            layout.setShowAtTop(true);
+        });
+        waitForIdle();
+        assertThat("Drawer should show at top with new attribute",
+            profileView.getBottom() == resolverList.getTop() && profileView.getTop() == 0);
+    }
+
+    @Test
     public void hasLastChosenActivity() throws Exception {
         Intent sendIntent = createSendImageIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -283,4 +314,4 @@
     private void waitForIdle() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
-}
\ No newline at end of file
+}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 94a05b2..415d3e3 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -299,10 +299,9 @@
 
   const PackageGroup& package_group = package_groups_[idx];
   const size_t package_count = package_group.packages_.size();
+  FindEntryResult current_entry;
   for (size_t i = 0; i < package_count; i++) {
     const LoadedPackage* loaded_package = package_group.packages_[i];
-
-    FindEntryResult current_entry;
     if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, &current_entry)) {
       continue;
     }
@@ -394,7 +393,7 @@
     return kInvalidCookie;
   }
 
-  if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+  if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
     if (!may_be_bag) {
       LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
       return kInvalidCookie;
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c361ea2..28548e2 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -18,6 +18,7 @@
 
 #include "androidfw/LoadedArsc.h"
 
+#include <algorithm>
 #include <cstddef>
 #include <limits>
 
@@ -128,9 +129,14 @@
 // Precondition: The header passed in has already been verified, so reading any fields and trusting
 // the ResChunk_header is safe.
 static bool VerifyResTableType(const ResTable_type* header) {
+  if (header->id == 0) {
+    LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0.";
+    return false;
+  }
+
   const size_t entry_count = dtohl(header->entryCount);
   if (entry_count > std::numeric_limits<uint16_t>::max()) {
-    LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE.";
+    LOG(ERROR) << "RES_TABLE_TYPE_TYPE has too many entries (" << entry_count << ").";
     return false;
   }
 
@@ -140,17 +146,17 @@
   const size_t offsets_length = sizeof(uint32_t) * entry_count;
 
   if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
-    LOG(ERROR) << "Entry offsets overlap actual entry data.";
+    LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data.";
     return false;
   }
 
   if (entries_offset > dtohl(header->header.size)) {
-    LOG(ERROR) << "Entry offsets extend beyond chunk.";
+    LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets extend beyond chunk.";
     return false;
   }
 
   if (entries_offset & 0x03) {
-    LOG(ERROR) << "Entries start at unaligned address.";
+    LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address.";
     return false;
   }
   return true;
@@ -235,7 +241,6 @@
   return true;
 }
 
-template <bool Verified>
 bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx,
                               const ResTable_config& config, FindEntryResult* out_entry) const {
   const ResTable_config* best_config = nullptr;
@@ -244,31 +249,56 @@
 
   for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) {
     const Type* type = &type_spec_ptr->types[i];
+    const ResTable_type* type_chunk = type->type;
 
     if (type->configuration.match(config) &&
         (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
       // The configuration matches and is better than the previous selection.
       // Find the entry value if it exists for this configuration.
-      const size_t entry_count = dtohl(type->type->entryCount);
-      const size_t offsets_offset = dtohs(type->type->header.headerSize);
-      if (entry_idx < entry_count) {
-        // If the package hasn't been verified, do bounds checking.
-        if (!Verified) {
-          if (!VerifyResTableType(type->type)) {
-            continue;
-          }
+      const size_t entry_count = dtohl(type_chunk->entryCount);
+      const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
+
+      // Check if there is the desired entry in this type.
+
+      if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
+        // This is encoded as a sparse map, so perform a binary search.
+        const ResTable_sparseTypeEntry* sparse_indices =
+            reinterpret_cast<const ResTable_sparseTypeEntry*>(
+                reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
+        const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
+        const ResTable_sparseTypeEntry* result =
+            std::lower_bound(sparse_indices, sparse_indices_end, entry_idx,
+                             [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
+                               return dtohs(entry.idx) < entry_idx;
+                             });
+
+        if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) {
+          // No entry found.
+          continue;
+        }
+
+        // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
+        // the real offset divided by 4.
+        best_offset = uint32_t{dtohs(result->offset)} * 4u;
+      } else {
+        if (entry_idx >= entry_count) {
+          // This entry cannot be here.
+          continue;
         }
 
         const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
-            reinterpret_cast<const uint8_t*>(type->type) + offsets_offset);
+            reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
         const uint32_t offset = dtohl(entry_offsets[entry_idx]);
-        if (offset != ResTable_type::NO_ENTRY) {
-          // There is an entry for this resource, record it.
-          best_config = &type->configuration;
-          best_type = type->type;
-          best_offset = offset;
+        if (offset == ResTable_type::NO_ENTRY) {
+          continue;
         }
+
+        // There is an entry for this resource, record it.
+        best_offset = offset;
       }
+
+      best_config = &type->configuration;
+      best_type = type_chunk;
     }
   }
 
@@ -276,10 +306,8 @@
     return false;
   }
 
-  if (!Verified) {
-    if (!VerifyResTableEntry(best_type, best_offset, entry_idx)) {
-      return false;
-    }
+  if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) {
+    return false;
   }
 
   const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
@@ -301,7 +329,7 @@
   // If the type IDs are offset in this package, we need to take that into account when searching
   // for a type.
   const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_];
-  if (ptr == nullptr) {
+  if (UNLIKELY(ptr == nullptr)) {
     return false;
   }
 
@@ -312,41 +340,7 @@
       return false;
     }
   }
-
-  // Don't bother checking if the entry ID is larger than the number of entries.
-  if (entry_idx >= dtohl(ptr->type_spec->entryCount)) {
-    return false;
-  }
-
-  if (verified_) {
-    return FindEntry<true>(ptr, entry_idx, config, out_entry);
-  }
-  return FindEntry<false>(ptr, entry_idx, config, out_entry);
-}
-
-static bool VerifyType(const Chunk& chunk) {
-  ATRACE_CALL();
-  const ResTable_type* header = chunk.header<ResTable_type, kResTableTypeMinSize>();
-
-  if (!VerifyResTableType(header)) {
-    return false;
-  }
-
-  const size_t entry_count = dtohl(header->entryCount);
-  const size_t offsets_offset = chunk.header_size();
-
-  // Check each entry offset.
-  const uint32_t* offsets =
-      reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(header) + offsets_offset);
-  for (size_t i = 0; i < entry_count; i++) {
-    uint32_t offset = dtohl(offsets[i]);
-    if (offset != ResTable_type::NO_ENTRY) {
-      if (!VerifyResTableEntry(header, offset, i)) {
-        return false;
-      }
-    }
-  }
-  return true;
+  return FindEntry(ptr, entry_idx, config, out_entry);
 }
 
 void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
@@ -461,7 +455,7 @@
       sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
   const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>();
   if (header == nullptr) {
-    LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small.";
+    LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small.";
     return {};
   }
 
@@ -483,7 +477,7 @@
   if (header->header.headerSize >= sizeof(ResTable_package)) {
     uint32_t type_id_offset = dtohl(header->typeIdOffset);
     if (type_id_offset > std::numeric_limits<uint8_t>::max()) {
-      LOG(ERROR) << "Type ID offset in RES_TABLE_PACKAGE_TYPE is too large.";
+      LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE type ID offset too large.";
       return {};
     }
     loaded_package->type_id_offset_ = static_cast<int>(type_id_offset);
@@ -514,7 +508,7 @@
           status_t err = loaded_package->type_string_pool_.setTo(
               child_chunk.header<ResStringPool_header>(), child_chunk.size());
           if (err != NO_ERROR) {
-            LOG(ERROR) << "Corrupt package type string pool.";
+            LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt.";
             return {};
           }
         } else if (pool_address == header_address + dtohl(header->keyStrings)) {
@@ -522,11 +516,11 @@
           status_t err = loaded_package->key_string_pool_.setTo(
               child_chunk.header<ResStringPool_header>(), child_chunk.size());
           if (err != NO_ERROR) {
-            LOG(ERROR) << "Corrupt package key string pool.";
+            LOG(ERROR) << "RES_STRING_POOL_TYPE for keys corrupt.";
             return {};
           }
         } else {
-          LOG(WARNING) << "Too many string pool chunks found in package.";
+          LOG(WARNING) << "Too many RES_STRING_POOL_TYPEs found in RES_TABLE_PACKAGE_TYPE.";
         }
       } break;
 
@@ -557,18 +551,18 @@
 
         const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
         if (type_spec == nullptr) {
-          LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE is too small.";
+          LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
           return {};
         }
 
         if (type_spec->id == 0) {
-          LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
+          LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
           return {};
         }
 
         if (loaded_package->type_id_offset_ + static_cast<int>(type_spec->id) >
             std::numeric_limits<uint8_t>::max()) {
-          LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has out of range ID.";
+          LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has out of range ID.";
           return {};
         }
 
@@ -580,12 +574,12 @@
         // There can only be 2^16 entries in a type, because that is the ID
         // space for entries (EEEE) in the resource ID 0xPPTTEEEE.
         if (entry_count > std::numeric_limits<uint16_t>::max()) {
-          LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_SPEC_TYPE: " << entry_count << ".";
+          LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has too many entries (" << entry_count << ").";
           return {};
         }
 
         if (entry_count * sizeof(uint32_t) > chunk.data_size()) {
-          LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_TYPE_SPEC_TYPE.";
+          LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small to hold entries.";
           return {};
         }
 
@@ -604,41 +598,32 @@
       case RES_TABLE_TYPE_TYPE: {
         const ResTable_type* type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
         if (type == nullptr) {
-          LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small.";
+          LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small.";
           return {};
         }
 
-        if (type->id == 0) {
-          LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0.";
+        if (!VerifyResTableType(type)) {
           return {};
         }
 
         // Type chunks must be preceded by their TypeSpec chunks.
         if (!types_builder || type->id - 1 != last_type_idx) {
-          LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without RES_TABLE_TYPE_SPEC_TYPE.";
+          LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE.";
           return {};
         }
 
-        // Only verify the type if we haven't already failed verification.
-        if (loaded_package->verified_) {
-          if (!VerifyType(child_chunk)) {
-            LOG(WARNING) << "Package failed verification, resource retrieval may be slower";
-            loaded_package->verified_ = false;
-          }
-        }
-
         types_builder->AddType(type);
       } break;
 
       case RES_TABLE_LIBRARY_TYPE: {
         const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>();
         if (lib == nullptr) {
-          LOG(ERROR) << "Chunk RES_TABLE_LIBRARY_TYPE is too small.";
+          LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small.";
           return {};
         }
 
         if (child_chunk.data_size() / sizeof(ResTable_lib_entry) < dtohl(lib->count)) {
-          LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_LIBRARY_TYPE.";
+          LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small to hold entries.";
           return {};
         }
 
@@ -705,7 +690,7 @@
   const uint8_t type_id = get_type_id(resid);
   const uint16_t entry_id = get_entry_id(resid);
 
-  if (type_id == 0) {
+  if (UNLIKELY(type_id == 0)) {
     LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
     return false;
   }
@@ -723,7 +708,7 @@
   ATRACE_CALL();
   const ResTable_header* header = chunk.header<ResTable_header>();
   if (header == nullptr) {
-    LOG(ERROR) << "Chunk RES_TABLE_TYPE is too small.";
+    LOG(ERROR) << "RES_TABLE_TYPE too small.";
     return false;
   }
 
@@ -742,11 +727,11 @@
           status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
                                                    child_chunk.size());
           if (err != NO_ERROR) {
-            LOG(ERROR) << "Corrupt string pool.";
+            LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt.";
             return false;
           }
         } else {
-          LOG(WARNING) << "Multiple string pool chunks found in resource table.";
+          LOG(WARNING) << "Multiple RES_STRING_POOL_TYPEs found in RES_TABLE_TYPE.";
         }
         break;
 
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 377735b..965e2db 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -119,11 +119,6 @@
     return overlay_;
   }
 
-  // Returns true if this package is verified to be valid.
-  inline bool IsVerified() const {
-    return verified_;
-  }
-
   // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
   // package could have been assigned a different package ID than what this LoadedPackage was
   // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
@@ -145,7 +140,6 @@
 
   LoadedPackage();
 
-  template <bool Verified>
   bool FindEntry(const util::unique_cptr<TypeSpec>& type_spec_ptr, uint16_t entry_idx,
                  const ResTable_config& config, FindEntryResult* out_entry) const;
 
@@ -157,7 +151,6 @@
   bool dynamic_ = false;
   bool system_ = false;
   bool overlay_ = false;
-  bool verified_ = true;
 
   ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
   std::vector<DynamicPackageEntry> dynamic_package_map_;
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index ba5844b..6c43a67 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -39,7 +39,6 @@
 
   const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
   ASSERT_NE(nullptr, loaded_package);
-  EXPECT_TRUE(loaded_package->IsVerified());
 
   std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
   ASSERT_NE(nullptr, asset);
@@ -59,7 +58,6 @@
 
   const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
   ASSERT_NE(nullptr, loaded_package);
-  EXPECT_TRUE(loaded_package->IsVerified());
 
   std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
   ASSERT_NE(nullptr, asset);
@@ -114,20 +112,6 @@
   ASSERT_NE(nullptr, loaded_overlay_apk);
 }
 
-TEST(ApkAssetsTest, LoadUnverifiableApk) {
-  std::unique_ptr<const ApkAssets> loaded_apk =
-      ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk");
-  ASSERT_NE(nullptr, loaded_apk);
-
-  const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
-  ASSERT_NE(nullptr, loaded_arsc);
-
-  const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
-  ASSERT_NE(nullptr, loaded_package);
-
-  EXPECT_FALSE(loaded_package->IsVerified());
-}
-
 TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
   std::unique_ptr<const ApkAssets> loaded_apk =
       ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 739e733..85e8f25 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -26,12 +26,10 @@
 #include "data/basic/R.h"
 #include "data/libclient/R.h"
 #include "data/styles/R.h"
-#include "data/unverified/R.h"
 
 namespace app = com::android::app;
 namespace basic = com::android::basic;
 namespace libclient = com::android::libclient;
-namespace unverified = com::android::unverified;
 
 namespace android {
 
@@ -83,37 +81,6 @@
 }
 BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld);
 
-static void GetResourceBenchmark(const std::vector<std::string>& paths,
-                                 const ResTable_config* config, uint32_t resid,
-                                 benchmark::State& state) {
-  std::vector<std::unique_ptr<const ApkAssets>> apk_assets;
-  std::vector<const ApkAssets*> apk_assets_ptrs;
-  for (const std::string& path : paths) {
-    std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
-    if (apk == nullptr) {
-      state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
-      return;
-    }
-    apk_assets_ptrs.push_back(apk.get());
-    apk_assets.push_back(std::move(apk));
-  }
-
-  AssetManager2 assetmanager;
-  assetmanager.SetApkAssets(apk_assets_ptrs);
-  if (config != nullptr) {
-    assetmanager.SetConfiguration(*config);
-  }
-
-  Res_value value;
-  ResTable_config selected_config;
-  uint32_t flags;
-
-  while (state.KeepRunning()) {
-    assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value,
-                             &selected_config, &flags);
-  }
-}
-
 static void BM_AssetManagerGetResource(benchmark::State& state) {
   GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
                        basic::R::integer::number1, state);
@@ -126,12 +93,6 @@
 }
 BENCHMARK(BM_AssetManagerGetResourceOld);
 
-static void BM_AssetManagerGetResourceUnverified(benchmark::State& state) {
-  GetResourceBenchmark({GetTestDataPath() + "/unverified/unverified.apk"}, nullptr /*config*/,
-                       unverified::R::integer::number1, state);
-}
-BENCHMARK(BM_AssetManagerGetResourceUnverified);
-
 static void BM_AssetManagerGetLibraryResource(benchmark::State& state) {
   GetResourceBenchmark(
       {GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk",
@@ -214,30 +175,6 @@
 }
 BENCHMARK(BM_AssetManagerGetBagOld);
 
-static void BM_AssetManagerGetBagUnverified(benchmark::State& state) {
-  std::unique_ptr<const ApkAssets> apk =
-      ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk");
-  if (apk == nullptr) {
-    state.SkipWithError("Failed to load assets");
-    return;
-  }
-
-  AssetManager2 assets;
-  assets.SetApkAssets({apk.get()});
-
-  while (state.KeepRunning()) {
-    const ResolvedBag* bag = assets.GetBag(unverified::R::array::integerArray1);
-    const auto bag_end = end(bag);
-    for (auto iter = begin(bag); iter != bag_end; ++iter) {
-      uint32_t key = iter->key;
-      Res_value value = iter->value;
-      benchmark::DoNotOptimize(key);
-      benchmark::DoNotOptimize(value);
-    }
-  }
-}
-BENCHMARK(BM_AssetManagerGetBagUnverified);
-
 static void BM_AssetManagerGetResourceLocales(benchmark::State& state) {
   std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
   if (apk == nullptr) {
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 567adfe..92462a6 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -28,7 +28,6 @@
 #include "data/libclient/R.h"
 #include "data/styles/R.h"
 #include "data/system/R.h"
-#include "data/unverified/R.h"
 
 namespace app = com::android::app;
 namespace appaslib = com::android::appaslib::app;
@@ -36,7 +35,6 @@
 namespace lib_one = com::android::lib_one;
 namespace lib_two = com::android::lib_two;
 namespace libclient = com::android::libclient;
-namespace unverified = com::android::unverified;
 
 namespace android {
 
@@ -452,30 +450,4 @@
 
 TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
 
-TEST_F(AssetManager2Test, OperateOnUnverifiedApkAssets) {
-  std::unique_ptr<const ApkAssets> unverified_assets =
-      ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk");
-  ASSERT_NE(nullptr, unverified_assets);
-
-  AssetManager2 assetmanager;
-  assetmanager.SetApkAssets({unverified_assets.get()});
-
-  Res_value value;
-  ResTable_config config;
-  uint32_t flags;
-
-  EXPECT_EQ(kInvalidCookie,
-            assetmanager.GetResource(unverified::R::string::test1, false /*may_be_bag*/, 0u, &value,
-                                     &config, &flags));
-  EXPECT_EQ(kInvalidCookie,
-            assetmanager.GetResource(unverified::R::string::test2, false /*may_be_bag*/, 0u, &value,
-                                     &config, &flags));
-  EXPECT_NE(kInvalidCookie,
-            assetmanager.GetResource(unverified::R::integer::number1, false /*may_be_bag*/, 0u,
-                                     &value, &config, &flags));
-
-  EXPECT_EQ(nullptr, assetmanager.GetBag(unverified::R::style::Theme1));
-  EXPECT_NE(nullptr, assetmanager.GetBag(unverified::R::array::integerArray1));
-}
-
 }  // namespace android
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index 3619b7ee..7149bee 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -18,6 +18,7 @@
 
 #include "android-base/stringprintf.h"
 #include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
 
 namespace android {
 
@@ -48,4 +49,34 @@
   }
 }
 
+void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_config* config,
+                          uint32_t resid, benchmark::State& state) {
+  std::vector<std::unique_ptr<const ApkAssets>> apk_assets;
+  std::vector<const ApkAssets*> apk_assets_ptrs;
+  for (const std::string& path : paths) {
+    std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
+    if (apk == nullptr) {
+      state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
+      return;
+    }
+    apk_assets_ptrs.push_back(apk.get());
+    apk_assets.push_back(std::move(apk));
+  }
+
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets(apk_assets_ptrs);
+  if (config != nullptr) {
+    assetmanager.SetConfiguration(*config);
+  }
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value,
+                             &selected_config, &flags);
+  }
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/BenchmarkHelpers.h b/libs/androidfw/tests/BenchmarkHelpers.h
index 0bb96b5..eb0939d 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.h
+++ b/libs/androidfw/tests/BenchmarkHelpers.h
@@ -30,6 +30,9 @@
 void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTable_config* config,
                              uint32_t resid, ::benchmark::State& state);
 
+void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_config* config,
+                          uint32_t resid, benchmark::State& state);
+
 }  // namespace android
 
 #endif  // ANDROIDFW_TESTS_BENCHMARKHELPERS_H
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 954a54d..37ddafb 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -19,11 +19,13 @@
 #include "TestHelpers.h"
 #include "data/basic/R.h"
 #include "data/libclient/R.h"
+#include "data/sparse/R.h"
 #include "data/styles/R.h"
 
 namespace app = com::android::app;
 namespace basic = com::android::basic;
 namespace libclient = com::android::libclient;
+namespace sparse = com::android::sparse;
 
 namespace android {
 
@@ -68,6 +70,23 @@
   ASSERT_NE(nullptr, entry.entry);
 }
 
+TEST(LoadedArscTest, LoadSparseEntryApp) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
+                                      &contents));
+
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  config.sdkVersion = 26;
+
+  FindEntryResult entry;
+  ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry));
+  ASSERT_NE(nullptr, entry.entry);
+}
+
 TEST(LoadedArscTest, LoadSharedLibrary) {
   std::string contents;
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
diff --git a/libs/androidfw/tests/SparseEntry_bench.cpp b/libs/androidfw/tests/SparseEntry_bench.cpp
index d6dc07d..c9b4ad8 100644
--- a/libs/androidfw/tests/SparseEntry_bench.cpp
+++ b/libs/androidfw/tests/SparseEntry_bench.cpp
@@ -24,40 +24,40 @@
 
 namespace android {
 
-static void BM_SparseEntryGetResourceSparseSmall(benchmark::State& state) {
+static void BM_SparseEntryGetResourceOldSparse(benchmark::State& state, uint32_t resid) {
   ResTable_config config;
   memset(&config, 0, sizeof(config));
   config.sdkVersion = 26;
-  GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config,
-                          sparse::R::integer::foo_9, state);
+  GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state);
 }
-BENCHMARK(BM_SparseEntryGetResourceSparseSmall);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Large, sparse::R::string::foo_999);
 
-static void BM_SparseEntryGetResourceNotSparseSmall(benchmark::State& state) {
+static void BM_SparseEntryGetResourceOldNotSparse(benchmark::State& state, uint32_t resid) {
   ResTable_config config;
   memset(&config, 0, sizeof(config));
   config.sdkVersion = 26;
-  GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config,
-                          sparse::R::integer::foo_9, state);
+  GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state);
 }
-BENCHMARK(BM_SparseEntryGetResourceNotSparseSmall);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Large, sparse::R::string::foo_999);
 
-static void BM_SparseEntryGetResourceSparseLarge(benchmark::State& state) {
+static void BM_SparseEntryGetResourceSparse(benchmark::State& state, uint32_t resid) {
   ResTable_config config;
   memset(&config, 0, sizeof(config));
   config.sdkVersion = 26;
-  GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config,
-                          sparse::R::string::foo_999, state);
+  GetResourceBenchmark({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state);
 }
-BENCHMARK(BM_SparseEntryGetResourceSparseLarge);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Large, sparse::R::string::foo_999);
 
-static void BM_SparseEntryGetResourceNotSparseLarge(benchmark::State& state) {
+static void BM_SparseEntryGetResourceNotSparse(benchmark::State& state, uint32_t resid) {
   ResTable_config config;
   memset(&config, 0, sizeof(config));
   config.sdkVersion = 26;
-  GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config,
-                          sparse::R::string::foo_999, state);
+  GetResourceBenchmark({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state);
 }
-BENCHMARK(BM_SparseEntryGetResourceNotSparseLarge);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Small, sparse::R::integer::foo_9);
+BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Large, sparse::R::string::foo_999);
 
 }  // namespace android
diff --git a/libs/androidfw/tests/data/unverified/R.h b/libs/androidfw/tests/data/unverified/R.h
deleted file mode 100644
index b734b49..0000000
--- a/libs/androidfw/tests/data/unverified/R.h
+++ /dev/null
@@ -1,32 +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.
- */
-
-#ifndef TESTS_DATA_UNVERIFIED_R_H_
-#define TESTS_DATA_UNVERIFIED_R_H_
-
-#include <cstdint>
-
-#include "tests/data/basic/R.h"
-
-namespace com {
-namespace android {
-
-namespace unverified = basic;
-
-}  // namespace android
-}  // namespace com
-
-#endif /* TESTS_DATA_UNVERIFIED_R_H_ */
diff --git a/libs/androidfw/tests/data/unverified/unverified.apk b/libs/androidfw/tests/data/unverified/unverified.apk
deleted file mode 100644
index 234b390..0000000
--- a/libs/androidfw/tests/data/unverified/unverified.apk
+++ /dev/null
Binary files differ
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 845acc0..e2f02df 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -205,7 +205,13 @@
             if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
                 paint = &tmpPaint;
             }
-            renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
+
+            // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
+            // we need to restrict the portion of the surface drawn to the size of the renderNode.
+            SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
+            SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
+            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
+                                  bounds, bounds, paint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index d5fe7f4..4ba368f 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -161,14 +161,18 @@
 
 bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                                        bool wideColorGamut) {
+    // compute the size of the surface (i.e. texture) to be allocated for this layer
+    const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
+    const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
+
     SkSurface* layer = node->getLayerSurface();
-    if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
+    if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
         SkImageInfo info;
         if (wideColorGamut) {
-            info = SkImageInfo::Make(node->getWidth(), node->getHeight(), kRGBA_F16_SkColorType,
+            info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType,
                                      kPremul_SkAlphaType);
         } else {
-            info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
+            info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight);
         }
         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
         SkASSERT(mRenderThread.getGrContext() != nullptr);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 05a9b75..20443ec 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -51,10 +51,17 @@
 
 static bool gHasRenderThreadInstance = false;
 
+static void (*gOnStartHook)() = nullptr;
+
 bool RenderThread::hasInstance() {
     return gHasRenderThreadInstance;
 }
 
+void RenderThread::setOnStartHook(void (*onStartHook)()) {
+    LOG_ALWAYS_FATAL_IF(hasInstance(), "can't set an onStartHook after we've started...");
+    gOnStartHook = onStartHook;
+}
+
 RenderThread& RenderThread::getInstance() {
     // This is a pointer because otherwise __cxa_finalize
     // will try to delete it like a Good Citizen but that causes us to crash
@@ -256,6 +263,9 @@
 
 bool RenderThread::threadLoop() {
     setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
+    if (gOnStartHook) {
+        gOnStartHook();
+    }
     initThreadLocals();
 
     while (true) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index d17a509..970537b 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -67,6 +67,9 @@
     PREVENT_COPY_AND_ASSIGN(RenderThread);
 
 public:
+    // Sets a callback that fires before any RenderThread setup has occured.
+    ANDROID_API static void setOnStartHook(void (*onStartHook)());
+
     WorkQueue& queue() { return ThreadBase::queue(); }
 
     // Mimics android.view.Choreographer
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 7dd271f..c7f57fe 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -460,15 +460,15 @@
         ProjectionLayer(int* drawCounter)
                 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
                 , mDrawCounter(drawCounter) {}
-        void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
+        virtual sk_sp<SkImage> onNewImageSnapshot() override {
             EXPECT_EQ(3, (*mDrawCounter)++);
             EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
                                        300 - SCROLL_Y),
                       TestUtils::getClipBounds(this->getCanvas()));
+            return nullptr;
         }
         SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); }
         sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
-        sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; }
         void onCopyOnWrite(ContentChangeMode) override {}
         int* mDrawCounter;
     };
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index b3fec1f..8068121 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -31,7 +31,10 @@
     PREVENT_COPY_AND_ASSIGN(ThreadBase);
 
 public:
-    ThreadBase() : mLooper(new Looper(false)), mQueue([this]() { mLooper->wake(); }, mLock) {}
+    ThreadBase()
+            : Thread(false)
+            , mLooper(new Looper(false))
+            , mQueue([this]() { mLooper->wake(); }, mLock) {}
 
     WorkQueue& queue() { return mQueue; }
 
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index da80989..2849d58 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -29,6 +29,12 @@
 
 using namespace std;
 
+// DESTINATION enum value, sync with proto/android/privacy.proto
+const uint8_t DEST_LOCAL = 0;
+const uint8_t DEST_EXPLICIT = 100;
+const uint8_t DEST_AUTOMATIC = 200;
+
+
 class IncidentReportArgs : public Parcelable {
 public:
     IncidentReportArgs();
@@ -41,19 +47,19 @@
     void setAll(bool all);
     void setDest(int dest);
     void addSection(int section);
-    void addHeader(const vector<int8_t>& header);
+    void addHeader(const vector<uint8_t>& header);
 
     inline bool all() const { return mAll; }
     bool containsSection(int section) const;
     inline int dest() const { return mDest; }
     inline const set<int>& sections() const { return mSections; }
-    inline const vector<vector<int8_t>>& headers() const { return mHeaders; }
+    inline const vector<vector<uint8_t>>& headers() const { return mHeaders; }
 
     void merge(const IncidentReportArgs& that);
 
 private:
     set<int> mSections;
-    vector<vector<int8_t>> mHeaders;
+    vector<vector<uint8_t>> mHeaders;
     bool mAll;
     int mDest;
 };
diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto
index 5fd75d6..7590b22 100644
--- a/libs/incident/proto/android/privacy.proto
+++ b/libs/incident/proto/android/privacy.proto
@@ -34,21 +34,23 @@
 
     // Fields or messages annotated with DEST_EXPLICIT can be sent
     // off the device with an explicit user action.
-    DEST_EXPLICIT = 1;
+    DEST_EXPLICIT = 100;
 
     // Fields or messages annotated with DEST_AUTOMATIC can be sent by
     // automatic means, without per-sending user consent. The user
     // still must have previously accepted a consent to share this
     // information.
-    DEST_AUTOMATIC = 2;
+    DEST_AUTOMATIC = 200;
 
-    // There is no more permissive option than DEST_AUTOMATIC.
+    // This is the default value, which could be overridden by other values.
+    // The reason to pick 255 is it fits into one byte.
+    DEST_UNSET = 255;
+
+    // Currently use 0, 100, 200 and 255, values in between are reserved for futures.
 }
 
 message PrivacyFlags {
-  optional Destination dest = 1  [
-      default = DEST_EXPLICIT
-  ];
+  optional Destination dest = 1 [ default = DEST_UNSET ];
 
   // regex to filter pii sensitive info from a string field type
   repeated string patterns = 2;
@@ -58,3 +60,8 @@
     // Flags for automatically filtering statistics
     optional PrivacyFlags privacy = 102672883;
 }
+
+extend google.protobuf.MessageOptions {
+    // Flags used to annotate a message which all its unset primitive types inhert this tag.
+    optional PrivacyFlags msg_privacy = 102672883;
+}
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index e628722..bd9c8ee 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -69,7 +69,7 @@
         return err;
     }
 
-    for (vector<vector<int8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) {
+    for (vector<vector<uint8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) {
         err = out->writeByteVector(*it);
         if (err != NO_ERROR) {
             return err;
@@ -161,7 +161,7 @@
 }
 
 void
-IncidentReportArgs::addHeader(const vector<int8_t>& header)
+IncidentReportArgs::addHeader(const vector<uint8_t>& header)
 {
     mHeaders.push_back(header);
 }
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 4a771eb..8b01aef 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -138,6 +138,7 @@
             ret.packageName = pkg.packageName;
             ret.splitNames = pkg.splitNames;
             ret.versionCode = pkg.versionCode;
+            ret.versionCodeMajor = pkg.versionCodeMajor;
             ret.baseRevisionCode = pkg.baseRevisionCode;
             ret.splitRevisionCodes = pkg.splitRevisionCodes;
             ret.installLocation = pkg.installLocation;
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index 99f7f71..894a1ab 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -13,11 +13,11 @@
     android-support-v7-recyclerview \
     android-support-v7-preference \
     android-support-v7-appcompat \
-    android-support-v14-preference
+    android-support-v14-preference \
+    apptoolkit-lifecycle-runtime
 
 LOCAL_SHARED_JAVA_LIBRARIES := \
-    apptoolkit-lifecycle-common \
-    apptoolkit-lifecycle-runtime
+    apptoolkit-lifecycle-common
 
 LOCAL_STATIC_JAVA_LIBRARY := legacy-android-test
 
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index b9abde2..49c5467 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -15,10 +15,12 @@
 
 ifeq ($(LOCAL_USE_AAPT2),true)
 LOCAL_STATIC_JAVA_LIBRARIES += \
-    android-support-annotations
+    android-support-annotations \
+    apptoolkit-lifecycle-common
 
 LOCAL_STATIC_ANDROID_LIBRARIES += \
     android-support-v4 \
+    apptoolkit-lifecycle-runtime \
     SettingsLib
 else
 LOCAL_RESOURCE_DIR += $(call my-dir)/res
@@ -59,5 +61,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES += \
     android-support-annotations \
     android-support-v4 \
+    apptoolkit-lifecycle-runtime \
+    apptoolkit-lifecycle-common \
     SettingsLib
 endif
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
index 2c26410..8055caa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -227,7 +227,7 @@
                 // cache the version code
                 PackageInfo info = context.getPackageManager().getPackageInfo(
                         context.getPackageName(), 0);
-                sCachedVersionCode = Integer.toString(info.versionCode);
+                sCachedVersionCode = Long.toString(info.getLongVersionCode());
 
                 // append the version code to the uri
                 builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java
index 8e27edf..8e01619 100644
--- a/packages/Shell/src/com/android/shell/Screenshooter.java
+++ b/packages/Shell/src/com/android/shell/Screenshooter.java
@@ -17,12 +17,11 @@
 package com.android.shell;
 
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
 import android.util.Log;
 import android.view.Display;
-import android.view.Surface;
 import android.view.SurfaceControl;
 
 /**
@@ -35,18 +34,6 @@
 
     private static final String TAG = "Screenshooter";
 
-    /** Rotation constant: Freeze rotation to 0 degrees (natural orientation) */
-    public static final int ROTATION_FREEZE_0 = Surface.ROTATION_0;
-
-    /** Rotation constant: Freeze rotation to 90 degrees . */
-    public static final int ROTATION_FREEZE_90 = Surface.ROTATION_90;
-
-    /** Rotation constant: Freeze rotation to 180 degrees . */
-    public static final int ROTATION_FREEZE_180 = Surface.ROTATION_180;
-
-    /** Rotation constant: Freeze rotation to 270 degrees . */
-    public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270;
-
     /**
      * Takes a screenshot.
      *
@@ -60,78 +47,21 @@
         final int displayWidth = displaySize.x;
         final int displayHeight = displaySize.y;
 
-        final float screenshotWidth;
-        final float screenshotHeight;
-
-        final int rotation = display.getRotation();
-        switch (rotation) {
-            case ROTATION_FREEZE_0: {
-                screenshotWidth = displayWidth;
-                screenshotHeight = displayHeight;
-            } break;
-            case ROTATION_FREEZE_90: {
-                screenshotWidth = displayHeight;
-                screenshotHeight = displayWidth;
-            } break;
-            case ROTATION_FREEZE_180: {
-                screenshotWidth = displayWidth;
-                screenshotHeight = displayHeight;
-            } break;
-            case ROTATION_FREEZE_270: {
-                screenshotWidth = displayHeight;
-                screenshotHeight = displayWidth;
-            } break;
-            default: {
-                throw new IllegalArgumentException("Invalid rotation: "
-                        + rotation);
-            }
-        }
-
+        int rotation = display.getRotation();
+        Rect crop = new Rect(0, 0, displayWidth, displayHeight);
         Log.d(TAG, "Taking screenshot of dimensions " + displayWidth + " x " + displayHeight);
         // Take the screenshot
         Bitmap screenShot =
-                SurfaceControl.screenshot((int) screenshotWidth, (int) screenshotHeight);
+                SurfaceControl.screenshot(crop, displayWidth, displayHeight, rotation);
         if (screenShot == null) {
-            Log.e(TAG, "Failed to take screenshot of dimensions " + screenshotWidth + " x "
-                    + screenshotHeight);
+            Log.e(TAG, "Failed to take screenshot of dimensions " + displayWidth + " x "
+                    + displayHeight);
             return null;
         }
 
-        // Rotate the screenshot to the current orientation
-        if (rotation != ROTATION_FREEZE_0) {
-            Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight,
-                    Bitmap.Config.ARGB_8888, screenShot.hasAlpha(), screenShot.getColorSpace());
-            Canvas canvas = new Canvas(unrotatedScreenShot);
-            canvas.translate(unrotatedScreenShot.getWidth() / 2,
-                    unrotatedScreenShot.getHeight() / 2);
-            canvas.rotate(getDegreesForRotation(rotation));
-            canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2);
-            canvas.drawBitmap(screenShot, 0, 0, null);
-            canvas.setBitmap(null);
-            screenShot.recycle();
-            screenShot = unrotatedScreenShot;
-        }
-
         // Optimization
         screenShot.setHasAlpha(false);
 
         return screenShot;
     }
-
-    private static float getDegreesForRotation(int value) {
-        switch (value) {
-            case Surface.ROTATION_90: {
-                return 360f - 90f;
-            }
-            case Surface.ROTATION_180: {
-                return 360f - 180f;
-            }
-            case Surface.ROTATION_270: {
-                return 360f - 270f;
-            } default: {
-                return 0;
-            }
-        }
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 775b9e8..a980413 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -16,8 +16,8 @@
 
 package com.android.keyguard;
 
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
 
 import android.content.Context;
 import android.os.AsyncTask;
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.widget.LinearLayout;
 
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index ec5f356..d636316 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,8 +15,8 @@
  */
 package com.android.keyguard;
 
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -33,6 +33,7 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
index d4149ea..9c847be 100644
--- a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
+++ b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
@@ -61,8 +61,3 @@
 ##       4: SYSTEM_REGISTER_USER     System sysui registers user's callbacks
 ##       5: SYSTEM_UNREGISTER_USER   System sysui unregisters user's callbacks (after death)
 36060 sysui_recents_connection (type|1),(user|1)
-
-# ---------------------------
-# LatencyTracker.java
-# ---------------------------
-36070 sysui_latency (action|1|5),(latency|1|3)
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 1d55ee5..cbb69ee 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -25,7 +25,7 @@
 import android.os.SystemClock;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 00e8b1a..3b54e11 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.app.WallpaperManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -112,11 +113,13 @@
     private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
     private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
+    private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
 
     private final Context mContext;
     private final GlobalActionsManager mWindowManagerFuncs;
     private final AudioManager mAudioManager;
     private final IDreamManager mDreamManager;
+    private final DevicePolicyManager mDevicePolicyManager;
 
     private ArrayList<Action> mItems;
     private ActionsDialog mDialog;
@@ -132,6 +135,7 @@
     private boolean mIsWaitingForEcmExit = false;
     private boolean mHasTelephony;
     private boolean mHasVibrator;
+    private boolean mHasLogoutButton;
     private final boolean mShowSilentToggle;
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
 
@@ -144,6 +148,8 @@
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.getService(DreamService.DREAM_SERVICE));
+        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -289,6 +295,7 @@
                 R.array.config_globalActionsList);
 
         ArraySet<String> addedKeys = new ArraySet<String>();
+        mHasLogoutButton = false;
         for (int i = 0; i < defaultActions.length; i++) {
             String actionKey = defaultActions[i];
             if (addedKeys.contains(actionKey)) {
@@ -325,6 +332,12 @@
                 mItems.add(getAssistAction());
             } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
                 mItems.add(new RestartAction());
+            } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
+                if (mDevicePolicyManager.isLogoutButtonEnabled()
+                        && getCurrentUser().id != UserHandle.USER_SYSTEM) {
+                    mItems.add(new LogoutAction());
+                    mHasLogoutButton = true;
+                }
             } else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
@@ -490,6 +503,37 @@
         }
     }
 
+    private final class LogoutAction extends SinglePressAction {
+        private LogoutAction() {
+            super(R.drawable.ic_logout, R.string.global_action_logout);
+        }
+
+        @Override
+        public boolean showDuringKeyguard() {
+            return true;
+        }
+
+        @Override
+        public boolean showBeforeProvisioning() {
+            return false;
+        }
+
+        @Override
+        public void onPress() {
+            // Add a little delay before executing, to give the dialog a chance to go away before
+            // switching user
+            mHandler.postDelayed(() -> {
+                try {
+                    ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
+                    ActivityManager.getService().stopUser(getCurrentUser().id, true /*force*/,
+                            null);
+                } catch (RemoteException re) {
+                    Log.e(TAG, "Couldn't logout user " + re);
+                }
+            }, 500);
+        }
+    }
+
     private Action getSettingsAction() {
         return new SinglePressAction(R.drawable.ic_settings,
                 R.string.global_action_settings) {
@@ -764,7 +808,10 @@
         public View getView(int position, View convertView, ViewGroup parent) {
             Action action = getItem(position);
             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
-            if (position == 2) {
+            // When there is no logout button, only power off and restart should be in white
+            // background, thus setting division view at third item; with logout button being the
+            // third item, set the division view at fourth item instead.
+            if (position == (mHasLogoutButton ? 3 : 2)) {
                 HardwareUiLayout.get(parent).setDivisionView(view);
             }
             return view;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c92acd0..1faf981 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -25,7 +25,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
-import android.app.Activity;
+
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.NotificationManager;
@@ -53,7 +53,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.provider.Settings.System;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.EventLog;
@@ -77,20 +76,16 @@
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 9aecc68..ca9a553 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -46,7 +46,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 2d3080b..130a5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -267,7 +267,7 @@
 
         try {
             return mIam.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
-                    false /* animate */, initialBounds);
+                    false /* animate */, initialBounds, true /* showRecents */);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 991c3c8..5fcd006 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -49,7 +49,6 @@
 import android.media.MediaActionSound;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.Bundle;
 import android.os.Environment;
 import android.os.PowerManager;
 import android.os.Process;
@@ -162,7 +161,7 @@
         Matrix matrix = new Matrix();
         int overlayColor = 0x40FFFFFF;
 
-        Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, data.image.getConfig());
+        Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
         matrix.setTranslate((previewWidth - mImageWidth) / 2, (previewHeight - mImageHeight) / 2);
         c.setBitmap(picture);
         c.drawBitmap(data.image, matrix, paint);
@@ -171,7 +170,7 @@
 
         // Note, we can't use the preview for the small icon, since it is non-square
         float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
-        Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, data.image.getConfig());
+        Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
         matrix.setScale(scale, scale);
         matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
                 (iconSize - (scale * mImageHeight)) / 2);
@@ -557,25 +556,14 @@
     /**
      * Takes a screenshot of the current display and shows an animation.
      */
-    void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
-            int x, int y, int width, int height) {
-        // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
-        // only in the natural orientation of the device :!)
-        mDisplay.getRealMetrics(mDisplayMetrics);
-        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
-        float degrees = getDegreesForRotation(mDisplay.getRotation());
-        boolean requiresRotation = (degrees > 0);
-        if (requiresRotation) {
-            // Get the dimensions of the device in its native orientation
-            mDisplayMatrix.reset();
-            mDisplayMatrix.preRotate(-degrees);
-            mDisplayMatrix.mapPoints(dims);
-            dims[0] = Math.abs(dims[0]);
-            dims[1] = Math.abs(dims[1]);
-        }
+    private void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
+            Rect crop) {
+        int rot = mDisplay.getRotation();
+        int width = crop.width();
+        int height = crop.height();
 
         // Take the screenshot
-        mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
+        mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);
         if (mScreenBitmap == null) {
             notifyScreenshotError(mContext, mNotificationManager,
                     R.string.screenshot_failed_to_capture_text);
@@ -583,29 +571,6 @@
             return;
         }
 
-        if (requiresRotation) {
-            // Rotate the screenshot to the current orientation
-            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
-                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888,
-                    mScreenBitmap.hasAlpha(), mScreenBitmap.getColorSpace());
-            Canvas c = new Canvas(ss);
-            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
-            c.rotate(degrees);
-            c.translate(-dims[0] / 2, -dims[1] / 2);
-            c.drawBitmap(mScreenBitmap, 0, 0, null);
-            c.setBitmap(null);
-            // Recycle the previous bitmap
-            mScreenBitmap.recycle();
-            mScreenBitmap = ss;
-        }
-
-        if (width != mDisplayMetrics.widthPixels || height != mDisplayMetrics.heightPixels) {
-            // Crop the screenshot to selected region
-            Bitmap cropped = Bitmap.createBitmap(mScreenBitmap, x, y, width, height);
-            mScreenBitmap.recycle();
-            mScreenBitmap = cropped;
-        }
-
         // Optimizations
         mScreenBitmap.setHasAlpha(false);
         mScreenBitmap.prepareToDraw();
@@ -617,8 +582,8 @@
 
     void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
         mDisplay.getRealMetrics(mDisplayMetrics);
-        takeScreenshot(finisher, statusBarVisible, navBarVisible, 0, 0, mDisplayMetrics.widthPixels,
-                mDisplayMetrics.heightPixels);
+        takeScreenshot(finisher, statusBarVisible, navBarVisible,
+                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
     }
 
     /**
@@ -648,7 +613,7 @@
                                 mScreenshotLayout.post(new Runnable() {
                                     public void run() {
                                         takeScreenshot(finisher, statusBarVisible, navBarVisible,
-                                                rect.left, rect.top, rect.width(), rect.height());
+                                                rect);
                                     }
                                 });
                             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 23d9cae..e04bd0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -112,6 +112,7 @@
     private int mNotificationMaxHeight;
     private int mNotificationAmbientHeight;
     private int mIncreasedPaddingBetweenElements;
+    private boolean mMustStayOnScreen;
 
     /** Does this row contain layouts that can adapt to row expansion */
     private boolean mExpandable;
@@ -491,6 +492,7 @@
             notifyHeightChanged(false  /* needsAnimation */);
         }
         if (isHeadsUp) {
+            mMustStayOnScreen = true;
             setAboveShelf(true);
         } else if (isAboveShelf() != wasAboveShelf) {
             mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
@@ -517,6 +519,12 @@
         addChildNotification(row, -1);
     }
 
+    @Override
+    public void setHeadsUpIsVisible() {
+        super.setHeadsUpIsVisible();
+        mMustStayOnScreen = false;
+    }
+
     /**
      * Add a child notification to this view.
      *
@@ -1942,7 +1950,7 @@
 
     @Override
     public boolean mustStayOnScreen() {
-        return mIsHeadsUp;
+        return mIsHeadsUp && mMustStayOnScreen;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 18b9860..f762513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -478,6 +478,9 @@
         return false;
     }
 
+    public void setHeadsUpIsVisible() {
+    }
+
     public boolean isChildInGroup() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 80d4061..9a4e616 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -26,7 +26,7 @@
 import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.Dependency;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 6d3bc1d..61b007f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -67,7 +67,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index afe5c91..83125c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,10 +27,8 @@
 import android.content.res.Resources;
 import android.os.AsyncTask;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -42,7 +40,7 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -50,7 +48,6 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 09fe579..f41cb29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -615,6 +615,7 @@
                     .addCategory(Intent.CATEGORY_BROWSABLE)
                     .addCategory("unique:" + System.currentTimeMillis())
                     .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
+                    .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
                     .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
                     .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ef05bbb..dacd3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -33,7 +33,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 652f8bb..8516278 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -288,7 +288,7 @@
         String description = null;
         // Only send data sim callbacks to QS.
         if (mCurrentState.dataSim) {
-            qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
+            qsTypeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mQsDataType : 0;
             qsIcon = new IconState(mCurrentState.enabled
                     && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
             description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
@@ -300,7 +300,7 @@
                 && !mCurrentState.carrierNetworkChangeMode
                 && mCurrentState.activityOut;
         showDataIcon &= mCurrentState.isDefault || dataDisabled;
-        int typeIcon = showDataIcon ? icons.mDataType : 0;
+        int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mDataType : 0;
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
                 mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
@@ -460,7 +460,7 @@
         mCurrentState.roaming = isRoaming();
         if (isCarrierNetworkChangeActive()) {
             mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
-        } else if (isDataDisabled()) {
+        } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) {
             mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
         }
         if (isEmergencyOnly() != mCurrentState.isEmergency) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 40ee838..baf0ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -29,7 +29,9 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.PersistableBundle;
 import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
@@ -245,6 +247,7 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         mContext.registerReceiver(this, filter, null, mReceiverHandler);
         mListening = true;
 
@@ -426,6 +429,14 @@
                 // emergency state.
                 recalculateEmergency();
             }
+        } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+            mConfig = Config.readConfig(mContext);
+            mReceiverHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleConfigurationChanged();
+                }
+            });
         } else {
             int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -969,6 +980,7 @@
         boolean hideLtePlus = false;
         boolean hspaDataDistinguishable;
         boolean inflateSignalStrengths = false;
+        boolean alwaysShowDataRatIcon = false;
 
         static Config readConfig(Context context) {
             Config config = new Config();
@@ -982,6 +994,14 @@
                     res.getBoolean(R.bool.config_hspa_data_distinguishable);
             config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
             config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength);
+
+            CarrierConfigManager configMgr = (CarrierConfigManager)
+                    context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            PersistableBundle b = configMgr.getConfig();
+            if (b != null) {
+                config.alwaysShowDataRatIcon = b.getBoolean(
+                        CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
+            }
             return config;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
index e0fd481..0650e23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
@@ -95,6 +95,12 @@
     public boolean inShelf;
 
     /**
+     * A state indicating whether a headsup is currently fully visible, even when not scrolled.
+     * Only valid if the view is heads upped.
+     */
+    public boolean headsUpIsVisible;
+
+    /**
      * How much the child overlaps with the previous child on top. This is used to
      * show the background properly when the child on top is translating away.
      */
@@ -126,6 +132,7 @@
             clipTopAmount = svs.clipTopAmount;
             notGoneIndex = svs.notGoneIndex;
             location = svs.location;
+            headsUpIsVisible = svs.headsUpIsVisible;
         }
     }
 
@@ -175,6 +182,10 @@
 
             expandableView.setTransformingInShelf(false);
             expandableView.setInShelf(inShelf);
+
+            if (headsUpIsVisible) {
+                expandableView.setHeadsUpIsVisible();
+            }
         }
     }
 
@@ -229,6 +240,10 @@
             expandableView.setTransformingInShelf(true);
         }
         expandableView.setInShelf(this.inShelf);
+
+        if (headsUpIsVisible) {
+            expandableView.setHeadsUpIsVisible();
+        }
     }
 
     private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index ebebfac..a3d2423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -624,7 +624,7 @@
         if (childViewState == null) {
             return false;
         }
-        if ((childViewState.location &= ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
+        if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
             return false;
         }
         if (row.getVisibility() != View.VISIBLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index c060b08..195607d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -135,7 +135,7 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             ExpandableViewState state = resultState.getViewStateForView(child);
-            if (!child.mustStayOnScreen()) {
+            if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
                 previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
                 previousNotificationStart = Math.max(drawStart, previousNotificationStart);
             }
@@ -378,6 +378,13 @@
         boolean isEmptyShadeView = child instanceof EmptyShadeView;
 
         childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
+        float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
+        if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
+            // Even if we're not scrolled away we're in view and we're also not in the
+            // shelf. We can relax the constraints and let us scroll off the top!
+            float end = childViewState.yTranslation + childViewState.height + inset;
+            childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
+        }
         if (isDismissView) {
             childViewState.yTranslation = Math.min(childViewState.yTranslation,
                     ambientState.getInnerHeight() - childHeight);
@@ -396,8 +403,7 @@
             Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
         }
 
-        childViewState.yTranslation += ambientState.getTopPadding()
-                + ambientState.getStackTranslation();
+        childViewState.yTranslation += inset;
         return currentYPosition;
     }
 
@@ -420,19 +426,21 @@
                 break;
             }
             ExpandableViewState childState = resultState.getViewStateForView(row);
-            if (topHeadsUpEntry == null) {
+            if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
                 topHeadsUpEntry = row;
                 childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
             }
             boolean isTopEntry = topHeadsUpEntry == row;
             float unmodifiedEndLocation = childState.yTranslation + childState.height;
             if (mIsExpanded) {
-                // Ensure that the heads up is always visible even when scrolled off
-                clampHunToTop(ambientState, row, childState);
-                if (i == 0 && ambientState.isAboveShelf(row)) {
-                    // the first hun can't get off screen.
-                    clampHunToMaxTranslation(ambientState, row, childState);
-                    childState.hidden = false;
+                if (row.mustStayOnScreen() && !childState.headsUpIsVisible) {
+                    // Ensure that the heads up is always visible even when scrolled off
+                    clampHunToTop(ambientState, row, childState);
+                    if (i == 0 && ambientState.isAboveShelf(row)) {
+                        // the first hun can't get off screen.
+                        clampHunToMaxTranslation(ambientState, row, childState);
+                        childState.hidden = false;
+                    }
                 }
             }
             if (row.isPinned()) {
@@ -493,6 +501,7 @@
         if (childViewState.yTranslation >= shelfStart) {
             childViewState.hidden = true;
             childViewState.inShelf = true;
+            childViewState.headsUpIsVisible = false;
         }
         if (!ambientState.isShadeExpanded()) {
             childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation);
@@ -531,7 +540,8 @@
         ExpandableViewState childViewState = resultState.getViewStateForView(child);
         int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
         float baseZ = ambientState.getBaseZHeight();
-        if (child.mustStayOnScreen() && !ambientState.isDozingAndNotPulsing(child)
+        if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
+                && !ambientState.isDozingAndNotPulsing(child)
                 && childViewState.yTranslation < ambientState.getTopPadding()
                 + ambientState.getStackTranslation()) {
             if (childrenOnTop != 0.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index e3c746b..588b758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -84,6 +84,7 @@
         viewState.scaleX = view.getScaleX();
         viewState.scaleY = view.getScaleY();
         viewState.inShelf = false;
+        viewState.headsUpIsVisible = false;
     }
 
     public ExpandableViewState getViewStateForView(View requestedView) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index b08b26d..2754026 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -49,7 +49,6 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.qs.tiles.DndTile;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 383d327..0d41e20 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -441,8 +441,7 @@
                 .withEndAction(() -> mDialog.dismiss())
                 .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
                 .start();
-        if (mAccessibilityMgr.isObservedEventType(
-                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) {
+        if (mAccessibilityMgr.isEnabled()) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             event.setPackageName(mContext.getPackageName());
diff --git a/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java
index d28e42e..474085c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java
@@ -29,6 +29,8 @@
 
 /**
  * A volume dialog controller for the automotive use case.
+ * TODO(hwwang): consider removing this class since it's coupled with stream_type and we are
+ * moving to use AudioAttributes usage for volume control in a car.
  *
  * {@link android.car.media.CarAudioManager} is the source of truth to get the stream volumes.
  * And volume changes should be sent to the car's audio module instead of the android's audio mixer.
@@ -70,7 +72,7 @@
             return;
         }
         try {
-            mCarAudioManager.setStreamVolume(stream, level, flag);
+            mCarAudioManager.setUsageVolume(stream, level, flag);
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Car is not connected", e);
         }
@@ -84,7 +86,7 @@
         }
 
         try {
-            return mCarAudioManager.getStreamVolume(stream);
+            return mCarAudioManager.getUsageVolume(stream);
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Car is not connected", e);
             return 0;
@@ -99,7 +101,7 @@
         }
 
         try {
-            return mCarAudioManager.getStreamMaxVolume(stream);
+            return mCarAudioManager.getUsageMaxVolume(stream);
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Car is not connected", e);
             return 0;
@@ -114,7 +116,7 @@
         }
 
         try {
-            return mCarAudioManager.getStreamMinVolume(stream);
+            return mCarAudioManager.getUsageMinVolume(stream);
         } catch (CarNotConnectedException e) {
             Log.e(TAG, "Car is not connected", e);
             return 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 1419e9a..3b796ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -15,6 +15,7 @@
 
 import com.android.settingslib.net.DataUsageController;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -137,6 +138,22 @@
     }
 
     @Test
+    public void testAlwaysShowDataRatIcon() {
+        setupDefaultSignal();
+        when(mMockTm.getDataEnabled(mSubId)).thenReturn(false);
+        updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED,
+                TelephonyManager.NETWORK_TYPE_GSM);
+
+        // Switch to showing data RAT icon when data is disconnected
+        // and re-initialize the NetworkController.
+        mConfig.alwaysShowDataRatIcon = true;
+        mNetworkController.handleConfigurationChanged();
+
+        verifyDataIndicators(TelephonyIcons.ICON_G,
+                TelephonyIcons.QS_DATA_G);
+    }
+
+    @Test
     public void test4gDataIconConfigChange() {
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index bd6af01..4f1f4d7 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5018,6 +5018,11 @@
     // OS: P
     FUELGAUGE_POWER_USAGE_SUMMARY_V2 = 1263;
 
+    // OPEN: Settings > Connected devices > Connection preferences
+    // CATEGORY: SETTINGS
+    // OS: P
+    CONNECTION_DEVICE_ADVANCED = 1264;
+
     // ---- End P Constants, all P constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 23e4f50..0291276 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -52,6 +52,7 @@
 import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.autofill.FillEventHistory;
+import android.service.autofill.UserData;
 import android.util.LocalLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -581,6 +582,34 @@
         }
 
         @Override
+        public UserData getUserData() throws RemoteException {
+            UserHandle user = getCallingUserHandle();
+            int uid = getCallingUid();
+
+            synchronized (mLock) {
+                AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+                if (service != null) {
+                    return service.getUserData(uid);
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        public void setUserData(UserData userData) throws RemoteException {
+            UserHandle user = getCallingUserHandle();
+            int uid = getCallingUid();
+
+            synchronized (mLock) {
+                AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+                if (service != null) {
+                    service.setUserData(uid, userData);
+                }
+            }
+        }
+
+        @Override
         public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
                 throws RemoteException {
             activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
@@ -723,6 +752,7 @@
             }
 
             boolean oldDebug = sDebug;
+            final String prefix = "  ";
             try {
                 synchronized (mLock) {
                     oldDebug = sDebug;
@@ -731,6 +761,7 @@
                     pw.print("Verbose mode: "); pw.println(sVerbose);
                     pw.print("Disabled users: "); pw.println(mDisabledUsers);
                     pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
+                    pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw);
                     final int size = mServicesCache.size();
                     pw.print("Cached services: ");
                     if (size == 0) {
@@ -740,7 +771,7 @@
                         for (int i = 0; i < size; i++) {
                             pw.print("\nService at index "); pw.println(i);
                             final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
-                            impl.dumpLocked("  ", pw);
+                            impl.dumpLocked(prefix, pw);
                         }
                     }
                     mUi.dump(pw);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 21e2722..8b6dc20 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -51,6 +51,7 @@
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
+import android.service.autofill.UserData;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -121,6 +122,11 @@
     private boolean mDisabled;
 
     /**
+     * Data used for field classification.
+     */
+    private UserData mUserData;
+
+    /**
      * Caches whether the setup completed for the current user.
      */
     @GuardedBy("mLock")
@@ -183,6 +189,14 @@
         }
     }
 
+    private int getServiceUidLocked() {
+        if (mInfo == null) {
+            Slog.w(TAG,  "getServiceUidLocked(): no mInfo");
+            return -1;
+        }
+        return mInfo.getServiceInfo().applicationInfo.uid;
+    }
+
     @Nullable
     String getServicePackageName() {
         final ComponentName serviceComponent = getServiceComponentName();
@@ -574,9 +588,9 @@
      * Initializes the last fill selection after an autofill service returned a new
      * {@link FillResponse}.
      */
-    void setLastResponse(int serviceUid, int sessionId, @NonNull FillResponse response) {
+    void setLastResponse(int sessionId, @NonNull FillResponse response) {
         synchronized (mLock) {
-            mEventHistory = new FillEventHistory(serviceUid, sessionId, response.getClientState());
+            mEventHistory = new FillEventHistory(sessionId, response.getClientState());
         }
     }
 
@@ -688,18 +702,54 @@
      */
     FillEventHistory getFillEventHistory(int callingUid) {
         synchronized (mLock) {
-            if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) {
+            if (mEventHistory != null
+                    && isCalledByServiceLocked("getFillEventHistory", callingUid)) {
                 return mEventHistory;
             }
         }
-
         return null;
     }
 
+    // Called by Session - does not need to check uid
+    UserData getUserData() {
+        synchronized (mLock) {
+            return mUserData;
+        }
+    }
+
+    // Called by AutofillManager
+    UserData getUserData(int callingUid) {
+        synchronized (mLock) {
+            if (isCalledByServiceLocked("getUserData", callingUid)) {
+                return mUserData;
+            }
+        }
+        return null;
+    }
+
+    // Called by AutofillManager
+    void setUserData(int callingUid, UserData userData) {
+        synchronized (mLock) {
+            if (isCalledByServiceLocked("setUserData", callingUid)) {
+                mUserData = userData;
+            }
+        }
+    }
+
+    private boolean isCalledByServiceLocked(String methodName, int callingUid) {
+        if (getServiceUidLocked() != callingUid) {
+            Slog.w(TAG, methodName + "() called by UID " + callingUid
+                    + ", but service UID is " + getServiceUidLocked());
+            return false;
+        }
+        return true;
+    }
+
     void dumpLocked(String prefix, PrintWriter pw) {
         final String prefix2 = prefix + "  ";
 
         pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+        pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
         pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null
                 ? mInfo.getServiceInfo().getComponentName() : null);
         pw.print(prefix); pw.print("Component from settings: ");
@@ -762,8 +812,13 @@
             }
         }
 
-        pw.print(prefix); pw.println("Clients");
-        mClients.dump(pw, prefix2);
+        pw.print(prefix); pw.print("Clients: ");
+        if (mClients == null) {
+            pw.println("N/A");
+        } else {
+            pw.println();
+            mClients.dump(pw, prefix2);
+        }
 
         if (mEventHistory == null || mEventHistory.getEvents() == null
                 || mEventHistory.getEvents().size() == 0) {
@@ -779,6 +834,14 @@
                         + event.getDatasetId());
             }
         }
+
+        pw.print(prefix); pw.print("User data: ");
+        if (mUserData == null) {
+            pw.println("N/A");
+        } else {
+            pw.println();
+            mUserData.dump(prefix2, pw);
+        }
     }
 
     void destroySessionsLocked() {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 831c488..aea9ad0 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -97,7 +97,7 @@
     private PendingRequest mPendingRequest;
 
     public interface FillServiceCallbacks {
-        void onFillRequestSuccess(int requestFlags, @Nullable FillResponse response, int serviceUid,
+        void onFillRequestSuccess(int requestFlags, @Nullable FillResponse response,
                 @NonNull String servicePackageName);
         void onFillRequestFailure(@Nullable CharSequence message,
                 @NonNull String servicePackageName);
@@ -281,11 +281,11 @@
         mContext.unbindService(mServiceConnection);
     }
 
-    private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
-            int callingUid, int requestFlags, FillResponse response) {
+    private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest, int requestFlags,
+            FillResponse response) {
         mHandler.getHandler().post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onFillRequestSuccess(requestFlags, response, callingUid,
+                mCallbacks.onFillRequestSuccess(requestFlags, response,
                         mComponentName.getPackageName());
             }
         });
@@ -546,7 +546,7 @@
                     final RemoteFillService remoteService = getService();
                     if (remoteService != null) {
                         remoteService.dispatchOnFillRequestSuccess(PendingFillRequest.this,
-                                getCallingUid(), request.getFlags(), response);
+                                request.getFlags(), response);
                     }
                 }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index e3db1b1..3615bca 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -55,7 +55,6 @@
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
-import android.service.autofill.FieldsDetection;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -63,6 +62,7 @@
 import android.service.autofill.InternalValidator;
 import android.service.autofill.SaveInfo;
 import android.service.autofill.SaveRequest;
+import android.service.autofill.UserData;
 import android.service.autofill.ValueFinder;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -485,7 +485,7 @@
     // FillServiceCallbacks
     @Override
     public void onFillRequestSuccess(int requestFlags, @Nullable FillResponse response,
-            int serviceUid, @NonNull String servicePackageName) {
+            @NonNull String servicePackageName) {
         synchronized (mLock) {
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
@@ -499,13 +499,13 @@
         }
 
         // TODO(b/67867469): remove once feature is finished
-        if (response.getFieldsDetection() != null && !mService.isFieldDetectionEnabled()) {
+        if (response.getFieldClassificationIds() != null && !mService.isFieldDetectionEnabled()) {
             Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
             processNullResponseLocked(requestFlags);
             return;
         }
 
-        mService.setLastResponse(serviceUid, id, response);
+        mService.setLastResponse(id, response);
 
         int sessionFinishedState = 0;
         final long disableDuration = response.getDisableDuration();
@@ -908,7 +908,7 @@
             final FillResponse response = mResponses.valueAt(i);
             final List<Dataset> datasets = response.getDatasets();
             if (datasets == null || datasets.isEmpty()) {
-                if (sVerbose) Slog.v(TAG,  "logContextCommitted() no datasets at " + i);
+                if (sVerbose) Slog.v(TAG, "logContextCommitted() no datasets at " + i);
             } else {
                 for (int j = 0; j < datasets.size(); j++) {
                     final Dataset dataset = datasets.get(j);
@@ -931,25 +931,27 @@
                 }
             }
         }
-        final FieldsDetection fieldsDetection = lastResponse.getFieldsDetection();
+        final AutofillId[] fieldClassificationIds = lastResponse.getFieldClassificationIds();
 
-        if (!hasAtLeastOneDataset && fieldsDetection == null) {
+        if (!hasAtLeastOneDataset && fieldClassificationIds == null) {
             if (sVerbose) {
                 Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets nor fields "
-                        + "detection)");
+                        + "classification ids)");
             }
             return;
         }
 
+        final UserData userData = mService.getUserData();
         final AutofillId detectableFieldId;
         final String detectableRemoteId;
         String detectedRemoteId = null;
-        if (fieldsDetection == null) {
+        if (userData == null) {
             detectableFieldId = null;
             detectableRemoteId = null;
         } else {
-            detectableFieldId = fieldsDetection.getFieldId();
-            detectableRemoteId = fieldsDetection.getRemoteId();
+            // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
+            detectableFieldId = fieldClassificationIds[0];
+            detectableRemoteId = userData.getRemoteIds()[0];
         }
 
         int detectedFieldScore = -1;
@@ -1062,7 +1064,8 @@
                     if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
                             && currentValue.isText() && currentValue.getTextValue() != null) {
                         final String actualValue = currentValue.getTextValue().toString();
-                        final String expectedValue = fieldsDetection.getValue();
+                        // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
+                        final String expectedValue = userData.getValues()[0];
                         if (actualValue.equalsIgnoreCase(expectedValue)) {
                             detectedRemoteId = detectableRemoteId;
                             detectedFieldScore = 0;
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index dfc65a32b..5ee3cbf 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -108,9 +108,11 @@
         mCallback = callback;
 
         final LayoutInflater inflater = LayoutInflater.from(context);
+
         final ViewGroup decor = (ViewGroup) inflater.inflate(
                 R.layout.autofill_dataset_picker, null);
 
+
         final RemoteViews.OnClickHandler interceptionHandler = new RemoteViews.OnClickHandler() {
             @Override
             public boolean onClickHandler(View view, PendingIntent pendingIntent,
@@ -153,7 +155,38 @@
             mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
         } else {
             final int datasetCount = response.getDatasets().size();
-            final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
+
+            // Total items include the (optional) header and footer - we cannot use listview's
+            // addHeader() and addFooter() because it would complicate the scrolling logic.
+            int totalItems = datasetCount;
+
+            RemoteViews.OnClickHandler clickBlocker = null;
+            final RemoteViews headerPresentation = response.getHeader();
+            View header = null;
+            if (headerPresentation != null) {
+                clickBlocker = newClickBlocker();
+                header = headerPresentation.apply(context, null, clickBlocker);
+                totalItems++;
+            }
+
+            final RemoteViews footerPresentation = response.getFooter();
+            View footer = null;
+            if (footerPresentation != null) {
+                if (clickBlocker == null) { // already set for header
+                    clickBlocker = newClickBlocker();
+                }
+                footer = footerPresentation.apply(context, null, clickBlocker);
+                totalItems++;
+            }
+            if (sVerbose) {
+                Slog.v(TAG, "Number datasets: " + datasetCount + " Total items: " + totalItems);
+            }
+
+            final ArrayList<ViewItem> items = new ArrayList<>(totalItems);
+            if (header != null) {
+                if (sVerbose) Slog.v(TAG, "adding header");
+                items.add(new ViewItem(null, null, null, header));
+            }
             for (int i = 0; i < datasetCount; i++) {
                 final Dataset dataset = response.getDatasets().get(i);
                 final int index = dataset.getFieldIds().indexOf(focusedViewId);
@@ -184,6 +217,10 @@
                     items.add(new ViewItem(dataset, filter, valueText, view));
                 }
             }
+            if (footer != null) {
+                if (sVerbose) Slog.v(TAG, "adding footer");
+                items.add(new ViewItem(null, null, null, footer));
+            }
 
             mAdapter = new ItemsAdapter(items);
 
@@ -192,7 +229,12 @@
             mListView.setVisibility(View.VISIBLE);
             mListView.setOnItemClickListener((adapter, view, position, id) -> {
                 final ViewItem vi = mAdapter.getItem(position);
-                mCallback.onDatasetPicked(vi.getDataset());
+                if (vi.dataset == null) {
+                    // Clicked on header or footer; ignore.
+                    if (sDebug) Slog.d(TAG, "Ignoring click on item " + position + ": " + view);
+                    return;
+                }
+                mCallback.onDatasetPicked(vi.dataset);
             });
 
             if (filterText == null) {
@@ -206,6 +248,20 @@
         }
     }
 
+    /**
+     * Creates a remoteview interceptor used to block clicks.
+     */
+    private RemoteViews.OnClickHandler newClickBlocker() {
+        return new RemoteViews.OnClickHandler() {
+            @Override
+            public boolean onClickHandler(View view, PendingIntent pendingIntent,
+                    Intent fillInIntent) {
+                if (sVerbose) Slog.v(TAG, "Ignoring click on " + view);
+                return true;
+            }
+        };
+    }
+
     private void applyNewFilterText() {
         final int oldCount = mAdapter.getCount();
         mAdapter.getFilter().filter(mFilterText, (count) -> {
@@ -298,7 +354,7 @@
                 MeasureSpec.AT_MOST);
         final int itemCount = mAdapter.getCount();
         for (int i = 0; i < itemCount; i++) {
-            View view = mAdapter.getItem(i).getView();
+            View view = mAdapter.getItem(i).view;
             view.measure(widthMeasureSpec, heightMeasureSpec);
             final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
             final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
@@ -336,33 +392,21 @@
         outPoint.y = (int) typedValue.getFraction(outPoint.y, outPoint.y);
     }
 
+    /**
+     * An item for the list view - either a (clickable) dataset or a (read-only) header / footer.
+     */
     private static class ViewItem {
-        private final String mValue;
-        private final Dataset mDataset;
-        private final View mView;
-        private final Pattern mFilter;
+        public final @Nullable String value;
+        public final @Nullable Dataset dataset;
+        public final @NonNull View view;
+        public final @Nullable Pattern filter;
 
-        ViewItem(Dataset dataset, Pattern filter, String value, View view) {
-            mDataset = dataset;
-            mValue = value;
-            mView = view;
-            mFilter = filter;
-        }
-
-        public Pattern getFilter() {
-            return mFilter;
-        }
-
-        public View getView() {
-            return mView;
-        }
-
-        public Dataset getDataset() {
-            return mDataset;
-        }
-
-        public String getValue() {
-            return mValue;
+        ViewItem(@Nullable Dataset dataset, @Nullable Pattern filter, @Nullable String value,
+                @NonNull View view) {
+            this.dataset = dataset;
+            this.value = value;
+            this.view = view;
+            this.filter = filter;
         }
     }
 
@@ -525,15 +569,13 @@
                     final int itemCount = mAllItems.size();
                     for (int i = 0; i < itemCount; i++) {
                         final ViewItem item = mAllItems.get(i);
-                        final String value = item.getValue();
-                        final Pattern filter = item.getFilter();
                         final boolean matches;
-                        if (filter != null) {
-                            matches = filter.matcher(constraintLowerCase).matches();
+                        if (item.filter != null) {
+                            matches = item.filter.matcher(constraintLowerCase).matches();
                         } else {
-                            matches = (value == null)
-                                    ? (item.mDataset.getAuthentication() == null)
-                                    : value.toLowerCase().startsWith(constraintLowerCase);
+                            matches = (item.value == null)
+                                    ? (item.dataset.getAuthentication() == null)
+                                    : item.value.toLowerCase().startsWith(constraintLowerCase);
                         }
                         if (matches) {
                             filteredItems.add(item);
@@ -580,7 +622,7 @@
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            return getItem(position).getView();
+            return getItem(position).view;
         }
     }
 
diff --git a/services/backup/java/com/android/server/backup/FileMetadata.java b/services/backup/java/com/android/server/backup/FileMetadata.java
index 5465609..3d260cb 100644
--- a/services/backup/java/com/android/server/backup/FileMetadata.java
+++ b/services/backup/java/com/android/server/backup/FileMetadata.java
@@ -36,7 +36,7 @@
     public long mode;                      // e.g. 0666 (actually int)
     public long mtime;                     // last mod time, UTC time_t (actually int)
     public long size;                      // bytes of content
-    public int version;                    // App version.
+    public long version;                   // App version.
     public boolean hasApk;                 // Whether backup file contains apk.
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index f658f22..2d2993d 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -99,10 +99,10 @@
     // For compactness we store the SHA-256 hash of each app's Signatures
     // rather than the Signature blocks themselves.
     public class Metadata {
-        public int versionCode;
+        public long versionCode;
         public ArrayList<byte[]> sigHashes;
 
-        Metadata(int version, ArrayList<byte[]> hashes) {
+        Metadata(long version, ArrayList<byte[]> hashes) {
             versionCode = version;
             sigHashes = hashes;
         }
@@ -206,7 +206,7 @@
                 homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
                         PackageManager.GET_SIGNATURES);
                 homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
-                homeVersion = homeInfo.versionCode;
+                homeVersion = homeInfo.getLongVersionCode();
                 homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures);
             } catch (NameNotFoundException e) {
                 Slog.w(TAG, "Can't access preferred home info");
@@ -287,7 +287,7 @@
                         // metadata again.  In either case, take it out of mExisting so that
                         // we don't consider it deleted later.
                         mExisting.remove(packName);
-                        if (info.versionCode == mStateVersions.get(packName).versionCode) {
+                        if (info.getLongVersionCode() == mStateVersions.get(packName).versionCode) {
                             continue;
                         }
                     }
@@ -309,13 +309,18 @@
 
                     // marshal the version code in a canonical form
                     outputBuffer.reset();
-                    outputBufferStream.writeInt(info.versionCode);
+                    if (info.versionCodeMajor != 0) {
+                        outputBufferStream.writeInt(Integer.MIN_VALUE);
+                        outputBufferStream.writeLong(info.getLongVersionCode());
+                    } else {
+                        outputBufferStream.writeInt(info.versionCode);
+                    }
                     writeSignatureHashArray(outputBufferStream,
                             BackupUtils.hashSignatureArray(info.signatures));
 
                     if (DEBUG) {
                         Slog.v(TAG, "+ writing metadata for " + packName
-                                + " version=" + info.versionCode
+                                + " version=" + info.getLongVersionCode()
                                 + " entityLen=" + outputBuffer.size());
                     }
                     
@@ -409,7 +414,13 @@
                 }
             } else {
                 // it's a file metadata record
-                int versionCode = inputBufferStream.readInt();
+                int versionCodeInt = inputBufferStream.readInt();
+                long versionCode;
+                if (versionCodeInt == Integer.MIN_VALUE) {
+                    versionCode = inputBufferStream.readLong();
+                } else {
+                    versionCode = versionCodeInt;
+                }
                 ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream);
                 if (DEBUG) {
                     Slog.i(TAG, "   read metadata for " + key
@@ -561,7 +572,13 @@
             // The global metadata was last; now read all the apps
             while (true) {
                 pkg = in.readUTF();
-                int versionCode = in.readInt();
+                int versionCodeInt = in.readInt();
+                long versionCode;
+                if (versionCodeInt == Integer.MIN_VALUE) {
+                    versionCode = in.readLong();
+                } else {
+                    versionCode = versionCodeInt;
+                }
 
                 if (!ignoreExisting) {
                     mExisting.add(pkg);
@@ -609,7 +626,12 @@
             // now write all the app names + versions
             for (PackageInfo pkg : pkgs) {
                 out.writeUTF(pkg.packageName);
-                out.writeInt(pkg.versionCode);
+                if (pkg.versionCodeMajor != 0) {
+                    out.writeInt(Integer.MIN_VALUE);
+                    out.writeLong(pkg.getLongVersionCode());
+                } else {
+                    out.writeInt(pkg.versionCode);
+                }
             }
 
             out.flush();
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index b538c6d..5884dc5 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -495,14 +495,14 @@
                 return;
             }
 
-            if (metaInfo.versionCode > mCurrentPackage.versionCode) {
+            if (metaInfo.versionCode > mCurrentPackage.getLongVersionCode()) {
                 // Data is from a "newer" version of the app than we have currently
                 // installed.  If the app has not declared that it is prepared to
                 // handle this case, we do not attempt the restore.
                 if ((mCurrentPackage.applicationInfo.flags
                         & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
                     String message = "Source version " + metaInfo.versionCode
-                            + " > installed version " + mCurrentPackage.versionCode;
+                            + " > installed version " + mCurrentPackage.getLongVersionCode();
                     Slog.w(TAG, "Package " + pkgName + ": " + message);
                     Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
                             BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
@@ -522,7 +522,7 @@
                 } else {
                     if (DEBUG) {
                         Slog.v(TAG, "Source version " + metaInfo.versionCode
-                                + " > installed version " + mCurrentPackage.versionCode
+                                + " > installed version " + mCurrentPackage.getLongVersionCode()
                                 + " but restoreAnyVersion");
                     }
                     Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
@@ -543,7 +543,7 @@
                 Slog.v(TAG, "Package " + pkgName
                         + " restore version [" + metaInfo.versionCode
                         + "] is compatible with installed version ["
-                        + mCurrentPackage.versionCode + "]");
+                        + mCurrentPackage.getLongVersionCode() + "]");
             }
 
             // Reset per-package preconditions and fire the appropriate next state
@@ -635,7 +635,7 @@
     }
 
     // Guts of a key/value restore operation
-    void initiateOneRestore(PackageInfo app, int appVersionCode) {
+    void initiateOneRestore(PackageInfo app, long appVersionCode) {
         final String packageName = app.packageName;
 
         if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
index 734fa1d..010684e 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
@@ -56,6 +56,8 @@
                             pkg.packageName);
                     bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION,
                             pkg.versionCode);
+                    bundle.putLong(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION,
+                            pkg.getLongVersionCode());
                 }
                 if (extras != null) {
                     bundle.putAll(extras);
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index b3e20dc..a731fc9 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -97,7 +97,7 @@
 
         printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
         printer.println(pkg.packageName);
-        printer.println(Integer.toString(pkg.versionCode));
+        printer.println(Long.toString(pkg.getLongVersionCode()));
         printer.println(Integer.toString(Build.VERSION.SDK_INT));
 
         String installerName = packageManager.getInstallerPackageName(pkg.packageName);
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 2910ba2..ff9cb56 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -422,7 +422,7 @@
                                     LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
                                     null);
                             policy = RestorePolicy.ACCEPT;
-                        } else if (pkgInfo.versionCode >= info.version) {
+                        } else if (pkgInfo.getLongVersionCode() >= info.version) {
                             Slog.i(TAG, "Sig + version match; taking data");
                             policy = RestorePolicy.ACCEPT;
                             mMonitor = BackupManagerMonitorUtils.monitorEvent(
@@ -439,7 +439,7 @@
                                 Slog.i(TAG, "Data version " + info.version
                                         + " is newer than installed "
                                         + "version "
-                                        + pkgInfo.versionCode
+                                        + pkgInfo.getLongVersionCode()
                                         + " - requiring apk");
                                 policy = RestorePolicy.ACCEPT_IF_APK;
                             } else {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bccae06..86b0164 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -972,7 +972,10 @@
                         getNetworkTypeName(networkType), "");
                 info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
                 info.setIsAvailable(true);
-                state = new NetworkState(info, new LinkProperties(), new NetworkCapabilities(),
+                final NetworkCapabilities capabilities = new NetworkCapabilities();
+                capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
+                        !info.isRoaming());
+                state = new NetworkState(info, new LinkProperties(), capabilities,
                         null, null, null);
             }
             filterNetworkStateForUid(state, uid, ignoreBlocked);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index d7aeb8c..4f1e335 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -76,6 +76,7 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.AtomicFile;
 import com.android.internal.os.BackgroundThread;
@@ -1754,6 +1755,27 @@
         }
     }
 
+    void removePowerSaveTempWhitelistAppChecked(String packageName, int userId)
+            throws RemoteException {
+        getContext().enforceCallingPermission(
+                Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+                "No permission to change device idle whitelist");
+        final int callingUid = Binder.getCallingUid();
+        userId = ActivityManager.getService().handleIncomingUser(
+                Binder.getCallingPid(),
+                callingUid,
+                userId,
+                /*allowAll=*/ false,
+                /*requireFull=*/ false,
+                "removePowerSaveTempWhitelistApp", null);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            removePowerSaveTempWhitelistAppInternal(packageName, userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     /**
      * Adds an app to the temporary whitelist and resets the endTime for granting the
      * app an exemption to access network and acquire wakelocks.
@@ -1819,6 +1841,32 @@
         }
     }
 
+    /**
+     * Removes an app from the temporary whitelist and notifies the observers.
+     */
+    private void removePowerSaveTempWhitelistAppInternal(String packageName, int userId) {
+        try {
+            final int uid = getContext().getPackageManager().getPackageUidAsUser(
+                    packageName, userId);
+            final int appId = UserHandle.getAppId(uid);
+            removePowerSaveTempWhitelistAppDirectInternal(appId);
+        } catch (NameNotFoundException e) {
+        }
+    }
+
+    private void removePowerSaveTempWhitelistAppDirectInternal(int appId) {
+        synchronized (this) {
+            final int idx = mTempWhitelistAppIdEndTimes.indexOfKey(appId);
+            if (idx < 0) {
+                // Nothing else to do
+                return;
+            }
+            final String reason = mTempWhitelistAppIdEndTimes.valueAt(idx).second;
+            mTempWhitelistAppIdEndTimes.removeAt(idx);
+            onAppRemovedFromTempWhitelistLocked(appId, reason);
+        }
+    }
+
     private void postTempActiveTimeoutMessage(int uid, long delay) {
         if (DEBUG) {
             Slog.d(TAG, "postTempActiveTimeoutMessage: uid=" + uid + ", delay=" + delay);
@@ -1840,18 +1888,7 @@
             }
             if (timeNow >= entry.first.value) {
                 mTempWhitelistAppIdEndTimes.delete(uid);
-                if (DEBUG) {
-                    Slog.d(TAG, "Removing UID " + uid + " from temp whitelist");
-                }
-                updateTempWhitelistAppIdsLocked(uid, false);
-                mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, uid, 0)
-                        .sendToTarget();
-                reportTempWhitelistChangedLocked();
-                try {
-                    mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_TEMP_WHITELIST_FINISH,
-                            entry.second, uid);
-                } catch (RemoteException e) {
-                }
+                onAppRemovedFromTempWhitelistLocked(uid, entry.second);
             } else {
                 // Need more time
                 if (DEBUG) {
@@ -1862,6 +1899,22 @@
         }
     }
 
+    @GuardedBy("this")
+    private void onAppRemovedFromTempWhitelistLocked(int appId, String reason) {
+        if (DEBUG) {
+            Slog.d(TAG, "Removing appId " + appId + " from temp whitelist");
+        }
+        updateTempWhitelistAppIdsLocked(appId, false);
+        mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, appId, 0)
+                .sendToTarget();
+        reportTempWhitelistChangedLocked();
+        try {
+            mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_TEMP_WHITELIST_FINISH,
+                    reason, appId);
+        } catch (RemoteException e) {
+        }
+    }
+
     public void exitIdleInternal(String reason) {
         synchronized (this) {
             becomeActiveLocked(reason, Binder.getCallingUid());
@@ -2699,9 +2752,11 @@
                 + "changes made using this won't be persisted across boots");
         pw.println("  tempwhitelist");
         pw.println("    Print packages that are temporarily whitelisted.");
-        pw.println("  tempwhitelist [-u USER] [-d DURATION] [package ..]");
-        pw.println("    Temporarily place packages in whitelist for DURATION milliseconds.");
+        pw.println("  tempwhitelist [-u USER] [-d DURATION] [-r] [package]");
+        pw.println("    Temporarily place package in whitelist for DURATION milliseconds.");
         pw.println("    If no DURATION is specified, 10 seconds is used");
+        pw.println("    If [-r] option is used, then the package is removed from temp whitelist "
+                + "and any [-d] is ignored");
     }
 
     class Shell extends ShellCommand {
@@ -2985,6 +3040,7 @@
             }
         } else if ("tempwhitelist".equals(cmd)) {
             long duration = 10000;
+            boolean removePkg = false;
             String opt;
             while ((opt=shell.getNextOption()) != null) {
                 if ("-u".equals(opt)) {
@@ -3001,16 +3057,25 @@
                         return -1;
                     }
                     duration = Long.parseLong(opt);
+                } else if ("-r".equals(opt)) {
+                    removePkg = true;
                 }
             }
             String arg = shell.getNextArg();
             if (arg != null) {
                 try {
-                    addPowerSaveTempWhitelistAppChecked(arg, duration, shell.userId, "shell");
+                    if (removePkg) {
+                        removePowerSaveTempWhitelistAppChecked(arg, shell.userId);
+                    } else {
+                        addPowerSaveTempWhitelistAppChecked(arg, duration, shell.userId, "shell");
+                    }
                 } catch (Exception e) {
                     pw.println("Failed: " + e);
                     return -1;
                 }
+            } else if (removePkg) {
+                pw.println("[-r] requires a package name");
+                return -1;
             } else {
                 dumpTempWhitelistSchedule(pw, false);
             }
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index d3ce306..6174aec 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -32,9 +32,11 @@
 # The device is being asked to go into a soft sleep (typically by the ungaze gesture).
 # It logs the time remaining before the device would've normally gone to sleep without the request.
 2731 power_soft_sleep_requested (savedwaketimems|2)
+# Power save state has changed. See BatterySaverController.java for the details.
+2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
 
 #
-# Leave IDs through 2739 for more power logs (2730 used by battery_discharge above)
+# Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
 #
 
 
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index f20ca43..f4238f2 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -282,6 +282,7 @@
         mBoundUserId = UserHandle.USER_NULL;
         if (component != null) {
             if (D) Log.d(mTag, "unbinding " + component);
+            mBoundService = null;
             mContext.unbindService(this);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 43a4aef..763478e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -44,7 +44,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
@@ -414,6 +416,7 @@
 import com.android.server.ThreadPriorityBooster;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.am.EventLogTags;
 import com.android.server.am.proto.ActivityManagerServiceProto;
 import com.android.server.am.proto.BroadcastProto;
 import com.android.server.am.proto.GrantUriProto;
@@ -632,7 +635,7 @@
     final ActivityStackSupervisor mStackSupervisor;
     private final KeyguardController mKeyguardController;
 
-    final ActivityStarter mActivityStarter;
+    private final ActivityStartController mActivityStartController;
 
     final ClientLifecycleManager mLifecycleManager;
 
@@ -1361,7 +1364,7 @@
     @GuardedBy("this") boolean mCallFinishBooting = false;
     @GuardedBy("this") boolean mBootAnimationComplete = false;
     @GuardedBy("this") boolean mLaunchWarningShown = false;
-    @GuardedBy("this") boolean mCheckedForSetup = false;
+    private @GuardedBy("this") boolean mCheckedForSetup = false;
 
     final Context mContext;
 
@@ -1707,7 +1710,6 @@
     static final int SHOW_UID_ERROR_UI_MSG = 14;
     static final int SHOW_FINGERPRINT_ERROR_UI_MSG = 15;
     static final int PROC_START_TIMEOUT_MSG = 20;
-    static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
     static final int KILL_APPLICATION_MSG = 22;
     static final int FINALIZE_PENDING_INTENT_MSG = 23;
     static final int POST_HEAVY_NOTIFICATION_MSG = 24;
@@ -2077,11 +2079,6 @@
                     processContentProviderPublishTimedOutLocked(app);
                 }
             } break;
-            case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    mActivityStarter.doPendingActivityLaunchesLocked(true);
-                }
-            } break;
             case KILL_APPLICATION_MSG: {
                 synchronized (ActivityManagerService.this) {
                     final int appId = msg.arg1;
@@ -2677,7 +2674,7 @@
         mContext = mInjector.getContext();
         mUiContext = null;
         GL_ES_VERSION = 0;
-        mActivityStarter = null;
+        mActivityStartController = null;
         mAppErrors = null;
         mAppWarnings = null;
         mAppOpsService = mInjector.getAppOpsService(null, null);
@@ -2801,7 +2798,7 @@
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
         mTaskChangeNotificationController =
                 new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
-        mActivityStarter = new ActivityStarter(this);
+        mActivityStartController = new ActivityStartController(this);
         mRecentTasks = createRecentTasks();
         mStackSupervisor.setRecentTasks(mRecentTasks);
         mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
@@ -4121,7 +4118,7 @@
                 // For ANR debugging to verify if the user activity is the one that actually
                 // launched.
                 final String myReason = reason + ":" + userId + ":" + resolvedUserId;
-                mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason);
+                mActivityStartController.startHomeActivity(intent, aInfo, myReason);
             }
         } else {
             Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
@@ -4154,49 +4151,12 @@
         return ai;
     }
 
-    /**
-     * Starts the "new version setup screen" if appropriate.
-     */
-    void startSetupActivityLocked() {
-        // Only do this once per boot.
-        if (mCheckedForSetup) {
-            return;
-        }
+    boolean getCheckedForSetup() {
+        return mCheckedForSetup;
+    }
 
-        // We will show this screen if the current one is a different
-        // version than the last one shown, and we are not running in
-        // low-level factory test mode.
-        final ContentResolver resolver = mContext.getContentResolver();
-        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL &&
-                Settings.Global.getInt(resolver,
-                        Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
-            mCheckedForSetup = true;
-
-            // See if we should be showing the platform update setup UI.
-            final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
-            final List<ResolveInfo> ris = mContext.getPackageManager().queryIntentActivities(intent,
-                    PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
-            if (!ris.isEmpty()) {
-                final ResolveInfo ri = ris.get(0);
-                String vers = ri.activityInfo.metaData != null
-                        ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
-                        : null;
-                if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
-                    vers = ri.activityInfo.applicationInfo.metaData.getString(
-                            Intent.METADATA_SETUP_VERSION);
-                }
-                String lastVers = Settings.Secure.getString(
-                        resolver, Settings.Secure.LAST_SETUP_SHOWN);
-                if (vers != null && !vers.equals(lastVers)) {
-                    intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
-                    intent.setComponent(new ComponentName(
-                            ri.activityInfo.packageName, ri.activityInfo.name));
-                    mActivityStarter.startActivityLocked(null, intent, null /*ephemeralIntent*/,
-                            null, ri.activityInfo, null /*rInfo*/, null, null, null, null, 0, 0, 0,
-                            null, 0, 0, 0, null, false, false, null, null, "startSetupActivity");
-                }
-            }
-        }
+    void setCheckedForSetup(boolean checked) {
+        mCheckedForSetup = checked;
     }
 
     CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
@@ -4562,8 +4522,8 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivity", null);
         // TODO: Switch to user app stacks here.
-        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
-                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
+        return mActivityStartController.startActivityMayWait(caller, -1, callingPackage,
+                intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                 profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser");
     }
 
@@ -4625,10 +4585,10 @@
 
         // TODO: Switch to user app stacks here.
         try {
-            int ret = mActivityStarter.startActivityMayWait(null, targetUid, targetPackage, intent,
-                    resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null,
-                    null, null, bOptions, ignoreTargetSecurity, userId, null,
-                    "startActivityAsCaller");
+            int ret = mActivityStartController.startActivityMayWait(null, targetUid,
+                    targetPackage, intent, resolvedType, null, null, resultTo, resultWho,
+                    requestCode, startFlags, null, null, null, bOptions, ignoreTargetSecurity,
+                    userId, null, "startActivityAsCaller");
             return ret;
         } catch (SecurityException e) {
             // XXX need to figure out how to propagate to original app.
@@ -4655,9 +4615,9 @@
                 userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
         WaitResult res = new WaitResult();
         // TODO: Switch to user app stacks here.
-        mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
-                null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,
-                bOptions, false, userId, null, "startActivityAndWait");
+        mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
+                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
+                profilerInfo, res, null, bOptions, false, userId, null, "startActivityAndWait");
         return res;
     }
 
@@ -4669,9 +4629,9 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
         // TODO: Switch to user app stacks here.
-        int ret = mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
-                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                null, null, config, bOptions, false, userId, null, "startActivityWithConfig");
+        int ret = mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
+                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null, null,
+                config, bOptions, false, userId, null, "startActivityWithConfig");
         return ret;
     }
 
@@ -4718,9 +4678,9 @@
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
                 ALLOW_FULL_ONLY, "startVoiceActivity", null);
         // TODO: Switch to user app stacks here.
-        return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
-                resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo, null,
-                null, bOptions, false, userId, null, "startVoiceActivity");
+        return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
+                intent, resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo,
+                null, null, bOptions, false, userId, null, "startVoiceActivity");
     }
 
     @Override
@@ -4729,9 +4689,9 @@
         enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
                 ALLOW_FULL_ONLY, "startAssistantActivity", null);
-        return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
-                resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions, false,
-                userId, null, "startAssistantActivity");
+        return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
+                intent, resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions,
+                false, userId, null, "startAssistantActivity");
     }
 
     @Override
@@ -4771,9 +4731,9 @@
                 intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
                 intent.setComponent(recentsComponent);
                 intent.putExtras(options);
-                return mActivityStarter.startActivityMayWait(null, recentsUid, recentsPackage,
-                        intent, null, null, null, null, null, 0, 0, null, null, null, activityOptions,
-                        false, userId, null, "startRecentsActivity");
+                return mActivityStartController.startActivityMayWait(null, recentsUid,
+                        recentsPackage, intent, null, null, null, null, null, 0, 0, null, null,
+                        null, activityOptions, false, userId, null, "startRecentsActivity");
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4946,7 +4906,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            int res = mActivityStarter.startActivityLocked(r.app.thread, intent,
+            int res = mActivityStartController.startActivity(r.app.thread, intent,
                     null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
                     null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
                     r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
@@ -4976,20 +4936,6 @@
         }
     }
 
-    final int startActivityInPackage(int uid, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId,
-            TaskRecord inTask, String reason) {
-
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
-
-        // TODO: Switch to user app stacks here.
-        return mActivityStarter.startActivityMayWait(null, uid, callingPackage, intent,
-                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                null, null, null, bOptions, false, userId, inTask, reason);
-    }
-
     @Override
     public final int startActivities(IApplicationThread caller, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
@@ -4999,21 +4945,8 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, reason, null);
         // TODO: Switch to user app stacks here.
-        int ret = mActivityStarter.startActivities(caller, -1, callingPackage, intents,
-                resolvedTypes, resultTo, bOptions, userId, reason);
-        return ret;
-    }
-
-    final int startActivitiesInPackage(int uid, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            Bundle bOptions, int userId) {
-
-        final String reason = "startActivityInPackage";
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, false, ALLOW_FULL_ONLY, reason, null);
-        // TODO: Switch to user app stacks here.
-        int ret = mActivityStarter.startActivities(null, uid, callingPackage, intents, resolvedTypes,
-                resultTo, bOptions, userId, reason);
+        int ret = mActivityStartController.startActivities(caller, -1, callingPackage,
+                intents, resolvedTypes, resultTo, bOptions, userId, reason);
         return ret;
     }
 
@@ -6667,7 +6600,7 @@
                 ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
                 packageName == null ? ("stop user " + userId) : ("stop " + packageName));
 
-        didSomething |= mActivityStarter.clearPendingActivityLaunchesLocked(packageName);
+        didSomething |= mActivityStartController.clearPendingActivityLaunches(packageName);
 
         if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
                 packageName, null, doit, evenPersistent, userId)) {
@@ -10501,7 +10434,7 @@
     public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
         if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
-                    toTop, ANIMATE, null /* initialBounds */);
+                    toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
             return;
         }
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
@@ -10547,10 +10480,12 @@
      * @param animate Whether we should play an animation for the moving the task.
      * @param initialBounds If the primary stack gets created, it will use these bounds for the
      *                      stack. Pass {@code null} to use default bounds.
+     * @param showRecents If the recents activity should be shown on the other side of the task
+     *                    going into split-screen mode.
      */
     @Override
     public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
-            boolean animate, Rect initialBounds) {
+            boolean animate, Rect initialBounds, boolean showRecents) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
                 "setTaskWindowingModeSplitScreenPrimary()");
         synchronized (this) {
@@ -10575,7 +10510,7 @@
                 if (toTop) {
                     stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
                 }
-                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate);
+                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents);
                 return windowingMode != task.getWindowingMode();
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -11755,6 +11690,10 @@
         return AppGlobals.getPackageManager();
     }
 
+    ActivityStartController getActivityStartController() {
+        return mActivityStartController;
+    }
+
     PackageManagerInternal getPackageManagerInternalLocked() {
         if (mPackageManagerInt == null) {
             mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
@@ -12604,7 +12543,16 @@
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mActivityStarter.startConfirmCredentialIntent(intent, options);
+                intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
+                        FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                        FLAG_ACTIVITY_TASK_ON_HOME);
+                ActivityOptions activityOptions = options != null
+                        ? new ActivityOptions(options)
+                        : ActivityOptions.makeBasic();
+                activityOptions.setLaunchTaskId(
+                        mStackSupervisor.getHomeActivity().getTask().taskId);
+                mContext.startActivityAsUser(intent, activityOptions.toBundle(),
+                        UserHandle.CURRENT);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -12623,9 +12571,7 @@
             mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
                     + APP_SWITCH_DELAY_TIME;
             mDidAppSwitch = false;
-            mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
-            Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
-            mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);
+            mActivityStartController.schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
         }
     }
 
@@ -14596,7 +14542,7 @@
                 try {
                     PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
                     if (pi != null) {
-                        sb.append(" v").append(pi.versionCode);
+                        sb.append(" v").append(pi.getLongVersionCode());
                         if (pi.versionName != null) {
                             sb.append(" (").append(pi.versionName).append(")");
                         }
@@ -15491,7 +15437,7 @@
 
     private void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
         pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
-        mActivityStarter.dump(pw, "", dumpPackage);
+        mActivityStartController.dump(pw, "", dumpPackage);
     }
 
     void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -24248,8 +24194,8 @@
             }
 
             synchronized (ActivityManagerService.this) {
-                return startActivitiesInPackage(packageUid, packageName, intents, resolvedTypes,
-                        /*resultTo*/ null, bOptions, userId);
+                return mActivityStartController.startActivitiesInPackage(packageUid, packageName,
+                        intents, resolvedTypes, /*resultTo*/ null, bOptions, userId);
             }
         }
 
@@ -24400,7 +24346,7 @@
                     pw.println("  Reason: " + reason);
                 }
                 pw.println();
-                mActivityStarter.dump(pw, "  ", null);
+                mActivityStartController.dump(pw, "  ", null);
                 pw.println();
                 pw.println("-------------------------------------------------------------------------------");
                 dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 7eb922c..979323f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -224,6 +224,8 @@
                     return runGetInactive(pw);
                 case "set-standby-bucket":
                     return runSetStandbyBucket(pw);
+                case "get-standby-bucket":
+                    return runGetStandbyBucket(pw);
                 case "send-trim-memory":
                     return runSendTrimMemory(pw);
                 case "display":
@@ -1826,6 +1828,29 @@
         return 0;
     }
 
+    private int bucketNameToBucketValue(String name) {
+        String lower = name.toLowerCase();
+        if (lower.startsWith("ac")) {
+            return UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+        } else if (lower.startsWith("wo")) {
+            return UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+        } else if (lower.startsWith("fr")) {
+            return UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+        } else if (lower.startsWith("ra")) {
+            return UsageStatsManager.STANDBY_BUCKET_RARE;
+        } else if (lower.startsWith("ne")) {
+            return UsageStatsManager.STANDBY_BUCKET_NEVER;
+        } else {
+            try {
+                int bucket = Integer.parseInt(lower);
+                return bucket;
+            } catch (NumberFormatException nfe) {
+                getErrPrintWriter().println("Error: Unknown bucket: " + name);
+            }
+        }
+        return -1;
+    }
+
     int runSetStandbyBucket(PrintWriter pw) throws RemoteException {
         int userId = UserHandle.USER_CURRENT;
 
@@ -1840,10 +1865,33 @@
         }
         String packageName = getNextArgRequired();
         String value = getNextArgRequired();
+        int bucket = bucketNameToBucketValue(value);
+        if (bucket < 0) return -1;
 
         IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
                 Context.USAGE_STATS_SERVICE));
-        usm.setAppStandbyBucket(packageName, Integer.parseInt(value), userId);
+        usm.setAppStandbyBucket(packageName, bucketNameToBucketValue(value), userId);
+        return 0;
+    }
+
+    int runGetStandbyBucket(PrintWriter pw) throws RemoteException {
+        int userId = UserHandle.USER_CURRENT;
+
+        String opt;
+        while ((opt=getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                getErrPrintWriter().println("Error: Unknown option: " + opt);
+                return -1;
+            }
+        }
+        String packageName = getNextArgRequired();
+
+        IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+                Context.USAGE_STATS_SERVICE));
+        int bucket = usm.getAppStandbyBucket(packageName, null, userId);
+        pw.println(bucket);
         return 0;
     }
 
@@ -2597,8 +2645,10 @@
             pw.println("      Sets the inactive state of an app.");
             pw.println("  get-inactive [--user <USER_ID>] <PACKAGE>");
             pw.println("      Returns the inactive state of an app.");
-            pw.println("  set-standby-bucket [--user <USER_ID>] <PACKAGE> <BUCKET>");
+            pw.println("  set-standby-bucket [--user <USER_ID>] <PACKAGE> active|working_set|frequent|rare");
             pw.println("      Puts an app in the standby bucket.");
+            pw.println("  get-standby-bucket [--user <USER_ID>] <PACKAGE>");
+            pw.println("      Returns the standby bucket of an app.");
             pw.println("  send-trim-memory [--user <USER_ID>] <PROCESS>");
             pw.println("          [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]");
             pw.println("      Send a memory trim event to a <PROCESS>.  May also supply a raw trim int level.");
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bdfd82f..af4d3f8 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -144,6 +144,7 @@
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
+import com.android.server.am.EventLogTags;
 import com.android.server.wm.ConfigurationContainer;
 import com.android.server.wm.StackWindowController;
 import com.android.server.wm.StackWindowListener;
@@ -482,10 +483,10 @@
 
     @Override
     public void setWindowingMode(int windowingMode) {
-        setWindowingMode(windowingMode, false /* animate */);
+        setWindowingMode(windowingMode, false /* animate */, true /* showRecents */);
     }
 
-    void setWindowingMode(int preferredWindowingMode, boolean animate) {
+    void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents) {
         final int currentMode = getWindowingMode();
         final ActivityDisplay display = getDisplay();
         final TaskRecord topTask = topTask();
@@ -579,7 +580,7 @@
                 resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
             }
         } finally {
-            if (!alreadyInSplitScreenMode && mDisplayId == DEFAULT_DISPLAY
+            if (showRecents && !alreadyInSplitScreenMode && mDisplayId == DEFAULT_DISPLAY
                     && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 // Make sure recents stack exist when creating a dock stack as it normally needs to
                 // be on the other side of the docked stack and we make visibility decisions based
@@ -1424,9 +1425,8 @@
         if (prev.app != null && prev.app.thread != null) {
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
             try {
-                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
-                        prev.userId, System.identityHashCode(prev),
-                        prev.shortComponentName);
+                EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
+                        prev.shortComponentName, "userLeaving=" + userLeaving);
                 mService.updateUsageStats(prev, false);
 
                 mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken,
@@ -2260,7 +2260,7 @@
 
         // Remember how we'll process this pause/resume situation, and ensure
         // that the state is reset however we wind up proceeding.
-        final boolean userLeaving = mStackSupervisor.mUserLeaving;
+        boolean userLeaving = mStackSupervisor.mUserLeaving;
         mStackSupervisor.mUserLeaving = false;
 
         if (!hasRunningActivity) {
@@ -2331,6 +2331,13 @@
             // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
             // represent the last resumed activity. However, the last focus stack does if it isn't null.
             final ActivityRecord lastResumed = lastFocusedStack.mResumedActivity;
+            if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
+                // The user isn't leaving if this stack is the multi-window mode and the last
+                // focused stack should still be visible.
+                if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
+                        + " next=" + next + " lastResumed=" + lastResumed);
+                userLeaving = false;
+            }
             lastResumedCanPip = lastResumed != null && lastResumed.checkEnterPictureInPictureState(
                     "resumeTopActivity", userLeaving /* beforeStopping */);
         }
@@ -3892,11 +3899,12 @@
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), 0, srec.userId);
-                    int res = mService.mActivityStarter.startActivityLocked(srec.app.thread,
-                            destIntent, null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null,
-                            null, parent.appToken, null, 0, -1, parent.launchedFromUid,
-                            parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
-                            false, true, null, null, "navigateUpTo");
+                    int res = mService.getActivityStartController().startActivity(
+                            srec.app.thread, destIntent, null /*ephemeralIntent*/, null, aInfo,
+                            null /*rInfo*/, null, null, parent.appToken, null, 0, -1,
+                            parent.launchedFromUid, parent.launchedFromPackage, -1,
+                            parent.launchedFromUid, 0, null, false, true, null, null,
+                            "navigateUpTo");
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
                 } catch (RemoteException e) {
                     foundParentInTask = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 445bf67..48c08a5 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -129,7 +129,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
+import android.hardware.power.V1_0.PowerHint;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
@@ -364,6 +364,9 @@
      * is being brought in front of us. */
     boolean mUserLeaving = false;
 
+    /** Set when a power hint has started, but not ended. */
+    private boolean mPowerHintSent;
+
     /**
      * We don't want to allow the device to go to sleep while in the process
      * of launching an activity.  This is primarily to allow alarm intent
@@ -978,7 +981,7 @@
             }
         }
         // Send launch end powerhint when idle
-        mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
+        sendPowerHintForLaunchEndIfNeeded();
         return true;
     }
 
@@ -1470,7 +1473,7 @@
         // a chance to initialize itself while in the background, making the
         // switch back to it faster and look better.
         if (isFocusedStack(stack)) {
-            mService.startSetupActivityLocked();
+            mService.getActivityStartController().startSetupActivity();
         }
 
         // Update any services we are bound to that might care about whether
@@ -1531,6 +1534,32 @@
                 "activity", r.intent.getComponent(), false, false, true);
     }
 
+    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
+        boolean sendHint = forceSend;
+
+        if (!sendHint) {
+            // If not forced, send power hint when the activity's process is different than the
+            // current resumed activity.
+            final ActivityRecord resumedActivity = getResumedActivityLocked();
+            sendHint = resumedActivity == null
+                    || resumedActivity.app == null
+                    || !resumedActivity.app.equals(targetActivity.app);
+        }
+
+        if (sendHint && mService.mLocalPowerManager != null) {
+            mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
+            mPowerHintSent = true;
+        }
+    }
+
+    void sendPowerHintForLaunchEndIfNeeded() {
+        // Trigger launch power hint if activity is launched
+        if (mPowerHintSent && mService.mLocalPowerManager != null) {
+            mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
+            mPowerHintSent = false;
+        }
+    }
+
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
             String resultWho, int requestCode, int callingPid, int callingUid,
             String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
@@ -2481,14 +2510,14 @@
         }
     }
 
-    private void deferUpdateBounds(int activityType) {
+    void deferUpdateBounds(int activityType) {
         final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
         if (stack != null) {
             stack.deferUpdateBounds();
         }
     }
 
-    private void continueUpdateBounds(int activityType) {
+    void continueUpdateBounds(int activityType) {
         final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
         if (stack != null) {
             stack.continueUpdateBounds();
@@ -3335,7 +3364,7 @@
         }
 
         // Send launch end powerhint before going sleep
-        mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
+        sendPowerHintForLaunchEndIfNeeded();
 
         removeSleepTimeouts();
 
@@ -4473,7 +4502,7 @@
      *
      * @param task The task to put into resizing mode
      */
-    private void setResizingDuringAnimation(TaskRecord task) {
+    void setResizingDuringAnimation(TaskRecord task) {
         mResizingTasksDuringAnimation.add(task.taskId);
         task.setTaskDockedResizing(true);
     }
@@ -4532,8 +4561,7 @@
                     && task.getRootActivity() != null) {
                 final ActivityRecord targetActivity = task.getTopActivity();
 
-                mService.mActivityStarter.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
-                        targetActivity);
+                sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
                 mActivityMetricsLogger.notifyActivityLaunching();
                 try {
                     mService.moveTaskToFrontLocked(task.taskId, 0, bOptions,
@@ -4550,8 +4578,9 @@
                     setResizingDuringAnimation(task);
                 }
 
-                mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(),
-                        ActivityManager.START_TASK_TO_FRONT, task.getStack());
+                mService.getActivityStartController().postStartActivityProcessingForLastStarter(
+                        task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT,
+                        task.getStack());
                 return ActivityManager.START_TASK_TO_FRONT;
             }
             callingUid = task.mCallingUid;
@@ -4559,8 +4588,9 @@
             intent = task.intent;
             intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
             userId = task.userId;
-            int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
-                    null, null, 0, 0, bOptions, userId, task, "startActivityFromRecents");
+            int result = mService.getActivityStartController().startActivityInPackage(callingUid,
+                    callingPackage, intent, null, null, null, 0, 0, bOptions, userId, task,
+                    "startActivityFromRecents");
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 setResizingDuringAnimation(task);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
new file mode 100644
index 0000000..317a68f
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
+
+import android.app.ActivityOptions;
+import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
+import android.app.WaitResult;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.FactoryTest;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.ActivityStarter.DefaultFactory;
+import com.android.server.am.ActivityStarter.Factory;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controller for delegating activity launches.
+ *
+ * This class' main objective is to take external activity start requests and prepare them into
+ * a series of discrete activity launches that can be handled by an {@link ActivityStarter}. It is
+ * also responsible for handling logic that happens around an activity launch, but doesn't
+ * necessarily influence the activity start. Examples include power hint management, processing
+ * through the pending activity list, and recording home activity launches.
+ */
+public class ActivityStartController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_AM;
+
+    private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
+
+    private final ActivityManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
+    private final ActivityStartInterceptor mInterceptor;
+
+    /** Last home activity record we attempted to start. */
+    private ActivityRecord mLastHomeActivityStartRecord;
+
+    /** Temporary array to capture start activity results */
+    private ActivityRecord[] tmpOutRecord = new ActivityRecord[1];
+
+    /**The result of the last home activity we attempted to start. */
+    private int mLastHomeActivityStartResult;
+
+    /** A list of activities that are waiting to launch. */
+    private final ArrayList<ActivityStackSupervisor.PendingActivityLaunch>
+            mPendingActivityLaunches = new ArrayList<>();
+
+    private final Factory mFactory;
+
+    private final Handler mHandler;
+
+    private final class StartHandler extends Handler {
+        public StartHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case DO_PENDING_ACTIVITY_LAUNCHES_MSG:
+                    synchronized (mService) {
+                        doPendingActivityLaunches(true);
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * TODO(b/64750076): Capture information necessary for dump and
+     * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
+     * around */
+    private ActivityStarter mLastStarter;
+
+    ActivityStartController(ActivityManagerService service) {
+        this(service, service.mStackSupervisor, new DefaultFactory());
+    }
+
+    @VisibleForTesting
+    ActivityStartController(ActivityManagerService service, ActivityStackSupervisor supervisor,
+            Factory factory) {
+        mService = service;
+        mSupervisor = supervisor;
+        mHandler = new StartHandler(mService.mHandlerThread.getLooper());
+        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
+        mFactory = factory;
+    }
+
+    /**
+     * Retrieves a starter to be used for a new start request. The starter will be added to the
+     * active starters list.
+     *
+     * TODO(b/64750076): This should be removed when {@link #obtainStarter} is implemented. At that
+     * time, {@link ActivityStarter#execute} will be able to handle cleaning up the starter's
+     * internal references.
+     */
+    private ActivityStarter createStarter() {
+        mLastStarter = mFactory.getStarter(this, mService, mService.mStackSupervisor, mInterceptor);
+        return mLastStarter;
+    }
+
+    /**
+     * TODO(b/64750076): Remove once we directly expose starter interface to callers through
+     * {@link #obtainStarter}.
+     */
+    int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+            ActivityRecord[] outActivity, TaskRecord inTask, String reason) {
+        return createStarter().startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
+                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
+                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
+                options, ignoreTargetSecurity, componentSpecified, outActivity, inTask, reason);
+    }
+
+    /**
+     * TODO(b/64750076): usage of this doesn't seem right. We're making decisions based off the
+     * last starter for an arbitrary task record. Re-evaluate whether we can remove.
+     */
+    void postStartActivityProcessingForLastStarter(ActivityRecord r, int result,
+            ActivityStack targetStack) {
+        mLastStarter.postStartActivityProcessing(r, result, targetStack);
+    }
+
+    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
+        mSupervisor.moveHomeStackTaskToTop(reason);
+
+        final ActivityStarter starter = createStarter();
+
+        mLastHomeActivityStartResult = starter.startActivityLocked(null /*caller*/, intent,
+                null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
+                null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
+                null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
+                null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
+                0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
+                false /*componentSpecified*/, tmpOutRecord, null /*inTask*/,
+                "startHomeActivity: " + reason);
+        mLastHomeActivityStartRecord = tmpOutRecord[0];
+
+        if (mSupervisor.inResumeTopActivity) {
+            // If we are in resume section already, home activity will be initialized, but not
+            // resumed (to avoid recursive resume) and will stay that way until something pokes it
+            // again. We need to schedule another resume.
+            mSupervisor.scheduleResumeTopActivities();
+        }
+    }
+
+    /**
+     * Starts the "new version setup screen" if appropriate.
+     */
+    void startSetupActivity() {
+        // Only do this once per boot.
+        if (mService.getCheckedForSetup()) {
+            return;
+        }
+
+        // We will show this screen if the current one is a different
+        // version than the last one shown, and we are not running in
+        // low-level factory test mode.
+        final ContentResolver resolver = mService.mContext.getContentResolver();
+        if (mService.mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL &&
+                Settings.Global.getInt(resolver,
+                        Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
+            mService.setCheckedForSetup(true);
+
+            // See if we should be showing the platform update setup UI.
+            final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
+            final List<ResolveInfo> ris = mService.mContext.getPackageManager()
+                    .queryIntentActivities(intent,
+                            PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
+            if (!ris.isEmpty()) {
+                final ResolveInfo ri = ris.get(0);
+                String vers = ri.activityInfo.metaData != null
+                        ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
+                        : null;
+                if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
+                    vers = ri.activityInfo.applicationInfo.metaData.getString(
+                            Intent.METADATA_SETUP_VERSION);
+                }
+                String lastVers = Settings.Secure.getString(
+                        resolver, Settings.Secure.LAST_SETUP_SHOWN);
+                if (vers != null && !vers.equals(lastVers)) {
+                    intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+                    intent.setComponent(new ComponentName(
+                            ri.activityInfo.packageName, ri.activityInfo.name));
+                    startActivity(null, intent, null /*ephemeralIntent*/, null, ri.activityInfo,
+                            null /*rInfo*/, null, null, null, null, 0, 0, 0, null, 0, 0, 0, null,
+                            false, false, null, null, "startSetupActivity");
+                }
+            }
+        }
+    }
+
+    final int startActivityInPackage(int uid, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId,
+            TaskRecord inTask, String reason) {
+
+        userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivityInPackage",
+                null);
+
+        // TODO: Switch to user app stacks here.
+        return startActivityMayWait(null, uid, callingPackage,
+                intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
+                null, null, null, bOptions, false, userId, inTask, reason);
+    }
+
+    final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId) {
+        final String reason = "startActivityInPackage";
+        userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, reason, null);
+        // TODO: Switch to user app stacks here.
+        int ret = startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo,
+                bOptions, userId, reason);
+        return ret;
+    }
+
+    /**
+     * TODO(b/64750076): Remove once we directly expose starter interface to callers through
+     * {@link #obtainStarter}.
+     */
+    int startActivityMayWait(IApplicationThread caller, int callingUid,
+            String callingPackage, Intent intent, String resolvedType,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags,
+            ProfilerInfo profilerInfo, WaitResult outResult,
+            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
+            TaskRecord inTask, String reason) {
+        return createStarter().startActivityMayWait(caller, callingUid, callingPackage, intent,
+                resolvedType, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
+                startFlags, profilerInfo, outResult, globalConfig, bOptions,
+                ignoreTargetSecurity,
+                userId, inTask, reason);
+    }
+
+    int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId,
+            String reason) {
+        if (intents == null) {
+            throw new NullPointerException("intents is null");
+        }
+        if (resolvedTypes == null) {
+            throw new NullPointerException("resolvedTypes is null");
+        }
+        if (intents.length != resolvedTypes.length) {
+            throw new IllegalArgumentException("intents are length different than resolvedTypes");
+        }
+
+        final int realCallingPid = Binder.getCallingPid();
+        final int realCallingUid = Binder.getCallingUid();
+
+        int callingPid;
+        if (callingUid >= 0) {
+            callingPid = -1;
+        } else if (caller == null) {
+            callingPid = realCallingPid;
+            callingUid = realCallingUid;
+        } else {
+            callingPid = callingUid = -1;
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mService) {
+                ActivityRecord[] outActivity = new ActivityRecord[1];
+                for (int i=0; i < intents.length; i++) {
+                    Intent intent = intents[i];
+                    if (intent == null) {
+                        continue;
+                    }
+
+                    // Refuse possible leaked file descriptors
+                    if (intent != null && intent.hasFileDescriptors()) {
+                        throw new IllegalArgumentException("File descriptors passed in Intent");
+                    }
+
+                    boolean componentSpecified = intent.getComponent() != null;
+
+                    // Don't modify the client's object!
+                    intent = new Intent(intent);
+
+                    // Collect information about the target of the Intent.
+                    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
+                            null, userId);
+                    // TODO: New, check if this is correct
+                    aInfo = mService.getActivityInfoForUser(aInfo, userId);
+
+                    if (aInfo != null &&
+                            (aInfo.applicationInfo.privateFlags
+                                    & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)  != 0) {
+                        throw new IllegalArgumentException(
+                                "FLAG_CANT_SAVE_STATE not supported here");
+                    }
+
+                    ActivityOptions options = ActivityOptions.fromBundle(
+                            i == intents.length - 1 ? bOptions : null);
+                    int res = startActivity(caller, intent, null /*ephemeralIntent*/,
+                            resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
+                            callingPid, callingUid, callingPackage,
+                            realCallingPid, realCallingUid, 0,
+                            options, false, componentSpecified, outActivity, null, reason);
+                    if (res < 0) {
+                        return res;
+                    }
+
+                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return START_SUCCESS;
+    }
+
+    void schedulePendingActivityLaunches(long delayMs) {
+        mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+        Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+        mHandler.sendMessageDelayed(msg, delayMs);
+    }
+
+    void doPendingActivityLaunches(boolean doResume) {
+        while (!mPendingActivityLaunches.isEmpty()) {
+            final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+            final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
+            final ActivityStarter starter = createStarter();
+            try {
+                starter.startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume,
+                        null, null, null /*outRecords*/);
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
+                pal.sendErrorResult(e.getMessage());
+            }
+        }
+    }
+
+    void addPendingActivityLaunch(PendingActivityLaunch launch) {
+        mPendingActivityLaunches.add(launch);
+    }
+
+    boolean clearPendingActivityLaunches(String packageName) {
+        final int pendingLaunches = mPendingActivityLaunches.size();
+
+        for (int palNdx = pendingLaunches - 1; palNdx >= 0; --palNdx) {
+            final PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+            final ActivityRecord r = pal.r;
+            if (r != null && r.packageName.equals(packageName)) {
+                mPendingActivityLaunches.remove(palNdx);
+            }
+        }
+        return mPendingActivityLaunches.size() < pendingLaunches;
+    }
+
+    void dump(PrintWriter pw, String prefix, String dumpPackage) {
+        pw.print(prefix);
+        pw.print("mLastHomeActivityStartResult=");
+        pw.println(mLastHomeActivityStartResult);
+
+        if (mLastHomeActivityStartRecord != null) {
+            pw.print(prefix);
+            pw.println("mLastHomeActivityStartRecord:");
+            mLastHomeActivityStartRecord.dump(pw, prefix + "  ");
+        }
+
+        final boolean dumpPackagePresent = dumpPackage != null;
+
+        if (mLastStarter != null) {
+            final boolean dump = !dumpPackagePresent
+                    || mLastStarter.relatedToPackage(dumpPackage)
+                    || (mLastHomeActivityStartRecord != null
+                            && dumpPackage.equals(mLastHomeActivityStartRecord.packageName));
+
+            if (dump) {
+                pw.print(prefix);
+                mLastStarter.dump(pw, prefix + "  ");
+
+                if (dumpPackagePresent) {
+                    return;
+                }
+            }
+        }
+
+        if (dumpPackagePresent) {
+            pw.print(prefix);
+            pw.println("(nothing)");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 2fc5dda..3bee4228 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -33,7 +33,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
@@ -45,7 +44,6 @@
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -55,7 +53,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -77,9 +74,9 @@
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.AppGlobals;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
 import android.app.ProfilerInfo;
@@ -96,7 +93,6 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.hardware.power.V1_0.PowerHint;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -109,6 +105,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
@@ -116,11 +113,10 @@
 
 import java.io.PrintWriter;
 import java.text.DateFormat;
-import java.util.ArrayList;
 import java.util.Date;
 
 /**
- * Controller for interpreting how and then launching activities.
+ * Controller for interpreting how and then launching an activity.
  *
  * This class collects all the logic for determining how an intent and flags should be turned into
  * an activity and associated task and stack.
@@ -137,7 +133,7 @@
     private final ActivityStackSupervisor mSupervisor;
     private final ActivityStartInterceptor mInterceptor;
 
-    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
+    private final ActivityStartController mController;
 
     // Share state variable among methods when starting an activity.
     private ActivityRecord mStartActivity;
@@ -171,7 +167,6 @@
     private boolean mNoAnimation;
     private boolean mKeepCurTransition;
     private boolean mAvoidMoveToFront;
-    private boolean mPowerHintSent;
 
     // We must track when we deliver the new intent since multiple code paths invoke
     // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
@@ -182,10 +177,6 @@
     private IVoiceInteractionSession mVoiceSession;
     private IVoiceInteractor mVoiceInteractor;
 
-    // Last home activity record we attempted to start
-    private final ActivityRecord[] mLastHomeActivityStartRecord = new ActivityRecord[1];
-    // The result of the last home activity we attempted to start.
-    private int mLastHomeActivityStartResult;
     // Last activity record we attempted to start
     private final ActivityRecord[] mLastStartActivityRecord = new ActivityRecord[1];
     // The result of the last activity we attempted to start.
@@ -195,48 +186,49 @@
     // The reason we were trying to start the last activity
     private String mLastStartReason;
 
-    private void reset() {
-        mStartActivity = null;
-        mIntent = null;
-        mCallingUid = -1;
-        mOptions = null;
-
-        mLaunchTaskBehind = false;
-        mLaunchFlags = 0;
-        mLaunchMode = INVALID_LAUNCH_MODE;
-
-        mLaunchBounds.setEmpty();
-
-        mNotTop = null;
-        mDoResume = false;
-        mStartFlags = 0;
-        mSourceRecord = null;
-        mPreferredDisplayId = INVALID_DISPLAY;
-
-        mInTask = null;
-        mAddingToTask = false;
-        mReuseTask = null;
-
-        mNewTaskInfo = null;
-        mNewTaskIntent = null;
-        mSourceStack = null;
-
-        mTargetStack = null;
-        mMovedToFront = false;
-        mNoAnimation = false;
-        mKeepCurTransition = false;
-        mAvoidMoveToFront = false;
-
-        mVoiceSession = null;
-        mVoiceInteractor = null;
-
-        mIntentDelivered = false;
+    /**
+     * An interface that to provide {@link ActivityStarter} instances to the controller. This is
+     * used by tests to inject their own starter implementations for verification purposes.
+     */
+    @VisibleForTesting
+    interface Factory {
+        /**
+         * Generates an {@link ActivityStarter} that is ready to handle a new start request.
+         * @param controller The {@link ActivityStartController} which the starter who will own
+         *                   this instance.
+         * @return an {@link ActivityStarter}
+         */
+        ActivityStarter getStarter(ActivityStartController controller,
+                ActivityManagerService service, ActivityStackSupervisor supervisor,
+                ActivityStartInterceptor interceptor);
     }
 
-    ActivityStarter(ActivityManagerService service) {
+    /**
+     * Default implementation of {@link StarterFactory}.
+     */
+    static class DefaultFactory implements Factory {
+        @Override
+        public ActivityStarter getStarter(ActivityStartController controller,
+                ActivityManagerService service, ActivityStackSupervisor supervisor,
+                ActivityStartInterceptor interceptor) {
+            // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
+            return new ActivityStarter(controller, service, supervisor, interceptor);
+        }
+    }
+
+    ActivityStarter(ActivityStartController controller, ActivityManagerService service,
+            ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
+        mController = controller;
         mService = service;
-        mSupervisor = mService.mStackSupervisor;
-        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
+        mSupervisor = supervisor;
+        mInterceptor = interceptor;
+    }
+
+    boolean relatedToPackage(String packageName) {
+        return (mLastStartActivityRecord[0] != null
+                        && packageName.equals(mLastStartActivityRecord[0].packageName))
+                || (mStartActivity != null
+                        && packageName.equals(mStartActivity.packageName));
     }
 
     int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
@@ -268,7 +260,7 @@
         return getExternalResult(mLastStartActivityResult);
     }
 
-    public static int getExternalResult(int result) {
+    static int getExternalResult(int result) {
         // Aborted results are treated as successes externally, but we must track them internally.
         return result != START_ABORTED ? result : START_SUCCESS;
     }
@@ -536,9 +528,8 @@
                 || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                     realCallingPid, realCallingUid, "Activity start")) {
-                PendingActivityLaunch pal =  new PendingActivityLaunch(r,
-                        sourceRecord, startFlags, stack, callerApp);
-                mPendingActivityLaunches.add(pal);
+                mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
+                        sourceRecord, startFlags, stack, callerApp));
                 ActivityOptions.abort(options);
                 return ActivityManager.START_SWITCHES_CANCELED;
             }
@@ -555,7 +546,7 @@
             mService.mDidAppSwitch = true;
         }
 
-        doPendingActivityLaunchesLocked(false);
+        mController.doPendingActivityLaunches(false);
 
         return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
                 options, inTask, outActivity);
@@ -582,7 +573,6 @@
     }
 
     void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
-
         if (ActivityManager.isStartResultFatalError(result)) {
             return;
         }
@@ -620,34 +610,6 @@
         }
     }
 
-    void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
-        mSupervisor.moveHomeStackTaskToTop(reason);
-        mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent,
-                null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
-                null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
-                null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
-                null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
-                0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
-                false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/,
-                null /*inTask*/, "startHomeActivity: " + reason);
-        if (mSupervisor.inResumeTopActivity) {
-            // If we are in resume section already, home activity will be initialized, but not
-            // resumed (to avoid recursive resume) and will stay that way until something pokes it
-            // again. We need to schedule another resume.
-            mSupervisor.scheduleResumeTopActivities();
-        }
-    }
-
-    void startConfirmCredentialIntent(Intent intent, Bundle optionsBundle) {
-        intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
-                FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
-                FLAG_ACTIVITY_TASK_ON_HOME);
-        ActivityOptions options = (optionsBundle != null ? new ActivityOptions(optionsBundle)
-                        : ActivityOptions.makeBasic());
-        options.setLaunchTaskId(mSupervisor.getHomeActivity().getTask().taskId);
-        mService.mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
-    }
-
     final int startActivityMayWait(IApplicationThread caller, int callingUid,
             String callingPackage, Intent intent, String resolvedType,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
@@ -858,112 +820,7 @@
         }
     }
 
-    final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            Bundle bOptions, int userId, String reason) {
-        if (intents == null) {
-            throw new NullPointerException("intents is null");
-        }
-        if (resolvedTypes == null) {
-            throw new NullPointerException("resolvedTypes is null");
-        }
-        if (intents.length != resolvedTypes.length) {
-            throw new IllegalArgumentException("intents are length different than resolvedTypes");
-        }
-
-        final int realCallingPid = Binder.getCallingPid();
-        final int realCallingUid = Binder.getCallingUid();
-
-        int callingPid;
-        if (callingUid >= 0) {
-            callingPid = -1;
-        } else if (caller == null) {
-            callingPid = realCallingPid;
-            callingUid = realCallingUid;
-        } else {
-            callingPid = callingUid = -1;
-        }
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mService) {
-                ActivityRecord[] outActivity = new ActivityRecord[1];
-                for (int i=0; i<intents.length; i++) {
-                    Intent intent = intents[i];
-                    if (intent == null) {
-                        continue;
-                    }
-
-                    // Refuse possible leaked file descriptors
-                    if (intent != null && intent.hasFileDescriptors()) {
-                        throw new IllegalArgumentException("File descriptors passed in Intent");
-                    }
-
-                    boolean componentSpecified = intent.getComponent() != null;
-
-                    // Don't modify the client's object!
-                    intent = new Intent(intent);
-
-                    // Collect information about the target of the Intent.
-                    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
-                            null, userId);
-                    // TODO: New, check if this is correct
-                    aInfo = mService.getActivityInfoForUser(aInfo, userId);
-
-                    if (aInfo != null &&
-                            (aInfo.applicationInfo.privateFlags
-                                    & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)  != 0) {
-                        throw new IllegalArgumentException(
-                                "FLAG_CANT_SAVE_STATE not supported here");
-                    }
-
-                    ActivityOptions options = ActivityOptions.fromBundle(
-                            i == intents.length - 1 ? bOptions : null);
-                    int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/,
-                            resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
-                            callingPid, callingUid, callingPackage,
-                            realCallingPid, realCallingUid, 0,
-                            options, false, componentSpecified, outActivity, null, reason);
-                    if (res < 0) {
-                        return res;
-                    }
-
-                    resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-
-        return START_SUCCESS;
-    }
-
-    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
-        boolean sendHint = forceSend;
-
-        if (!sendHint) {
-            // If not forced, send power hint when the activity's process is different than the
-            // current resumed activity.
-            final ActivityRecord resumedActivity = mSupervisor.getResumedActivityLocked();
-            sendHint = resumedActivity == null
-                || resumedActivity.app == null
-                || !resumedActivity.app.equals(targetActivity.app);
-        }
-
-        if (sendHint && mService.mLocalPowerManager != null) {
-            mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
-            mPowerHintSent = true;
-        }
-    }
-
-    void sendPowerHintForLaunchEndIfNeeded() {
-        // Trigger launch power hint if activity is launched
-        if (mPowerHintSent && mService.mLocalPowerManager != null) {
-            mService.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
-            mPowerHintSent = false;
-        }
-    }
-
-    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+    int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
             ActivityRecord[] outActivity) {
@@ -1064,7 +921,7 @@
                 }
             }
 
-            sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
+            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
 
             reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
 
@@ -1179,7 +1036,7 @@
                 EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
         mTargetStack.mLastPausedActivity = null;
 
-        sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
+        mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
 
         mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                 mOptions);
@@ -1225,8 +1082,6 @@
     private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
             boolean doResume, int startFlags, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
-        reset();
-
         mStartActivity = r;
         mIntent = r.intent;
         mOptions = options;
@@ -1933,6 +1788,7 @@
         return START_SUCCESS;
     }
 
+    @VisibleForTesting
     void updateBounds(TaskRecord task, Rect bounds) {
         if (bounds.isEmpty()) {
             return;
@@ -1996,20 +1852,6 @@
         return launchFlags;
     }
 
-    final void doPendingActivityLaunchesLocked(boolean doResume) {
-        while (!mPendingActivityLaunches.isEmpty()) {
-            final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
-            final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
-            try {
-                startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null,
-                        null, null /*outRecords*/);
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
-                pal.sendErrorResult(e.getMessage());
-            }
-        }
-    }
-
     private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
             ActivityOptions aOptions) {
         final TaskRecord task = r.getTask();
@@ -2166,35 +2008,8 @@
                 (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
     }
 
-    boolean clearPendingActivityLaunchesLocked(String packageName) {
-        boolean didSomething = false;
-
-        for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
-            PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
-            ActivityRecord r = pal.r;
-            if (r != null && r.packageName.equals(packageName)) {
-                mPendingActivityLaunches.remove(palNdx);
-                didSomething = true;
-            }
-        }
-        return didSomething;
-    }
-
-    void dump(PrintWriter pw, String prefix, String dumpPackage) {
+    void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
-
-        if (dumpPackage != null) {
-            if ((mLastStartActivityRecord[0] == null ||
-                    !dumpPackage.equals(mLastHomeActivityStartRecord[0].packageName)) &&
-                    (mLastHomeActivityStartRecord[0] == null ||
-                    !dumpPackage.equals(mLastHomeActivityStartRecord[0].packageName)) &&
-                    (mStartActivity == null || !dumpPackage.equals(mStartActivity.packageName))) {
-                pw.print(prefix);
-                pw.println("(nothing)");
-                return;
-            }
-        }
-
         pw.print(prefix);
         pw.print("mCurrentUser=");
         pw.println(mSupervisor.mCurrentUser);
@@ -2213,15 +2028,6 @@
             pw.println("mLastStartActivityRecord:");
             r.dump(pw, prefix + "  ");
         }
-        pw.print(prefix);
-        pw.print("mLastHomeActivityStartResult=");
-        pw.println(mLastHomeActivityStartResult);
-        r = mLastHomeActivityStartRecord[0];
-        if (r != null) {
-            pw.print(prefix);
-            pw.println("mLastHomeActivityStartRecord:");
-            r.dump(pw, prefix + "  ");
-        }
         if (mStartActivity != null) {
             pw.print(prefix);
             pw.println("mStartActivity:");
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index fe38097..d0bc33a 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -407,10 +407,10 @@
                         // recents entry. Let's see if we have a safe-to-restart intent.
                         final Set<String> cats = task.intent.getCategories();
                         if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
-                            mService.startActivityInPackage(task.mCallingUid,
-                                    task.mCallingPackage, task.intent, null, null, null, 0, 0,
-                                    ActivityOptions.makeBasic().toBundle(), task.userId, null,
-                                    "AppErrors");
+                            mService.getActivityStartController().startActivityInPackage(
+                                    task.mCallingUid, task.mCallingPackage, task.intent, null, null,
+                                    null, 0, 0, ActivityOptions.makeBasic().toBundle(), task.userId,
+                                    null, "AppErrors");
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index ab86dbdb..e5872c03 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -122,8 +122,8 @@
                 throw new IllegalArgumentException("Bad app thread " + appThread);
             }
         }
-        return mService.mActivityStarter.startActivityMayWait(appThread, -1, callingPackage,
-                intent, resolvedType, null, null, null, null, 0, 0, null, null,
+        return mService.getActivityStartController().startActivityMayWait(appThread, -1,
+                callingPackage, intent, resolvedType, null, null, null, null, 0, 0, null, null,
                 null, bOptions, false, callingUser, tr, "AppTaskImpl");
     }
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9e9318a..87690d1 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -935,7 +935,7 @@
         }
     }
 
-    public void notePackageInstalled(String pkgName, int versionCode) {
+    public void notePackageInstalled(String pkgName, long versionCode) {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.notePackageInstalledLocked(pkgName, versionCode);
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index b2d3137..a131db5 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -38,7 +38,7 @@
 # The Activity Manager failed to pause the given activity.
 30012 am_failed_to_pause (User|1|5),(Token|1|5),(Wanting to pause|3),(Currently pausing|3)
 # Attempting to pause the current activity
-30013 am_pause_activity (User|1|5),(Token|1|5),(Component Name|3)
+30013 am_pause_activity (User|1|5),(Token|1|5),(Component Name|3),(User Leaving|3)
 # Application process has been started
 30014 am_proc_start (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3)
 # An application process has been marked as bad
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 7930f53..c26e770 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -332,12 +332,14 @@
                                 }
                                 allIntents[allIntents.length-1] = finalIntent;
                                 allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
-                                owner.startActivitiesInPackage(uid, key.packageName, allIntents,
-                                        allResolvedTypes, resultTo, options, userId);
+                                owner.getActivityStartController().startActivitiesInPackage(uid,
+                                        key.packageName, allIntents, allResolvedTypes, resultTo,
+                                        options, userId);
                             } else {
-                                owner.startActivityInPackage(uid, key.packageName, finalIntent,
-                                        resolvedType, resultTo, resultWho, requestCode, 0,
-                                        options, userId, null, "PendingIntentRecord");
+                                owner.getActivityStartController().startActivityInPackage(uid,
+                                        key.packageName, finalIntent, resolvedType, resultTo,
+                                        resultWho, requestCode, 0, options, userId, null,
+                                        "PendingIntentRecord");
                             }
                         } catch (RuntimeException e) {
                             Slog.w(TAG, "Unable to send startActivity intent", e);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 9d3c2ae..71d6604 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -730,7 +730,7 @@
     /*
      *  Return true if package has been added false if not
      */
-    public boolean addPackage(String pkg, int versionCode, ProcessStatsService tracker) {
+    public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) {
         if (!pkgList.containsKey(pkg)) {
             ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
                     versionCode);
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index effb86c..5f9d616 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -27,6 +27,7 @@
 import android.service.procstats.ProcessStatsServiceDumpProto;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -120,12 +121,12 @@
     }
 
     public ProcessState getProcessStateLocked(String packageName,
-            int uid, int versionCode, String processName) {
+            int uid, long versionCode, String processName) {
         return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
     }
 
     public ServiceState getServiceStateLocked(String packageName, int uid,
-            int versionCode, String processName, String className) {
+            long versionCode, String processName, String className) {
         return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
                 className);
     }
@@ -150,12 +151,13 @@
             }
             mProcessStats.mMemFactor = memFactor;
             mProcessStats.mStartTime = now;
-            final ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pmap
+            final ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pmap
                     = mProcessStats.mPackages.getMap();
             for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) {
-                final SparseArray<SparseArray<ProcessStats.PackageState>> uids = pmap.valueAt(ipkg);
+                final SparseArray<LongSparseArray<ProcessStats.PackageState>> uids =
+                        pmap.valueAt(ipkg);
                 for (int iuid=uids.size()-1; iuid>=0; iuid--) {
-                    final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
+                    final LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
                     for (int iver=vers.size()-1; iver>=0; iver--) {
                         final ProcessStats.PackageState pkg = vers.valueAt(iver);
                         final ArrayMap<String, ServiceState> services = pkg.mServices;
@@ -308,17 +310,17 @@
                             Slog.w(TAG, "  Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
                         }
                     }
-                    ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pkgMap
+                    ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pkgMap
                             = stats.mPackages.getMap();
                     final int NPKG = pkgMap.size();
                     for (int ip=0; ip<NPKG; ip++) {
                         Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
-                        SparseArray<SparseArray<ProcessStats.PackageState>> uids
+                        SparseArray<LongSparseArray<ProcessStats.PackageState>> uids
                                 = pkgMap.valueAt(ip);
                         final int NUID = uids.size();
                         for (int iu=0; iu<NUID; iu++) {
                             Slog.w(TAG, "  Uid: " + uids.keyAt(iu));
-                            SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
+                            LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
                             final int NVERS = vers.size();
                             for (int iv=0; iv<NVERS; iv++) {
                                 Slog.w(TAG, "    Vers: " + vers.keyAt(iv));
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 15a418dc..efa0bf8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1645,6 +1645,11 @@
     };
 
     private int getNewRingerMode(int stream, int index, int flags) {
+        // setRingerMode does nothing if the device is single volume,so the value would be unchanged
+        if (mIsSingleVolume) {
+            return getRingerModeExternal();
+        }
+
         // setting volume on ui sounds stream type also controls silent mode
         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                 (stream == getUiSoundsStreamType())) {
@@ -3691,7 +3696,7 @@
     private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted,
             String caller, int flags) {
         int result = FLAG_ADJUST_VOLUME;
-        if (isPlatformTelevision()) {
+        if (isPlatformTelevision() || mIsSingleVolume) {
             return result;
         }
 
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index e243e56..979beed 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -244,7 +244,7 @@
         event.timestampMs = timestampMs;
         event.uid = uid;
         event.ethertype = ethertype;
-        event.dstHwAddr = new MacAddress(dstHw);
+        event.dstHwAddr = MacAddress.fromBytes(dstHw);
         event.srcIp = srcIp;
         event.dstIp = dstIp;
         event.ipNextHeader = ipNextHeader;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 4af86a0..3f014b5 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -29,7 +29,7 @@
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
 import android.app.job.JobWorkItem;
-import android.app.usage.AppStandby;
+import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
@@ -2074,10 +2074,10 @@
 
     public static int standbyBucketToBucketIndex(int bucket) {
         // Normalize AppStandby constants to indices into our bookkeeping
-        if (bucket == AppStandby.STANDBY_BUCKET_NEVER) return 4;
-        else if (bucket >= AppStandby.STANDBY_BUCKET_RARE) return 3;
-        else if (bucket >= AppStandby.STANDBY_BUCKET_FREQUENT) return 2;
-        else if (bucket >= AppStandby.STANDBY_BUCKET_WORKING_SET) return 1;
+        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return 4;
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_RARE) return 3;
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_FREQUENT) return 2;
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return 1;
         else return 0;
     }
 
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
index 66145bb..a543daf 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -54,18 +54,10 @@
     abstract int onTransact();
 
     /**
-     * A function to invoke when a transaction times out.
-     *
-     * All instances of this class must implement this method by reporting the timeout to the
-     * client.
-     */
-    /* package */
-    abstract void onTimeout();
-
-    /**
      * A function to invoke when the transaction completes.
      *
-     * Only relevant for load, unload, enable, or disable transactions.
+     * For transactions with expected contents (such as a query), the class instance should
+     * implement the appropriate behavior (e.g. invoke onQueryResponse with an empty list).
      *
      * @param result the result of the transaction
      */
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index 47d9d56..3fcc682 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -106,17 +106,12 @@
                             contextHubId, hidlNanoAppBinary, this.getTransactionId());
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while trying to load nanoapp with ID 0x" +
-                            Long.toHexString(nanoAppBinary.getNanoAppId()));
+                            Long.toHexString(nanoAppBinary.getNanoAppId()), e);
                     return Result.UNKNOWN_FAILURE;
                 }
             }
 
             @Override
-            /* package */ void onTimeout() {
-                onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
-            }
-
-            @Override
             /* package */ void onTransactionComplete(int result) {
                 try {
                     onCompleteCallback.onTransactionComplete(result);
@@ -124,7 +119,7 @@
                         mClientManager.onNanoAppLoaded(contextHubId, nanoAppBinary.getNanoAppId());
                     }
                 } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
                 }
             }
         };
@@ -149,17 +144,12 @@
                             contextHubId, nanoAppId, this.getTransactionId());
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while trying to unload nanoapp with ID 0x" +
-                            Long.toHexString(nanoAppId));
+                            Long.toHexString(nanoAppId), e);
                     return Result.UNKNOWN_FAILURE;
                 }
             }
 
             @Override
-            /* package */ void onTimeout() {
-                onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
-            }
-
-            @Override
             /* package */ void onTransactionComplete(int result) {
                 try {
                     onCompleteCallback.onTransactionComplete(result);
@@ -167,7 +157,7 @@
                         mClientManager.onNanoAppUnloaded(contextHubId, nanoAppId);
                     }
                 } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
                 }
             }
         };
@@ -189,15 +179,14 @@
                 try {
                     return mContextHubProxy.queryApps(contextHubId);
                 } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException while trying to query for nanoapps");
+                    Log.e(TAG, "RemoteException while trying to query for nanoapps", e);
                     return Result.UNKNOWN_FAILURE;
                 }
             }
 
             @Override
-            /* package */ void onTimeout() {
-                onQueryResponse(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT,
-                        Collections.emptyList());
+            /* package */ void onTransactionComplete(int result) {
+                onQueryResponse(result, Collections.emptyList());
             }
 
             @Override
@@ -205,7 +194,7 @@
                 try {
                     onCompleteCallback.onQueryResponse(result, nanoAppStateList);
                 } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException while calling client onQueryComplete");
+                    Log.e(TAG, "RemoteException while calling client onQueryComplete", e);
                 }
             }
         };
@@ -333,7 +322,8 @@
                     synchronized (this) {
                         if (!transaction.isComplete()) {
                             Log.d(TAG, transaction + " timed out");
-                            transaction.onTimeout();
+                            transaction.onTransactionComplete(
+                                    ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
 
                             removeTransactionAndStartNext();
                         }
@@ -344,6 +334,8 @@
                 mTimeoutFuture = mTimeoutExecutor.schedule(onTimeoutFunc, timeoutSeconds,
                         TimeUnit.SECONDS);
             } else {
+                transaction.onTransactionComplete(
+                        ContextHubServiceUtil.toTransactionResult(result));
                 mTransactionQueue.remove();
             }
         }
diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/services/core/java/com/android/server/notification/ScheduleCalendar.java
index 40230bd..5ff0e21 100644
--- a/services/core/java/com/android/server/notification/ScheduleCalendar.java
+++ b/services/core/java/com/android/server/notification/ScheduleCalendar.java
@@ -18,6 +18,7 @@
 
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.util.ArraySet;
+import android.util.Log;
 
 import java.util.Calendar;
 import java.util.Objects;
@@ -41,10 +42,25 @@
     }
 
     public void maybeSetNextAlarm(long now, long nextAlarm) {
-        if (mSchedule != null) {
-            if (mSchedule.exitAtAlarm
-                    && (now > mSchedule.nextAlarm || nextAlarm < mSchedule.nextAlarm)) {
-                mSchedule.nextAlarm = nextAlarm;
+        if (mSchedule != null && mSchedule.exitAtAlarm) {
+            // alarm canceled
+            if (nextAlarm == 0) {
+                mSchedule.nextAlarm = 0;
+            }
+            // only allow alarms in the future
+            if (nextAlarm > now) {
+                // store earliest alarm
+                if (mSchedule.nextAlarm == 0) {
+                    mSchedule.nextAlarm = nextAlarm;
+                } else {
+                    mSchedule.nextAlarm = Math.min(mSchedule.nextAlarm, nextAlarm);
+                }
+            } else if (mSchedule.nextAlarm < now) {
+                if (ScheduleConditionProvider.DEBUG) {
+                    Log.d(ScheduleConditionProvider.TAG,
+                            "All alarms are in the past " + mSchedule.nextAlarm);
+                }
+                mSchedule.nextAlarm = 0;
             }
         }
     }
@@ -87,6 +103,9 @@
     }
 
     public boolean shouldExitForAlarm(long time) {
+        if (mSchedule == null) {
+            return false;
+        }
         return mSchedule.exitAtAlarm
                 && mSchedule.nextAlarm != 0
                 && time >= mSchedule.nextAlarm;
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 50a51b2..c5f80bb 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -37,6 +37,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
 import java.io.PrintWriter;
@@ -62,10 +64,9 @@
     private static final String SEPARATOR = ";";
     private static final String SCP_SETTING = "snoozed_schedule_condition_provider";
 
-
     private final Context mContext = this;
     private final ArrayMap<Uri, ScheduleCalendar> mSubscriptions = new ArrayMap<>();
-    private ArraySet<Uri> mSnoozed = new ArraySet<>();
+    private ArraySet<Uri> mSnoozedForAlarm = new ArraySet<>();
 
     private AlarmManager mAlarmManager;
     private boolean mConnected;
@@ -102,7 +103,7 @@
                 pw.println(mSubscriptions.get(conditionId).toString());
             }
         }
-        pw.println("      snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozed));
+        pw.println("      snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozedForAlarm));
         dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, now);
     }
 
@@ -129,7 +130,7 @@
     public void onSubscribe(Uri conditionId) {
         if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
         if (!ZenModeConfig.isValidScheduleConditionId(conditionId)) {
-            notifyCondition(createCondition(conditionId, Condition.STATE_FALSE, "badCondition"));
+            notifyCondition(createCondition(conditionId, Condition.STATE_ERROR, "invalidId"));
             return;
         }
         synchronized (mSubscriptions) {
@@ -169,32 +170,11 @@
         synchronized (mSubscriptions) {
             setRegistered(!mSubscriptions.isEmpty());
             for (Uri conditionId : mSubscriptions.keySet()) {
-                final ScheduleCalendar cal = mSubscriptions.get(conditionId);
-                if (cal != null && cal.isInSchedule(now)) {
-                    if (conditionSnoozed(conditionId) || cal.shouldExitForAlarm(now)) {
-                        conditionsToNotify.add(createCondition(
-                                conditionId, Condition.STATE_FALSE, "alarmCanceled"));
-                        addSnoozed(conditionId);
-                    } else {
-                        conditionsToNotify.add(createCondition(
-                                conditionId, Condition.STATE_TRUE, "meetsSchedule"));
-                    }
-                    cal.maybeSetNextAlarm(now, nextUserAlarmTime);
-                } else {
-                    conditionsToNotify.add(createCondition(
-                            conditionId, Condition.STATE_FALSE, "!meetsSchedule"));
-                    removeSnoozed(conditionId);
-                    if (cal != null && nextUserAlarmTime == 0) {
-                        cal.maybeSetNextAlarm(now, nextUserAlarmTime);
-                    }
-                }
-                if (cal != null) {
-                    final long nextChangeTime = cal.getNextChangeTime(now);
-                    if (nextChangeTime > 0 && nextChangeTime > now) {
-                        if (mNextAlarmTime == 0 || nextChangeTime < mNextAlarmTime) {
-                            mNextAlarmTime = nextChangeTime;
-                        }
-                    }
+                Condition condition =
+                        evaluateSubscriptionLocked(conditionId, mSubscriptions.get(conditionId),
+                                now, nextUserAlarmTime);
+                if (condition != null) {
+                    conditionsToNotify.add(condition);
                 }
             }
         }
@@ -202,6 +182,39 @@
         updateAlarm(now, mNextAlarmTime);
     }
 
+    @VisibleForTesting
+    @GuardedBy("mSubscriptions")
+    Condition evaluateSubscriptionLocked(Uri conditionId, ScheduleCalendar cal,
+            long now, long nextUserAlarmTime) {
+        Condition condition;
+        if (cal == null) {
+            condition = createCondition(conditionId, Condition.STATE_ERROR, "!invalidId");
+            removeSnoozed(conditionId);
+            return condition;
+        }
+        if (cal.isInSchedule(now)) {
+            if (conditionSnoozed(conditionId)) {
+                condition = createCondition(conditionId, Condition.STATE_FALSE, "snoozed");
+            } else if (cal.shouldExitForAlarm(now)) {
+                condition = createCondition(conditionId, Condition.STATE_FALSE, "alarmCanceled");
+                addSnoozed(conditionId);
+            } else {
+                condition = createCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule");
+            }
+        } else {
+            condition = createCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule");
+            removeSnoozed(conditionId);
+        }
+        cal.maybeSetNextAlarm(now, nextUserAlarmTime);
+        final long nextChangeTime = cal.getNextChangeTime(now);
+        if (nextChangeTime > 0 && nextChangeTime > now) {
+            if (mNextAlarmTime == 0 || nextChangeTime < mNextAlarmTime) {
+                mNextAlarmTime = nextChangeTime;
+            }
+        }
+        return condition;
+    }
+
     private void updateAlarm(long now, long time) {
         final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
@@ -266,27 +279,28 @@
     }
 
     private boolean conditionSnoozed(Uri conditionId) {
-        synchronized (mSnoozed) {
-            return mSnoozed.contains(conditionId);
+        synchronized (mSnoozedForAlarm) {
+            return mSnoozedForAlarm.contains(conditionId);
         }
     }
 
-    private void addSnoozed(Uri conditionId) {
-        synchronized (mSnoozed) {
-            mSnoozed.add(conditionId);
+    @VisibleForTesting
+    void addSnoozed(Uri conditionId) {
+        synchronized (mSnoozedForAlarm) {
+            mSnoozedForAlarm.add(conditionId);
             saveSnoozedLocked();
         }
     }
 
     private void removeSnoozed(Uri conditionId) {
-        synchronized (mSnoozed) {
-            mSnoozed.remove(conditionId);
+        synchronized (mSnoozedForAlarm) {
+            mSnoozedForAlarm.remove(conditionId);
             saveSnoozedLocked();
         }
     }
 
-    public void saveSnoozedLocked() {
-        final String setting = TextUtils.join(SEPARATOR, mSnoozed);
+    private void saveSnoozedLocked() {
+        final String setting = TextUtils.join(SEPARATOR, mSnoozedForAlarm);
         final int currentUser = ActivityManager.getCurrentUser();
         Settings.Secure.putStringForUser(mContext.getContentResolver(),
                 SCP_SETTING,
@@ -294,8 +308,8 @@
                 currentUser);
     }
 
-    public void readSnoozed() {
-        synchronized (mSnoozed) {
+    private void readSnoozed() {
+        synchronized (mSnoozedForAlarm) {
             long identity = Binder.clearCallingIdentity();
             try {
                 final String setting = Settings.Secure.getStringForUser(
@@ -312,7 +326,7 @@
                         if (TextUtils.isEmpty(token)) {
                             continue;
                         }
-                        mSnoozed.add(Uri.parse(token));
+                        mSnoozedForAlarm.add(Uri.parse(token));
                     }
                 }
             } finally {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 5f54c67..88fc65e 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -159,7 +159,7 @@
                     long startTime) {
                 final String packageName;
                 final String splitName;
-                final int versionCode;
+                final long versionCode;
                 final Intent failureIntent;
                 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                     final AuxiliaryResolveInfo instantAppIntentInfo =
@@ -241,7 +241,7 @@
             @NonNull String instantAppPackageName,
             @Nullable String instantAppSplitName,
             @Nullable ComponentName installFailureActivity,
-            int versionCode,
+            long versionCode,
             @Nullable String token,
             boolean needsPhaseTwo) {
         // Construct the intent that launches the instant installer
@@ -258,6 +258,7 @@
         if (origIntent.getData() != null) {
             intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
         }
+        intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
 
         // We have all of the data we need; just start the installer without a second phase
         if (!needsPhaseTwo) {
@@ -307,7 +308,8 @@
 
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName);
             intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName);
-            intent.putExtra(Intent.EXTRA_VERSION_CODE, versionCode);
+            intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) (versionCode & 0x7fffffff));
+            intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, versionCode);
             intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
             if (verificationBundle != null) {
                 intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 711352f..5cf08dc 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -225,7 +225,7 @@
     @GuardedBy("mLock")
     private String mPackageName;
     @GuardedBy("mLock")
-    private int mVersionCode;
+    private long mVersionCode;
     @GuardedBy("mLock")
     private Signature[] mSignatures;
     @GuardedBy("mLock")
@@ -1006,7 +1006,7 @@
             // Use first package to define unknown values
             if (mPackageName == null) {
                 mPackageName = apk.packageName;
-                mVersionCode = apk.versionCode;
+                mVersionCode = apk.getLongVersionCode();
             }
             if (mSignatures == null) {
                 mSignatures = apk.signatures;
@@ -1057,7 +1057,7 @@
             // ensure we've got appropriate package name, version code and signatures
             if (mPackageName == null) {
                 mPackageName = pkgInfo.packageName;
-                mVersionCode = pkgInfo.versionCode;
+                mVersionCode = pkgInfo.getLongVersionCode();
             }
             if (mSignatures == null) {
                 mSignatures = pkgInfo.signatures;
@@ -1149,7 +1149,7 @@
                     + " specified package " + params.appPackageName
                     + " inconsistent with " + apk.packageName);
         }
-        if (mVersionCode != apk.versionCode) {
+        if (mVersionCode != apk.getLongVersionCode()) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
                     + " version code " + apk.versionCode + " inconsistent with "
                     + mVersionCode);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d0ad4ac..9804283 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,6 @@
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -187,6 +186,7 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.IArtManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -242,6 +242,8 @@
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.LogPrinter;
+import android.util.LongSparseArray;
+import android.util.LongSparseLongArray;
 import android.util.MathUtils;
 import android.util.PackageUtils;
 import android.util.Pair;
@@ -263,16 +265,13 @@
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.IParcelFileDescriptorFactory;
-import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -292,6 +291,7 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
+import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexLogger;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
@@ -307,30 +307,23 @@
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import dalvik.system.CloseGuard;
-import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
-import libcore.io.Streams;
-import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.FilenameFilter;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -341,15 +334,12 @@
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -363,7 +353,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.zip.GZIPInputStream;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -467,6 +456,7 @@
     static final int SCAN_AS_SYSTEM = 1<<17;
     static final int SCAN_AS_PRIVILEGED = 1<<18;
     static final int SCAN_AS_OEM = 1<<19;
+    static final int SCAN_AS_VENDOR = 1<<20;
 
     @IntDef(flag = true, prefix = { "SCAN_" }, value = {
             SCAN_NO_DEX,
@@ -887,8 +877,8 @@
         public final @Nullable String apk;
         public final @NonNull SharedLibraryInfo info;
 
-        SharedLibraryEntry(String _path, String _apk, String name, int version, int type,
-                String declaringPackageName, int declaringPackageVersionCode) {
+        SharedLibraryEntry(String _path, String _apk, String name, long version, int type,
+                String declaringPackageName, long declaringPackageVersionCode) {
             path = _path;
             apk = _apk;
             info = new SharedLibraryInfo(name, version, type, new VersionedPackage(
@@ -897,8 +887,8 @@
     }
 
     // Currently known shared libraries.
-    final ArrayMap<String, SparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
-    final ArrayMap<String, SparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage =
+    final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
+    final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage =
             new ArrayMap<>();
 
     // All available activities, for your resolving pleasure.
@@ -938,6 +928,8 @@
 
     final PackageInstallerService mInstallerService;
 
+    final ArtManagerService mArtManagerService;
+
     private final PackageDexOptimizer mPackageDexOptimizer;
     // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
     // is used by other apps).
@@ -2574,8 +2566,25 @@
                     | SCAN_AS_SYSTEM,
                     0);
 
-            // Collect all vendor packages.
-            File vendorAppDir = new File("/vendor/app");
+            // Collected privileged vendor packages.
+                File privilegedVendorAppDir = new File(Environment.getVendorDirectory(),
+                        "priv-app");
+            try {
+                privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
+            } catch (IOException e) {
+                // failed to look up canonical path, continue with original one
+            }
+            scanDirTracedLI(privilegedVendorAppDir,
+                    mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM_DIR,
+                    scanFlags
+                    | SCAN_AS_SYSTEM
+                    | SCAN_AS_VENDOR
+                    | SCAN_AS_PRIVILEGED,
+                    0);
+
+            // Collect ordinary vendor packages.
+            File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
             try {
                 vendorAppDir = vendorAppDir.getCanonicalFile();
             } catch (IOException e) {
@@ -2585,7 +2594,8 @@
                     mDefParseFlags
                     | PackageParser.PARSE_IS_SYSTEM_DIR,
                     scanFlags
-                    | SCAN_AS_SYSTEM,
+                    | SCAN_AS_SYSTEM
+                    | SCAN_AS_VENDOR,
                     0);
 
             // Collect all OEM packages.
@@ -2642,7 +2652,7 @@
                                     + ps.name + "; removing system app.  Last known codePath="
                                     + ps.codePathString + ", installStatus=" + ps.installStatus
                                     + ", versionCode=" + ps.versionCode + "; scanned versionCode="
-                                    + scannedPkg.mVersionCode);
+                                    + scannedPkg.getLongVersionCode());
                             removePackageLI(scannedPkg, true);
                             mExpectingBetter.put(ps.name, ps.codePath);
                         }
@@ -2770,13 +2780,23 @@
                             rescanFlags =
                                     scanFlags
                                     | SCAN_AS_SYSTEM;
+                        } else if (FileUtils.contains(privilegedVendorAppDir, scanFile)) {
+                            reparseFlags =
+                                    mDefParseFlags |
+                                    PackageParser.PARSE_IS_SYSTEM_DIR;
+                            rescanFlags =
+                                    scanFlags
+                                    | SCAN_AS_SYSTEM
+                                    | SCAN_AS_VENDOR
+                                    | SCAN_AS_PRIVILEGED;
                         } else if (FileUtils.contains(vendorAppDir, scanFile)) {
                             reparseFlags =
                                     mDefParseFlags |
                                     PackageParser.PARSE_IS_SYSTEM_DIR;
                             rescanFlags =
                                     scanFlags
-                                    | SCAN_AS_SYSTEM;
+                                    | SCAN_AS_SYSTEM
+                                    | SCAN_AS_VENDOR;
                         } else if (FileUtils.contains(oemAppDir, scanFile)) {
                             reparseFlags =
                                     mDefParseFlags |
@@ -3018,6 +3038,7 @@
             }
 
             mInstallerService = new PackageInstallerService(context, this);
+            mArtManagerService = new ArtManagerService(this);
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
             if (instantAppResolverComponent != null) {
@@ -3830,7 +3851,7 @@
     public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
             int flags, int userId) {
         return getPackageInfoInternal(versionedPackage.getPackageName(),
-                versionedPackage.getVersionCode(), flags, Binder.getCallingUid(), userId);
+                versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
     }
 
     /**
@@ -3839,7 +3860,7 @@
      * to clearing. Because it can only be provided by trusted code, it's value can be
      * trusted and will be used as-is; unlike userId which will be validated by this method.
      */
-    private PackageInfo getPackageInfoInternal(String packageName, int versionCode,
+    private PackageInfo getPackageInfoInternal(String packageName, long versionCode,
             int flags, int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForPackage(flags, userId, packageName);
@@ -4036,7 +4057,7 @@
                 if (index < 0) {
                     continue;
                 }
-                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getVersion()) {
+                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getLongVersion()) {
                     return false;
                 }
             }
@@ -4432,7 +4453,8 @@
             final int[] allUsers = sUserManager.getUserIds();
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                final SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                final LongSparseArray<SharedLibraryEntry> versionedLib
+                        = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
@@ -4449,7 +4471,8 @@
                     final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
                     // Resolve the package name - we use synthetic package names internally
                     final String internalPackageName = resolveInternalPackageNameLPr(
-                            declaringPackage.getPackageName(), declaringPackage.getVersionCode());
+                            declaringPackage.getPackageName(),
+                            declaringPackage.getLongVersionCode());
                     final PackageSetting ps = mSettings.getPackageLPr(internalPackageName);
                     // Skip unused static shared libs cached less than the min period
                     // to prevent pruning a lib needed by a subsequently installed package.
@@ -4460,7 +4483,7 @@
                         packagesToDelete = new ArrayList<>();
                     }
                     packagesToDelete.add(new VersionedPackage(internalPackageName,
-                            declaringPackage.getVersionCode()));
+                            declaringPackage.getLongVersionCode()));
                 }
             }
         }
@@ -4470,7 +4493,7 @@
             for (int i = 0; i < packageCount; i++) {
                 final VersionedPackage pkgToDelete = packagesToDelete.get(i);
                 // Delete the package synchronously (will fail of the lib used for any user).
-                if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getVersionCode(),
+                if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(),
                         UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
                                 == PackageManager.DELETE_SUCCEEDED) {
                     if (volume.getUsableSpace() >= neededSpace) {
@@ -4775,7 +4798,7 @@
 
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
@@ -4799,7 +4822,7 @@
                     }
 
                     SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(),
-                            libInfo.getVersion(), libInfo.getType(),
+                            libInfo.getLongVersion(), libInfo.getType(),
                             libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo,
                             flags, userId));
 
@@ -4835,7 +4858,7 @@
                 if (libIdx < 0) {
                     continue;
                 }
-                if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getVersion()) {
+                if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) {
                     continue;
                 }
                 if (versionedPackages == null) {
@@ -4916,7 +4939,7 @@
             Set<String> libs = null;
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
@@ -8335,18 +8358,25 @@
             } else {
                 updatedPs.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_OEM;
             }
+            // If new package is not located in "/vendor" (e.g. due to an OTA),
+            // it needs to drop FLAG_VENDOR.
+            if (locationIsVendor(pkg.codePath)) {
+                updatedPs.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_VENDOR;
+            } else {
+                updatedPs.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VENDOR;
+            }
 
             if (ps != null && !ps.codePathString.equals(pkg.codePath)) {
                 // The path has changed from what was last scanned...  check the
                 // version of the new path against what we have stored to determine
                 // what to do.
                 if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
-                if (pkg.mVersionCode <= ps.versionCode) {
+                if (pkg.getLongVersionCode() <= ps.versionCode) {
                     // The system package has been updated and the code path does not match
                     // Ignore entry. Skip it.
                     if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + pkg.codePath
                             + " ignored: updated version " + ps.versionCode
-                            + " better than this " + pkg.mVersionCode);
+                            + " better than this " + pkg.getLongVersionCode());
                     if (!updatedPs.codePathString.equals(pkg.codePath)) {
                         Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
                                 + ps.name + " changing from " + updatedPs.codePathString
@@ -8358,7 +8388,7 @@
                         updatedPs.resourcePathString = pkg.codePath;
                     }
                     updatedPs.pkg = pkg;
-                    updatedPs.versionCode = pkg.mVersionCode;
+                    updatedPs.versionCode = pkg.getLongVersionCode();
 
                     // Update the disabled system child packages to point to the package too.
                     final int childCount = updatedPs.childPackageNames != null
@@ -8369,7 +8399,7 @@
                                 childPackageName);
                         if (updatedChildPkg != null) {
                             updatedChildPkg.pkg = pkg;
-                            updatedChildPkg.versionCode = pkg.mVersionCode;
+                            updatedChildPkg.versionCode = pkg.getLongVersionCode();
                         }
                     }
                 } else {
@@ -8387,7 +8417,7 @@
 
                     logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
                             + " reverting from " + ps.codePathString
-                            + ": new version " + pkg.mVersionCode
+                            + ": new version " + pkg.getLongVersionCode()
                             + " better than installed " + ps.versionCode);
 
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
@@ -8442,7 +8472,7 @@
 
             throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at "
                     + pkg.codePath + " ignored: updated version " + updatedPs.versionCode
-                    + " better than this " + pkg.mVersionCode);
+                    + " better than this " + pkg.getLongVersionCode());
         }
 
         if (isUpdatedPkg) {
@@ -8455,11 +8485,17 @@
                 scanFlags |= SCAN_AS_PRIVILEGED;
             }
 
-            // An updated OEM app will not have the PARSE_IS_OEM
+            // An updated OEM app will not have the SCAN_AS_OEM
             // flag set initially
             if ((updatedPs.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
                 scanFlags |= SCAN_AS_OEM;
             }
+
+            // An updated vendor app will not have the SCAN_AS_VENDOR
+            // flag set initially
+            if ((updatedPs.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
+                scanFlags |= SCAN_AS_VENDOR;
+            }
         }
 
         // Verify certificates against what was last scanned
@@ -8491,11 +8527,11 @@
                  * already installed version, hide it. It will be scanned later
                  * and re-added like an update.
                  */
-                if (pkg.mVersionCode <= ps.versionCode) {
+                if (pkg.getLongVersionCode() <= ps.versionCode) {
                     shouldHideSystemApp = true;
                     logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + pkg.codePath
-                            + " but new version " + pkg.mVersionCode + " better than installed "
-                            + ps.versionCode + "; hiding system");
+                            + " but new version " + pkg.getLongVersionCode()
+                            + " better than installed " + ps.versionCode + "; hiding system");
                 } else {
                     /*
                      * The newly found system app is a newer version that the
@@ -8505,7 +8541,8 @@
                      */
                     logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
                             + " reverting from " + ps.codePathString + ": new version "
-                            + pkg.mVersionCode + " better than installed " + ps.versionCode);
+                            + pkg.getLongVersionCode() + " better than installed "
+                            + ps.versionCode);
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                     synchronized (mInstallLock) {
@@ -9083,12 +9120,12 @@
         }
     }
 
-    private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, int[] versions,
+    private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, long[] versions,
             ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
         final int libNameCount = libs.size();
         for (int i = 0; i < libNameCount; i++) {
             String libName = libs.get(i);
-            int version = (versions != null && versions.length == libNameCount)
+            long version = (versions != null && versions.length == libNameCount)
                     ? versions[i] : PackageManager.VERSION_CODE_HIGHEST;
             PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version);
             if (libPkg != null) {
@@ -9097,7 +9134,7 @@
         }
     }
 
-    private PackageParser.Package findSharedNonSystemLibrary(String name, int version) {
+    private PackageParser.Package findSharedNonSystemLibrary(String name, long version) {
         synchronized (mPackages) {
             SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(name, version);
             if (libEntry != null) {
@@ -9107,8 +9144,8 @@
         }
     }
 
-    private SharedLibraryEntry getSharedLibraryEntryLPr(String name, int version) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+    private SharedLibraryEntry getSharedLibraryEntryLPr(String name, long version) {
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             return null;
         }
@@ -9116,15 +9153,15 @@
     }
 
     private SharedLibraryEntry getLatestSharedLibraVersionLPr(PackageParser.Package pkg) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
                 pkg.staticSharedLibName);
         if (versionedLib == null) {
             return null;
         }
-        int previousLibVersion = -1;
+        long previousLibVersion = -1;
         final int versionCount = versionedLib.size();
         for (int i = 0; i < versionCount; i++) {
-            final int libVersion = versionedLib.keyAt(i);
+            final long libVersion = versionedLib.keyAt(i);
             if (libVersion < pkg.staticSharedLibVersion) {
                 previousLibVersion = Math.max(previousLibVersion, libVersion);
             }
@@ -9408,14 +9445,14 @@
     }
 
     private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
-            @Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
+            @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
             @NonNull String packageName, @Nullable PackageParser.Package changingLib,
             boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
             throws PackageManagerException {
         final int libCount = requestedLibraries.size();
         for (int i = 0; i < libCount; i++) {
             final String libName = requestedLibraries.get(i);
-            final int libVersion = requiredVersions != null ? requiredVersions[i]
+            final long libVersion = requiredVersions != null ? requiredVersions[i]
                     : SharedLibraryInfo.VERSION_UNDEFINED;
             final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(libName, libVersion);
             if (libEntry == null) {
@@ -9430,11 +9467,11 @@
                 }
             } else {
                 if (requiredVersions != null && requiredCertDigests != null) {
-                    if (libEntry.info.getVersion() != requiredVersions[i]) {
+                    if (libEntry.info.getLongVersion() != requiredVersions[i]) {
                         throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                             "Package " + packageName + " requires unavailable static shared"
                                     + " library " + libName + " version "
-                                    + libEntry.info.getVersion() + "; failing!");
+                                    + libEntry.info.getLongVersion() + "; failing!");
                     }
 
                     PackageParser.Package libPkg = mPackages.get(libEntry.apk);
@@ -9793,7 +9830,7 @@
                 pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
                         disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
                         pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,
-                        pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode,
+                        pkg.applicationInfo.secondaryCpuAbi, pkg.getLongVersionCode(),
                         pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
                         true /*allowInstall*/, instantApp, virtualPreload,
                         parentPackageName, pkg.getChildPackageNames(),
@@ -10208,6 +10245,10 @@
             pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
         }
 
+        if ((scanFlags & SCAN_AS_VENDOR) != 0) {
+            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VENDOR;
+        }
+
         if (!isSystemApp(pkg)) {
             // Only system apps can use these features.
             pkg.mOriginalPackages = null;
@@ -10357,20 +10398,20 @@
                 }
 
                 // The version codes must be ordered as lib versions
-                int minVersionCode = Integer.MIN_VALUE;
-                int maxVersionCode = Integer.MAX_VALUE;
+                long minVersionCode = Long.MIN_VALUE;
+                long maxVersionCode = Long.MAX_VALUE;
 
-                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
                         pkg.staticSharedLibName);
                 if (versionedLib != null) {
                     final int versionCount = versionedLib.size();
                     for (int i = 0; i < versionCount; i++) {
                         SharedLibraryInfo libInfo = versionedLib.valueAt(i).info;
-                        final int libVersionCode = libInfo.getDeclaringPackage()
-                                .getVersionCode();
-                        if (libInfo.getVersion() <  pkg.staticSharedLibVersion) {
+                        final long libVersionCode = libInfo.getDeclaringPackage()
+                                .getLongVersionCode();
+                        if (libInfo.getLongVersion() <  pkg.staticSharedLibVersion) {
                             minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
-                        } else if (libInfo.getVersion() >  pkg.staticSharedLibVersion) {
+                        } else if (libInfo.getLongVersion() >  pkg.staticSharedLibVersion) {
                             maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
                         } else {
                             minVersionCode = maxVersionCode = libVersionCode;
@@ -10378,7 +10419,8 @@
                         }
                     }
                 }
-                if (pkg.mVersionCode < minVersionCode || pkg.mVersionCode > maxVersionCode) {
+                if (pkg.getLongVersionCode() < minVersionCode
+                        || pkg.getLongVersionCode() > maxVersionCode) {
                     throw new PackageManagerException("Static shared"
                             + " lib version codes must be ordered as lib versions");
                 }
@@ -10468,11 +10510,11 @@
         }
     }
 
-    private boolean addSharedLibraryLPw(String path, String apk, String name, int version,
-            int type, String declaringPackageName, int declaringVersionCode) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+    private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
+            int type, String declaringPackageName, long declaringVersionCode) {
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
-            versionedLib = new SparseArray<>();
+            versionedLib = new LongSparseArray<>();
             mSharedLibraries.put(name, versionedLib);
             if (type == SharedLibraryInfo.TYPE_STATIC) {
                 mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
@@ -10486,8 +10528,8 @@
         return true;
     }
 
-    private boolean removeSharedLibraryLPw(String name, int version) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+    private boolean removeSharedLibraryLPw(String name, long version) {
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             return false;
         }
@@ -10526,6 +10568,7 @@
                     // Set up information for our fall-back user intent resolution activity.
                     mPlatformPackage = pkg;
                     pkg.mVersionCode = mSdkVersion;
+                    pkg.mVersionCodeMajor = 0;
                     mAndroidApplication = pkg.applicationInfo;
                     if (!mResolverReplaced) {
                         mResolveActivity.applicationInfo = mAndroidApplication;
@@ -10567,7 +10610,7 @@
                 // names to allow install of multiple versions, so use name from manifest.
                 if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName,
                         pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
-                        pkg.manifestPackageName, pkg.mVersionCode)) {
+                        pkg.manifestPackageName, pkg.getLongVersionCode())) {
                     hasStaticSharedLibs = true;
                 } else {
                     Slog.w(TAG, "Package " + pkg.packageName + " library "
@@ -10613,7 +10656,7 @@
                             if (!addSharedLibraryLPw(null, pkg.packageName, name,
                                     SharedLibraryInfo.VERSION_UNDEFINED,
                                     SharedLibraryInfo.TYPE_DYNAMIC,
-                                    pkg.packageName, pkg.mVersionCode)) {
+                                    pkg.packageName, pkg.getLongVersionCode())) {
                                 Slog.w(TAG, "Package " + pkg.packageName + " library "
                                         + name + " already exists; skipping");
                             }
@@ -14608,6 +14651,9 @@
                     verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
                             pkgLite.versionCode);
 
+                    verification.putExtra(PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
+                            pkgLite.getLongVersionCode());
+
                     if (verificationInfo != null) {
                         if (verificationInfo.originatingUri != null) {
                             verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
@@ -15493,6 +15539,15 @@
                 return;
             }
 
+            // check if the new package supports all of the abis which the old package supports
+            boolean oldPkgSupportMultiArch = oldPackage.applicationInfo.secondaryCpuAbi != null;
+            boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null;
+            if (isSystemApp(oldPackage) && oldPkgSupportMultiArch && !newPkgSupportMultiArch) {
+                res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                        "Update to package " + pkgName + " doesn't support multi arch");
+                return;
+            }
+
             // In case of rollback, remember per-user/profile install state
             allUsers = sUserManager.getUserIds();
             installedUsers = ps.queryInstalledUsers(allUsers, true);
@@ -15574,18 +15629,22 @@
 
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
-            // Set the system/privileged/oem flags as needed
+            // Set the system/privileged/oem/vendor flags as needed
             final boolean privileged =
                     (oldPackage.applicationInfo.privateFlags
                             & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
             final boolean oem =
                     (oldPackage.applicationInfo.privateFlags
                             & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+            final boolean vendor =
+                    (oldPackage.applicationInfo.privateFlags
+                            & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
             final @ParseFlags int systemParseFlags = parseFlags;
             final @ScanFlags int systemScanFlags = scanFlags
                     | SCAN_AS_SYSTEM
                     | (privileged ? SCAN_AS_PRIVILEGED : 0)
-                    | (oem ? SCAN_AS_OEM : 0);
+                    | (oem ? SCAN_AS_OEM : 0)
+                    | (vendor ? SCAN_AS_VENDOR : 0);
 
             replaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags,
                     user, allUsers, installerPackageName, res, installReason);
@@ -16525,7 +16584,8 @@
                     // unless this is the exact same version code which is useful for
                     // development.
                     PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
-                    if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
+                    if (existingPkg != null &&
+                            existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
                         res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
                                 + "static-shared libs cannot be updated");
                         return;
@@ -16764,6 +16824,10 @@
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
     }
 
+    private static boolean isVendorApp(PackageParser.Package pkg) {
+        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+    }
+
     private static boolean hasDomainURLs(PackageParser.Package pkg) {
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
     }
@@ -16828,17 +16892,16 @@
         final boolean canViewInstantApps = canViewInstantApps(callingUid, userId);
         Preconditions.checkNotNull(versionedPackage);
         Preconditions.checkNotNull(observer);
-        Preconditions.checkArgumentInRange(versionedPackage.getVersionCode(),
+        Preconditions.checkArgumentInRange(versionedPackage.getLongVersionCode(),
                 PackageManager.VERSION_CODE_HIGHEST,
-                Integer.MAX_VALUE, "versionCode must be >= -1");
+                Long.MAX_VALUE, "versionCode must be >= -1");
 
         final String packageName = versionedPackage.getPackageName();
-        final int versionCode = versionedPackage.getVersionCode();
+        final long versionCode = versionedPackage.getLongVersionCode();
         final String internalPackageName;
         synchronized (mPackages) {
             // Normalize package name to handle renamed packages and static libs
-            internalPackageName = resolveInternalPackageNameLPr(versionedPackage.getPackageName(),
-                    versionedPackage.getVersionCode());
+            internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode);
         }
 
         final int uid = Binder.getCallingUid();
@@ -16946,24 +17009,24 @@
         return pkg.packageName;
     }
 
-    private String resolveInternalPackageNameLPr(String packageName, int versionCode) {
+    private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
         // Handle renamed packages
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
         packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
 
         // Is this a static library?
-        SparseArray<SharedLibraryEntry> versionedLib =
+        LongSparseArray<SharedLibraryEntry> versionedLib =
                 mStaticLibsByDeclaringPackage.get(packageName);
         if (versionedLib == null || versionedLib.size() <= 0) {
             return packageName;
         }
 
         // Figure out which lib versions the caller can see
-        SparseIntArray versionsCallerCanSee = null;
+        LongSparseLongArray versionsCallerCanSee = null;
         final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
         if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
                 && callingAppId != Process.ROOT_UID) {
-            versionsCallerCanSee = new SparseIntArray();
+            versionsCallerCanSee = new LongSparseLongArray();
             String libName = versionedLib.valueAt(0).info.getName();
             String[] uidPackages = getPackagesForUid(Binder.getCallingUid());
             if (uidPackages != null) {
@@ -16971,7 +17034,7 @@
                     PackageSetting ps = mSettings.getPackageLPr(uidPackage);
                     final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
                     if (libIdx >= 0) {
-                        final int libVersion = ps.usesStaticLibrariesVersions[libIdx];
+                        final long libVersion = ps.usesStaticLibrariesVersions[libIdx];
                         versionsCallerCanSee.append(libVersion, libVersion);
                     }
                 }
@@ -16989,10 +17052,10 @@
         for (int i = 0; i < versionCount; i++) {
             SharedLibraryEntry libEntry = versionedLib.valueAt(i);
             if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
-                    libEntry.info.getVersion()) < 0) {
+                    libEntry.info.getLongVersion()) < 0) {
                 continue;
             }
-            final int libVersionCode = libEntry.info.getDeclaringPackage().getVersionCode();
+            final long libVersionCode = libEntry.info.getDeclaringPackage().getLongVersionCode();
             if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
                 if (libVersionCode == versionCode) {
                     return libEntry.apk;
@@ -17000,7 +17063,7 @@
             } else if (highestVersion == null) {
                 highestVersion = libEntry;
             } else if (libVersionCode  > highestVersion.info
-                    .getDeclaringPackage().getVersionCode()) {
+                    .getDeclaringPackage().getLongVersionCode()) {
                 highestVersion = libEntry;
             }
         }
@@ -17129,7 +17192,7 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-    int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) {
+    int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags) {
         final PackageRemovedInfo info = new PackageRemovedInfo(this);
         final boolean res;
 
@@ -17180,7 +17243,7 @@
                         if (!ArrayUtils.isEmpty(libClientPackages)) {
                             Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
                                     + " hosting lib " + libEntry.info.getName() + " version "
-                                    + libEntry.info.getVersion() + " used by " + libClientPackages
+                                    + libEntry.info.getLongVersion() + " used by " + libClientPackages
                                     + " for user " + currUserId);
                             return PackageManager.DELETE_FAILED_USED_SHARED_LIBRARY;
                         }
@@ -17498,7 +17561,9 @@
     static boolean locationIsPrivileged(String path) {
         try {
             final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
-            return path.startsWith(privilegedAppDir.getCanonicalPath());
+            final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
+            return path.startsWith(privilegedAppDir.getCanonicalPath())
+                    || path.startsWith(privilegedVendorAppDir.getCanonicalPath());
         } catch (IOException e) {
             Slog.e(TAG, "Unable to access code path " + path);
         }
@@ -17514,6 +17579,15 @@
         return false;
     }
 
+    static boolean locationIsVendor(String path) {
+        try {
+            return path.startsWith(Environment.getVendorDirectory().getCanonicalPath());
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to access code path " + path);
+        }
+        return false;
+    }
+
     /*
      * Tries to delete system package.
      */
@@ -17635,6 +17709,9 @@
         if (locationIsOem(codePathString)) {
             scanFlags |= SCAN_AS_OEM;
         }
+        if (locationIsVendor(codePathString)) {
+            scanFlags |= SCAN_AS_VENDOR;
+        }
 
         final File codePath = new File(codePathString);
         final PackageParser.Package pkg =
@@ -20450,7 +20527,8 @@
                 final Iterator<String> it = mSharedLibraries.keySet().iterator();
                 while (it.hasNext()) {
                     String libName = it.next();
-                    SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+                    LongSparseArray<SharedLibraryEntry> versionedLib
+                            = mSharedLibraries.get(libName);
                     if (versionedLib == null) {
                         continue;
                     }
@@ -20470,7 +20548,7 @@
                         }
                         pw.print(libEntry.info.getName());
                         if (libEntry.info.isStatic()) {
-                            pw.print(" version=" + libEntry.info.getVersion());
+                            pw.print(" version=" + libEntry.info.getLongVersion());
                         }
                         if (!checkin) {
                             pw.print(" -> ");
@@ -20839,7 +20917,7 @@
         final int count = mSharedLibraries.size();
         for (int i = 0; i < count; i++) {
             final String libName = mSharedLibraries.keyAt(i);
-            SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+            LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
             if (versionedLib == null) {
                 continue;
             }
@@ -22196,6 +22274,11 @@
         return mInstallerService;
     }
 
+    @Override
+    public IArtManager getArtManager() {
+        return mArtManagerService;
+    }
+
     private boolean userNeedsBadging(int userId) {
         int index = mUserNeedsBadging.indexOfKey(userId);
         if (index < 0) {
@@ -22344,11 +22427,11 @@
      */
     private static void checkDowngrade(PackageParser.Package before, PackageInfoLite after)
             throws PackageManagerException {
-        if (after.versionCode < before.mVersionCode) {
+        if (after.getLongVersionCode() < before.getLongVersionCode()) {
             throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
                     "Update version code " + after.versionCode + " is older than current "
-                    + before.mVersionCode);
-        } else if (after.versionCode == before.mVersionCode) {
+                    + before.getLongVersionCode());
+        } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
             if (after.baseRevisionCode < before.baseRevisionCode) {
                 throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
                         "Update base revision code " + after.baseRevisionCode
@@ -22535,12 +22618,12 @@
         }
 
         @Override
-        public int getVersionCodeForPackage(String packageName) throws RemoteException {
+        public long getVersionCodeForPackage(String packageName) throws RemoteException {
             try {
                 int callingUser = UserHandle.getUserId(Binder.getCallingUid());
                 PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
                 if (pInfo != null) {
-                    return pInfo.versionCode;
+                    return pInfo.getLongVersionCode();
                 }
             } catch (Exception e) {
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 44f36d1..a7cced7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1245,7 +1245,7 @@
         final PrintWriter pw = getOutPrintWriter();
         int flags = 0;
         int userId = UserHandle.USER_ALL;
-        int versionCode = PackageManager.VERSION_CODE_HIGHEST;
+        long versionCode = PackageManager.VERSION_CODE_HIGHEST;
 
         String opt;
         while ((opt = getNextOption()) != null) {
@@ -1257,7 +1257,7 @@
                     userId = UserHandle.parseUserArg(getNextArgRequired());
                     break;
                 case "--versionCode":
-                    versionCode = Integer.parseInt(getNextArgRequired());
+                    versionCode = Long.parseLong(getNextArgRequired());
                     break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
@@ -1538,13 +1538,26 @@
         return 0;
     }
 
+    private boolean isVendorApp(String pkg) {
+        try {
+            final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+            return info != null && info.applicationInfo.isVendor();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     private int runGetPrivappPermissions() {
         final String pkg = getNextArg();
         if (pkg == null) {
             getErrPrintWriter().println("Error: no package specified.");
             return 1;
         }
-        ArraySet<String> privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg);
+
+        ArraySet<String> privAppPermissions = isVendorApp(pkg) ?
+                SystemConfig.getInstance().getVendorPrivAppPermissions(pkg)
+                    : SystemConfig.getInstance().getPrivAppPermissions(pkg);
+
         getOutPrintWriter().println(privAppPermissions == null
                 ? "{}" : privAppPermissions.toString());
         return 0;
@@ -1556,10 +1569,13 @@
             getErrPrintWriter().println("Error: no package specified.");
             return 1;
         }
-        ArraySet<String> privAppDenyPermissions =
-                SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
-        getOutPrintWriter().println(privAppDenyPermissions == null
-                ? "{}" : privAppDenyPermissions.toString());
+
+        ArraySet<String> privAppPermissions = isVendorApp(pkg) ?
+                SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg)
+                    : SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
+
+        getOutPrintWriter().println(privAppPermissions == null
+                ? "{}" : privAppPermissions.toString());
         return 0;
     }
 
@@ -1876,13 +1892,16 @@
         final InstallParams params = new InstallParams();
         params.sessionParams = sessionParams;
         String opt;
+        boolean replaceExisting = true;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
                 case "-l":
                     sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
                     break;
-                case "-r":
-                    sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+                case "-r": // ignore
+                    break;
+                case "-R":
+                    replaceExisting = false;
                     break;
                 case "-i":
                     params.installerPackageName = getNextArg();
@@ -1967,6 +1986,9 @@
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
+            if (replaceExisting) {
+                sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+            }
         }
         return params;
     }
@@ -2435,7 +2457,7 @@
         pw.println("    Install an application.  Must provide the apk data to install, either as a");
         pw.println("    file path or '-' to read from stdin.  Options are:");
         pw.println("      -l: forward lock application");
-        pw.println("      -r: allow replacement of existing application");
+        pw.println("      -R: disallow replacement of existing application");
         pw.println("      -t: allow test packages");
         pw.println("      -i: specify package name of installer owning the app");
         pw.println("      -s: install application on sdcard");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 3b414e9..2b91b7d 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -50,9 +50,9 @@
     PackageSetting(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
-            int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
+            long pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
             List<String> childPackageNames, int sharedUserId, String[] usesStaticLibraries,
-            int[] usesStaticLibrariesVersions) {
+            long[] usesStaticLibrariesVersions) {
         super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                 pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames,
@@ -140,6 +140,10 @@
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
     }
 
+    public boolean isVendor() {
+        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+    }
+
     public boolean isForwardLocked() {
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
     }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index a838768..9733624 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -75,7 +75,7 @@
     String resourcePathString;
 
     String[] usesStaticLibraries;
-    int[] usesStaticLibrariesVersions;
+    long[] usesStaticLibrariesVersions;
 
     /**
      * The path under which native libraries have been unpacked. This path is
@@ -105,7 +105,7 @@
     long timeStamp;
     long firstInstallTime;
     long lastUpdateTime;
-    int versionCode;
+    long versionCode;
 
     boolean uidError;
 
@@ -149,9 +149,9 @@
     PackageSettingBase(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
-            int pVersionCode, int pkgFlags, int pkgPrivateFlags,
+            long pVersionCode, int pkgFlags, int pkgPrivateFlags,
             String parentPackageName, List<String> childPackageNames,
-            String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
+            String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
         super(pkgFlags, pkgPrivateFlags);
         this.name = name;
         this.realName = realName;
@@ -180,7 +180,7 @@
 
     void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
               String primaryCpuAbiString, String secondaryCpuAbiString,
-              String cpuAbiOverrideString, int pVersionCode) {
+              String cpuAbiOverrideString, long pVersionCode) {
         this.codePath = codePath;
         this.codePathString = codePath.toString();
         this.resourcePath = resourcePath;
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index c97f5e5..46ba006 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -61,6 +61,7 @@
         this.pkgPrivateFlags = pkgPrivateFlags
                 & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
                 | ApplicationInfo.PRIVATE_FLAG_OEM
+                | ApplicationInfo.PRIVATE_FLAG_VENDOR
                 | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
                 | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ddad677..4a5772f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -593,10 +593,10 @@
 
     PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
-            String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, int vc, int
+            String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
             pkgFlags, int pkgPrivateFlags, String parentPackageName,
             List<String> childPackageNames, String[] usesStaticLibraries,
-            int[] usesStaticLibraryNames) {
+            long[] usesStaticLibraryNames) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
             if (p.appId == uid) {
@@ -673,11 +673,11 @@
     static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg,
             PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
             File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
-            String secondaryCpuAbi, int versionCode, int pkgFlags, int pkgPrivateFlags,
+            String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
             UserHandle installUser, boolean allowInstall, boolean instantApp,
             boolean virtualPreload, String parentPkgName, List<String> childPkgNames,
             UserManagerService userManager,
-            String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
+            String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
         final PackageSetting pkgSetting;
         if (originalPkg != null) {
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
@@ -788,7 +788,7 @@
             @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi,
             int pkgFlags, int pkgPrivateFlags, @Nullable List<String> childPkgNames,
             @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries,
-            @Nullable int[] usesStaticLibrariesVersions) throws PackageManagerException {
+            @Nullable long[] usesStaticLibrariesVersions) throws PackageManagerException {
         final String pkgName = pkgSetting.name;
         if (pkgSetting.sharedUser != sharedUser) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -847,6 +847,8 @@
                 pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
         pkgSetting.pkgPrivateFlags |=
                 pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM;
+        pkgSetting.pkgPrivateFlags |=
+                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR;
         pkgSetting.primaryCpuAbiString = primaryCpuAbi;
         pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
         if (childPkgNames != null) {
@@ -949,8 +951,8 @@
         p.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
         p.cpuAbiOverrideString = pkg.cpuAbiOverride;
         // Update version code if needed
-        if (pkg.mVersionCode != p.versionCode) {
-            p.versionCode = pkg.mVersionCode;
+        if (pkg.getLongVersionCode() != p.versionCode) {
+            p.versionCode = pkg.getLongVersionCode();
         }
         // Update signatures if needed.
         if (p.signatures.mSignatures == null) {
@@ -2287,9 +2289,9 @@
             String libName = parser.getAttributeValue(null, ATTR_NAME);
             String libVersionStr = parser.getAttributeValue(null, ATTR_VERSION);
 
-            int libVersion = -1;
+            long libVersion = -1;
             try {
-                libVersion = Integer.parseInt(libVersionStr);
+                libVersion = Long.parseLong(libVersionStr);
             } catch (NumberFormatException e) {
                 // ignore
             }
@@ -2297,7 +2299,7 @@
             if (libName != null && libVersion >= 0) {
                 outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class,
                         outPs.usesStaticLibraries, libName);
-                outPs.usesStaticLibrariesVersions = ArrayUtils.appendInt(
+                outPs.usesStaticLibrariesVersions = ArrayUtils.appendLong(
                         outPs.usesStaticLibrariesVersions, libVersion);
             }
 
@@ -2306,7 +2308,7 @@
     }
 
     void writeUsesStaticLibLPw(XmlSerializer serializer, String[] usesStaticLibraries,
-            int[] usesStaticLibraryVersions) throws IOException {
+            long[] usesStaticLibraryVersions) throws IOException {
         if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions)
                 || usesStaticLibraries.length != usesStaticLibraryVersions.length) {
             return;
@@ -2314,10 +2316,10 @@
         final int libCount = usesStaticLibraries.length;
         for (int i = 0; i < libCount; i++) {
             final String libName = usesStaticLibraries[i];
-            final int libVersion = usesStaticLibraryVersions[i];
+            final long libVersion = usesStaticLibraryVersions[i];
             serializer.startTag(null, TAG_USES_STATIC_LIB);
             serializer.attribute(null, ATTR_NAME, libName);
-            serializer.attribute(null, ATTR_VERSION, Integer.toString(libVersion));
+            serializer.attribute(null, ATTR_VERSION, Long.toString(libVersion));
             serializer.endTag(null, TAG_USES_STATIC_LIB);
         }
     }
@@ -3561,10 +3563,10 @@
             resourcePathStr = codePathStr;
         }
         String version = parser.getAttributeValue(null, "version");
-        int versionCode = 0;
+        long versionCode = 0;
         if (version != null) {
             try {
-                versionCode = Integer.parseInt(version);
+                versionCode = Long.parseLong(version);
             } catch (NumberFormatException e) {
             }
         }
@@ -3677,7 +3679,7 @@
         long lastUpdateTime = 0;
         PackageSetting packageSetting = null;
         String version = null;
-        int versionCode = 0;
+        long versionCode = 0;
         String parentPackageName;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
@@ -3705,7 +3707,7 @@
             version = parser.getAttributeValue(null, "version");
             if (version != null) {
                 try {
-                    versionCode = Integer.parseInt(version);
+                    versionCode = Long.parseLong(version);
                 } catch (NumberFormatException e) {
                 }
             }
@@ -4421,6 +4423,7 @@
             ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, "PRIVILEGED",
             ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, "REQUIRED_FOR_SYSTEM_USER",
             ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY, "STATIC_SHARED_LIBRARY",
+            ApplicationInfo.PRIVATE_FLAG_VENDOR, "VENDOR",
             ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD",
     };
 
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ba97c42..7bab318 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -725,7 +725,7 @@
                 // This means if a system app's version code doesn't change on an OTA,
                 // we don't notice it's updated.  But that's fine since their version code *should*
                 // really change on OTAs.
-                if ((getPackageInfo().getVersionCode() == pi.versionCode)
+                if ((getPackageInfo().getVersionCode() == pi.getLongVersionCode())
                         && (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime)
                         && areAllActivitiesStillEnabled()) {
                     return false;
@@ -759,11 +759,11 @@
         if (ShortcutService.DEBUG) {
             Slog.d(TAG, String.format("Package %s %s, version %d -> %d", getPackageName(),
                     (isNewApp ? "added" : "updated"),
-                    getPackageInfo().getVersionCode(), pi.versionCode));
+                    getPackageInfo().getVersionCode(), pi.getLongVersionCode()));
         }
 
         getPackageInfo().updateFromPackageInfo(pi);
-        final int newVersionCode = getPackageInfo().getVersionCode();
+        final long newVersionCode = getPackageInfo().getVersionCode();
 
         // See if there are any shortcuts that were prevented restoring because the app was of a
         // lower version, and re-enable them.
@@ -1412,7 +1412,7 @@
             ShortcutService.writeAttr(out, ATTR_FLAGS, flags);
 
             // Set the publisher version code at every backup.
-            final int packageVersionCode = getPackageInfo().getVersionCode();
+            final long packageVersionCode = getPackageInfo().getVersionCode();
             if (packageVersionCode == 0) {
                 s.wtf("Package version code should be available at this point.");
                 // However, 0 is a valid version code, so we just go ahead with it...
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 3a9bbc8..b14e9c9 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -59,8 +59,8 @@
      * been installed yet.
      */
     private boolean mIsShadow;
-    private int mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
-    private int mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+    private long mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+    private long mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
     private long mLastUpdateTime;
     private ArrayList<byte[]> mSigHashes;
 
@@ -73,7 +73,7 @@
     private boolean mBackupAllowed;
     private boolean mBackupSourceBackupAllowed;
 
-    private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
+    private ShortcutPackageInfo(long versionCode, long lastUpdateTime,
             ArrayList<byte[]> sigHashes, boolean isShadow) {
         mVersionCode = versionCode;
         mLastUpdateTime = lastUpdateTime;
@@ -96,11 +96,11 @@
         mIsShadow = shadow;
     }
 
-    public int getVersionCode() {
+    public long getVersionCode() {
         return mVersionCode;
     }
 
-    public int getBackupSourceVersionCode() {
+    public long getBackupSourceVersionCode() {
         return mBackupSourceVersionCode;
     }
 
@@ -123,7 +123,7 @@
      */
     public void updateFromPackageInfo(@NonNull PackageInfo pi) {
         if (pi != null) {
-            mVersionCode = pi.versionCode;
+            mVersionCode = pi.getLongVersionCode();
             mLastUpdateTime = pi.lastUpdateTime;
             mBackupAllowed = ShortcutService.shouldBackupApp(pi);
             mBackupAllowedInitialized = true;
@@ -145,7 +145,7 @@
             Slog.w(TAG, "Can't restore: package didn't or doesn't allow backup");
             return ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED;
         }
-        if (!anyVersionOkay && (currentPackage.versionCode < mBackupSourceVersionCode)) {
+        if (!anyVersionOkay && (currentPackage.getLongVersionCode() < mBackupSourceVersionCode)) {
             Slog.w(TAG, String.format(
                     "Can't restore: package current version %d < backed up version %d",
                     currentPackage.versionCode, mBackupSourceVersionCode));
@@ -162,11 +162,12 @@
             Slog.e(TAG, "Can't get signatures: package=" + packageName);
             return null;
         }
-        final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
-                BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
+        final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.getLongVersionCode(),
+                pi.lastUpdateTime, BackupUtils.hashSignatureArray(pi.signatures),
+                /* shadow=*/ false);
 
         ret.mBackupSourceBackupAllowed = s.shouldBackupApp(pi);
-        ret.mBackupSourceVersionCode = pi.versionCode;
+        ret.mBackupSourceVersionCode = pi.getLongVersionCode();
         return ret;
     }
 
@@ -213,7 +214,7 @@
             throws IOException, XmlPullParserException {
 
         // Don't use the version code from the backup file.
-        final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION,
+        final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION,
                 ShortcutInfo.VERSION_CODE_UNKNOWN);
 
         final long lastUpdateTime = ShortcutService.parseLongAttribute(
@@ -225,7 +226,7 @@
 
         // We didn't used to save these attributes, and all backed up shortcuts were from
         // apps that support backups, so the default values take this fact into consideration.
-        final int backupSourceVersion = ShortcutService.parseIntAttribute(parser,
+        final long backupSourceVersion = ShortcutService.parseLongAttribute(parser,
                 ATTR_BACKUP_SOURCE_VERSION, ShortcutInfo.VERSION_CODE_UNKNOWN);
 
         // Note the only time these "true" default value is used is when restoring from an old
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 689099c..0629d9e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -108,7 +108,7 @@
             return; // Not installed, no need to restore yet.
         }
         int restoreBlockReason;
-        int currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+        long currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
 
         if (!mPackageInfo.hasSignatures()) {
             s.wtf("Attempted to restore package " + mPackageName + "/u" + mPackageUserId
@@ -116,7 +116,7 @@
             restoreBlockReason = ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
         } else {
             final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
-            currentVersionCode = pi.versionCode;
+            currentVersionCode = pi.getLongVersionCode();
             restoreBlockReason = mPackageInfo.canRestoreTo(s, pi, canRestoreAnyVersion());
         }
 
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
new file mode 100644
index 0000000..5a1f840
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.Manifest;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.dex.ArtManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.content.pm.IPackageManager;
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+/**
+ * A system service that provides access to runtime and compiler artifacts.
+ *
+ * This service is not accessed by users directly, instead one uses an instance of
+ * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
+ * <p/>
+ * {@code context().getPackageManager().getArtManager();}
+ * <p class="note">
+ * Note: Accessing runtime artifacts may require extra permissions. For example querying the
+ * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
+ * which is a system-level permission that will not be granted to normal apps.
+ */
+public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
+    private static final String TAG = "ArtManagerService";
+
+    private static boolean DEBUG = false;
+    private static boolean DEBUG_IGNORE_PERMISSIONS = false;
+
+    private final IPackageManager mPackageManager;
+    private final Handler mHandler;
+
+    public ArtManagerService(IPackageManager pm) {
+        mPackageManager = pm;
+        mHandler = new Handler(BackgroundThread.getHandler().getLooper());
+    }
+
+    @Override
+    public void snapshotRuntimeProfile(String packageName, String codePath,
+            ISnapshotRuntimeProfileCallback callback) {
+        // Sanity checks on the arguments.
+        Preconditions.checkStringNotEmpty(packageName);
+        Preconditions.checkStringNotEmpty(codePath);
+        Preconditions.checkNotNull(callback);
+
+        // Verify that the caller has the right permissions.
+        checkReadRuntimeProfilePermission();
+
+        if (DEBUG) {
+            Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
+        }
+
+        PackageInfo info = null;
+        try {
+            // Note that we use the default user 0 to retrieve the package info.
+            // This doesn't really matter because for user 0 we always get a package back (even if
+            // it's not installed for the user 0). It is ok because we only care about the code
+            // paths and not if the package is enabled or not for the user.
+
+            // TODO(calin): consider adding an API to PMS which can retrieve the
+            // PackageParser.Package.
+            info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
+        } catch (RemoteException ignored) {
+            // Should not happen.
+        }
+        if (info == null) {
+            postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
+            return;
+        }
+
+        boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
+        String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
+        if (!pathFound && (splitCodePaths != null)) {
+            for (String path : splitCodePaths) {
+                if (path.equals(codePath)) {
+                    pathFound = true;
+                    break;
+                }
+            }
+        }
+        if (!pathFound) {
+            postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
+            return;
+        }
+
+        // All good, move forward and get the profile.
+        postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+    }
+
+    @Override
+    public boolean isRuntimeProfilingEnabled() {
+        // Verify that the caller has the right permissions.
+        checkReadRuntimeProfilePermission();
+
+        return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+    }
+
+    /**
+     * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
+     * on the internal {@code mHandler}.
+     */
+    private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
+            int errCode) {
+        mHandler.post(() -> {
+            try {
+                callback.onError(errCode);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
+            }
+        });
+    }
+
+    /**
+     * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
+     * If not, it throws a {@link SecurityException}.
+     */
+    private void checkReadRuntimeProfilePermission() {
+        if (DEBUG_IGNORE_PERMISSIONS) {
+            return;
+        }
+        try {
+            int result = mPackageManager.checkUidPermission(
+                    Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
+            if (result != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("You need "
+                        + Manifest.permission.READ_RUNTIME_PROFILES
+                        + " permission to snapshot profiles.");
+            }
+        } catch (RemoteException e) {
+            // Should not happen.
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 8c86db6..75a6106 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -225,6 +225,9 @@
     public boolean isVerifier() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
     }
+    public boolean isVendorPrivileged() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7d8e206..90ac4ab 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -959,8 +959,9 @@
      * <p>This handles parent/child apps.
      */
     private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) {
-        ArraySet<String> wlPermissions = SystemConfig.getInstance()
-                .getPrivAppPermissions(pkg.packageName);
+        ArraySet<String> wlPermissions = pkg.isVendor() ?
+                SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.packageName)
+                    : SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName);
         // Let's check if this package is whitelisted...
         boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm);
         // If it's not, we'll also tail-recurse to the parent.
@@ -971,7 +972,8 @@
     private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
             BasePermission bp, PermissionsState origPermissions) {
         boolean oemPermission = bp.isOEM();
-        boolean privilegedPermission = bp.isPrivileged();
+        boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
+        boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();
         boolean privappPermissionsDisable =
                 RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
         boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
@@ -982,8 +984,11 @@
                 // Only report violations for apps on system image
                 if (!mSystemReady && !pkg.isUpdatedSystemApp()) {
                     // it's only a reportable violation if the permission isn't explicitly denied
-                    final ArraySet<String> deniedPermissions = SystemConfig.getInstance()
-                            .getPrivAppDenyPermissions(pkg.packageName);
+                    final ArraySet<String> deniedPermissions = pkg.isVendor() ?
+                            SystemConfig.getInstance()
+                                    .getVendorPrivAppDenyPermissions(pkg.packageName)
+                            : SystemConfig.getInstance()
+                                    .getPrivAppDenyPermissions(pkg.packageName);
                     final boolean permissionViolation =
                             deniedPermissions == null || !deniedPermissions.contains(perm);
                     if (permissionViolation) {
@@ -1086,6 +1091,15 @@
                             || (oemPermission && pkg.isOem()
                                     && canGrantOemPermission(ps, perm));
                 }
+                // In any case, don't grant a privileged permission to privileged vendor apps, if
+                // the permission's protectionLevel does not have the extra 'vendorPrivileged'
+                // flag.
+                if (allowed && privilegedPermission &&
+                        !vendorPrivilegedPermission && pkg.isVendor()) {
+                   Slog.w(TAG, "Permission " + perm + " cannot be granted to privileged vendor apk "
+                           + pkg.packageName + " because it isn't a 'vendorPrivileged' permission.");
+                   allowed = false;
+                }
             }
         }
         if (!allowed) {
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 7c234f9..3810192 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -87,6 +87,12 @@
     private String mDeviceSpecificSettingsSource; // For dump() only.
 
     /**
+     * A short string describing which battery saver is now enabled, which we dump in the eventlog.
+     */
+    @GuardedBy("mLock")
+    private String mEventLogKeys;
+
+    /**
      * {@code true} if vibration is disabled in battery saver mode.
      *
      * @see Settings.Global#BATTERY_SAVER_CONSTANTS
@@ -328,7 +334,7 @@
         mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
         mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
         mFireWallDisabled = parser.getBoolean(KEY_FIREWALL_DISABLED, false);
-        mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
+        mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, true);
         mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
         mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
         mLaunchBoostDisabled = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED, true);
@@ -354,6 +360,27 @@
 
         mFilesForNoninteractive = (new CpuFrequencies()).parseString(
                 parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "")).toSysFileMap();
+
+        final StringBuilder sb = new StringBuilder();
+
+        if (mForceAllAppsStandby) sb.append("A");
+        if (mForceBackgroundCheck) sb.append("B");
+
+        if (mVibrationDisabled) sb.append("v");
+        if (mAnimationDisabled) sb.append("a");
+        if (mSoundTriggerDisabled) sb.append("s");
+        if (mFullBackupDeferred) sb.append("F");
+        if (mKeyValueBackupDeferred) sb.append("K");
+        if (!mFireWallDisabled) sb.append("f");
+        if (!mDataSaverDisabled) sb.append("d");
+        if (!mAdjustBrightnessDisabled) sb.append("b");
+
+        if (mLaunchBoostDisabled) sb.append("l");
+        if (mOptionalSensorsDisabled) sb.append("S");
+
+        sb.append(mGpsMode);
+
+        mEventLogKeys = sb.toString();
     }
 
     /**
@@ -431,6 +458,12 @@
         }
     }
 
+    public String toEventLogString() {
+        synchronized (mLock) {
+            return mEventLogKeys;
+        }
+    }
+
     public void dump(PrintWriter pw) {
         synchronized (mLock) {
             pw.println();
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 1b19c33..a6bca0b 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -45,6 +45,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.power.BatterySaverPolicy;
 import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener;
@@ -77,6 +78,12 @@
     @GuardedBy("mLock")
     private boolean mEnabled;
 
+    /**
+     * Previously enabled or not; only for the event logging. Only use it from
+     * {@link #handleBatterySaverStateChanged}.
+     */
+    private boolean mPreviouslyEnabled;
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -213,12 +220,18 @@
         final ArrayMap<String, String> fileValues;
 
         synchronized (mLock) {
-            Slog.i(TAG, "Battery saver " + (mEnabled ? "enabled" : "disabled")
-                    + ": isInteractive=" + isInteractive);
+            EventLogTags.writeBatterySaverMode(
+                    mPreviouslyEnabled ? 1 : 0, // Previously off or on.
+                    mEnabled ? 1 : 0, // Now off or on.
+                    isInteractive ?  1 : 0, // Device interactive state.
+                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "");
+            mPreviouslyEnabled = mEnabled;
 
             listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
+
             enabled = mEnabled;
 
+
             if (enabled) {
                 fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
             } else {
diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
index ab9ab67..a8c68c0 100644
--- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
+++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
@@ -63,7 +63,7 @@
                 PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i],
                         PackageManager.GET_SIGNATURES, userId);
                 keyAttestationPackageInfos[i] = new KeyAttestationPackageInfo(packageNames[i],
-                        packageInfo.versionCode, packageInfo.signatures);
+                        packageInfo.getLongVersionCode(), packageInfo.signatures);
             }
         } catch (NameNotFoundException nnfe) {
             throw new RemoteException(nnfe.getMessage());
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 00c208d..1ce1400 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -155,6 +155,14 @@
         return ret;
     }
 
+    private final static long[] toLongArray(List<Long> list) {
+        long[] ret = new long[list.size()];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = list.get(i);
+        }
+        return ret;
+    }
+
     // Assumes that sStatsdLock is held.
     private final void informAllUidsLocked(Context context) throws RemoteException {
         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -165,7 +173,7 @@
         }
 
         List<Integer> uids = new ArrayList();
-        List<Integer> versions = new ArrayList();
+        List<Long> versions = new ArrayList();
         List<String> apps = new ArrayList();
 
         // Add in all the apps for every user/profile.
@@ -174,12 +182,12 @@
             for (int j = 0; j < pi.size(); j++) {
                 if (pi.get(j).applicationInfo != null) {
                     uids.add(pi.get(j).applicationInfo.uid);
-                    versions.add(pi.get(j).versionCode);
+                    versions.add(pi.get(j).getLongVersionCode());
                     apps.add(pi.get(j).packageName);
                 }
             }
         }
-        sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
+        sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new
                 String[apps.size()]));
         if (DEBUG) {
             Slog.w(TAG, "Sent data for " + uids.size() + " apps");
@@ -222,7 +230,7 @@
                         int uid = b.getInt(Intent.EXTRA_UID);
                         String app = intent.getData().getSchemeSpecificPart();
                         PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
-                        sStatsd.informOnePackage(app, uid, pi.versionCode);
+                        sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
                     }
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to inform statsd of an app update", e);
diff --git a/services/core/java/com/android/server/timezone/CheckToken.java b/services/core/java/com/android/server/timezone/CheckToken.java
index 5128360..4c4a8d7 100644
--- a/services/core/java/com/android/server/timezone/CheckToken.java
+++ b/services/core/java/com/android/server/timezone/CheckToken.java
@@ -46,8 +46,8 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream(12 /* (3 * sizeof(int)) */);
         try (DataOutputStream dos = new DataOutputStream(baos)) {
             dos.writeInt(mOptimisticLockId);
-            dos.writeInt(mPackageVersions.mUpdateAppVersion);
-            dos.writeInt(mPackageVersions.mDataAppVersion);
+            dos.writeLong(mPackageVersions.mUpdateAppVersion);
+            dos.writeLong(mPackageVersions.mDataAppVersion);
         } catch (IOException e) {
             throw new RuntimeException("Unable to write into a ByteArrayOutputStream", e);
         }
@@ -58,8 +58,8 @@
         ByteArrayInputStream bais = new ByteArrayInputStream(tokenBytes);
         try (DataInputStream dis = new DataInputStream(bais)) {
             int versionId = dis.readInt();
-            int updateAppVersion = dis.readInt();
-            int dataAppVersion = dis.readInt();
+            long updateAppVersion = dis.readLong();
+            long dataAppVersion = dis.readLong();
             return new CheckToken(versionId, new PackageVersions(updateAppVersion, dataAppVersion));
         }
     }
diff --git a/services/core/java/com/android/server/timezone/PackageManagerHelper.java b/services/core/java/com/android/server/timezone/PackageManagerHelper.java
index 804941a..f6e35e8 100644
--- a/services/core/java/com/android/server/timezone/PackageManagerHelper.java
+++ b/services/core/java/com/android/server/timezone/PackageManagerHelper.java
@@ -26,7 +26,7 @@
  */
 interface PackageManagerHelper {
 
-    int getInstalledPackageVersion(String packageName)
+    long getInstalledPackageVersion(String packageName)
             throws PackageManager.NameNotFoundException;
 
     boolean isPrivilegedApp(String packageName) throws PackageManager.NameNotFoundException;
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index cac7f7b..5601c91 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -76,7 +76,7 @@
      */
     private static final String ATTRIBUTE_DATA_APP_VERSION = "dataAppPackageVersion";
 
-    private static final int UNKNOWN_PACKAGE_VERSION = -1;
+    private static final long UNKNOWN_PACKAGE_VERSION = -1;
 
     private final AtomicFile mPackageStatusFile;
 
@@ -320,14 +320,14 @@
             serializer.attribute(namespace, ATTRIBUTE_CHECK_STATUS, statusAttributeValue);
             serializer.attribute(namespace, ATTRIBUTE_OPTIMISTIC_LOCK_ID,
                     Integer.toString(optimisticLockId));
-            int updateAppVersion = status == null
+            long updateAppVersion = status == null
                     ? UNKNOWN_PACKAGE_VERSION : packageVersions.mUpdateAppVersion;
             serializer.attribute(namespace, ATTRIBUTE_UPDATE_APP_VERSION,
-                    Integer.toString(updateAppVersion));
-            int dataAppVersion = status == null
+                    Long.toString(updateAppVersion));
+            long dataAppVersion = status == null
                     ? UNKNOWN_PACKAGE_VERSION : packageVersions.mDataAppVersion;
             serializer.attribute(namespace, ATTRIBUTE_DATA_APP_VERSION,
-                    Integer.toString(dataAppVersion));
+                    Long.toString(dataAppVersion));
             serializer.endTag(namespace, TAG_PACKAGE_STATUS);
             serializer.endDocument();
             serializer.flush();
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index f0306b9..1c54320 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -445,8 +445,8 @@
     }
 
     private PackageVersions lookupInstalledPackageVersions() {
-        int updatePackageVersion;
-        int dataPackageVersion;
+        long updatePackageVersion;
+        long dataPackageVersion;
         try {
             updatePackageVersion =
                     mPackageManagerHelper.getInstalledPackageVersion(mUpdateAppPackageName);
diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
index b89dd38..6a330e6 100644
--- a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
@@ -81,11 +81,11 @@
     }
 
     @Override
-    public int getInstalledPackageVersion(String packageName)
+    public long getInstalledPackageVersion(String packageName)
             throws PackageManager.NameNotFoundException {
         int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
         PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
-        return packageInfo.versionCode;
+        return packageInfo.getLongVersionCode();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/timezone/PackageVersions.java b/services/core/java/com/android/server/timezone/PackageVersions.java
index fc0d6e1..0084c1a 100644
--- a/services/core/java/com/android/server/timezone/PackageVersions.java
+++ b/services/core/java/com/android/server/timezone/PackageVersions.java
@@ -21,10 +21,10 @@
  */
 final class PackageVersions {
 
-    final int mUpdateAppVersion;
-    final int mDataAppVersion;
+    final long mUpdateAppVersion;
+    final long mDataAppVersion;
 
-    PackageVersions(int updateAppVersion, int dataAppVersion) {
+    PackageVersions(long updateAppVersion, long dataAppVersion) {
         this.mUpdateAppVersion = updateAppVersion;
         this.mDataAppVersion = dataAppVersion;
     }
@@ -48,8 +48,8 @@
 
     @Override
     public int hashCode() {
-        int result = mUpdateAppVersion;
-        result = 31 * result + mDataAppVersion;
+        int result = Long.hashCode(mUpdateAppVersion);
+        result = 31 * result + Long.hashCode(mDataAppVersion);
         return result;
     }
 
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 1e334b8..4aa2b37 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -156,9 +156,10 @@
         return mWebViewProviderPackages;
     }
 
-    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
+    public long getFactoryPackageVersion(String packageName) throws NameNotFoundException {
         PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
-        return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode;
+        return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY)
+                .getLongVersionCode();
     }
 
     /**
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index d57edcd..405fe7d 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -36,7 +36,7 @@
 public interface SystemInterface {
     public WebViewProviderInfo[] getWebViewPackages();
     public int onWebViewProviderChanged(PackageInfo packageInfo);
-    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException;
+    public long getFactoryPackageVersion(String packageName) throws NameNotFoundException;
 
     public String getUserChosenWebViewProvider(Context context);
     public void updateUserSetting(Context context, String newProviderName);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
index 7fc907f..7e05e46 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdater.java
@@ -54,7 +54,7 @@
 
     private Context mContext;
     private SystemInterface mSystemInterface;
-    private int mMinimumVersionCode = -1;
+    private long mMinimumVersionCode = -1;
 
     // Keeps track of the number of running relro creations
     private int mNumRelroCreationsStarted = 0;
@@ -430,7 +430,7 @@
         if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
             return VALIDITY_INCORRECT_SDK_VERSION;
         }
-        if (!versionCodeGE(packageInfo.versionCode, getMinimumVersionCode())
+        if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
                 && !mSystemInterface.systemIsDebuggable()) {
             // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
             // minimum version code. This check is only enforced for user builds.
@@ -461,9 +461,9 @@
      *
      * @return true if versionCode1 is higher than or equal to versionCode2.
      */
-    private static boolean versionCodeGE(int versionCode1, int versionCode2) {
-        int v1 = versionCode1 / 100000;
-        int v2 = versionCode2 / 100000;
+    private static boolean versionCodeGE(long versionCode1, long versionCode2) {
+        long v1 = versionCode1 / 100000;
+        long v2 = versionCode2 / 100000;
 
         return v1 >= v2;
     }
@@ -478,16 +478,16 @@
      * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not
      * hold mLock meaning that we must take extra care to ensure this method is thread-safe.
      */
-    private int getMinimumVersionCode() {
+    private long getMinimumVersionCode() {
         if (mMinimumVersionCode > 0) {
             return mMinimumVersionCode;
         }
 
-        int minimumVersionCode = -1;
+        long minimumVersionCode = -1;
         for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
             if (provider.availableByDefault && !provider.isFallback) {
                 try {
-                    int versionCode =
+                    long versionCode =
                         mSystemInterface.getFactoryPackageVersion(provider.packageName);
                     if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
                         minimumVersionCode = versionCode;
@@ -577,7 +577,7 @@
             String packageDetails = String.format(
                     "versionName: %s, versionCode: %d, targetSdkVersion: %d",
                     systemUserPackageInfo.versionName,
-                    systemUserPackageInfo.versionCode,
+                    systemUserPackageInfo.getLongVersionCode(),
                     systemUserPackageInfo.applicationInfo.targetSdkVersion);
             if (validity == VALIDITY_OK) {
                 boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 84e475a..c091157 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -226,7 +226,7 @@
         final Rect taskFrame = new Rect();
         task.getBounds(taskFrame);
 
-        final GraphicBuffer buffer = SurfaceControl.captureLayersToBuffer(
+        final GraphicBuffer buffer = SurfaceControl.captureLayers(
                 task.getSurfaceControl().getHandle(), taskFrame, scaleFraction);
 
         if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
@@ -236,7 +236,7 @@
             return null;
         }
         return new TaskSnapshot(buffer, top.getConfiguration().orientation,
-                minRect(mainWindow.mContentInsets, mainWindow.mStableInsets),
+                getInsetsFromTaskBounds(mainWindow, task),
                 isLowRamDevice /* reduced */, scaleFraction /* scale */);
     }
 
@@ -244,11 +244,18 @@
         return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
     }
 
-    private Rect minRect(Rect rect1, Rect rect2) {
-        return new Rect(Math.min(rect1.left, rect2.left),
-                Math.min(rect1.top, rect2.top),
-                Math.min(rect1.right, rect2.right),
-                Math.min(rect1.bottom, rect2.bottom));
+    private Rect getInsetsFromTaskBounds(WindowState state, Task task) {
+        final Rect r = new Rect();
+        r.set(state.getContentFrameLw());
+        r.intersectUnchecked(state.getStableFrameLw());
+
+        final Rect taskBounds = task.getBounds();
+
+        r.set(Math.max(0, r.left - taskBounds.left),
+                Math.max(0, r.top - taskBounds.top),
+                Math.max(0, taskBounds.right - r.right),
+                Math.max(0, taskBounds.bottom - r.bottom));
+        return r;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 41915a3..57f754f 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -349,8 +349,9 @@
 
         // Let's remove all system decorations except the status bar, but only if the task is at the
         // very top of the screen.
+        final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
         rect.inset((int) (insets.left * mSnapshot.getScale()),
-                mTaskBounds.top != 0 ? (int) (insets.top * mSnapshot.getScale()) : 0,
+                isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
                 (int) (insets.right * mSnapshot.getScale()),
                 (int) (insets.bottom * mSnapshot.getScale()));
         return rect;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 82f842c..28f93e1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -229,6 +229,7 @@
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
@@ -5863,6 +5864,7 @@
             Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
         }
 
+        LatencyTracker.getInstance(mContext).onActionStart(LatencyTracker.ACTION_ROTATE_SCREEN);
         // TODO(multidisplay): rotation on non-default displays
         if (CUSTOM_SCREEN_ROTATION && displayContent.isDefaultDisplay) {
             mExitAnimId = exitAnim;
@@ -5987,6 +5989,7 @@
         if (configChanged) {
             mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
         }
+        LatencyTracker.getInstance(mContext).onActionEnd(LatencyTracker.ACTION_ROTATE_SCREEN);
     }
 
     static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c2ac905..9eab61c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -50,6 +50,7 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -4331,15 +4332,6 @@
         }
     }
 
-    boolean usesRelativeZOrdering() {
-        if (!isChildWindow()) {
-            return false;
-        } else if (mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
-            return true;
-        } else {
-            return false;
-        }
-    }
 
     @Override
     boolean shouldMagnify() {
@@ -4414,4 +4406,28 @@
     public boolean isDimming() {
         return mIsDimming;
     }
+
+    // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
+    // then we can drop all negative layering on the windowing side and simply inherit
+    // the default implementation here.
+    public void assignChildLayers(Transaction t) {
+        int layer = 1;
+        for (int i = 0; i < mChildren.size(); i++) {
+            final WindowState w = mChildren.get(i);
+
+            // APPLICATION_MEDIA_OVERLAY needs to go above APPLICATION_MEDIA
+            // while they both need to go below the main window. However the
+            // relative layering of multiple APPLICATION_MEDIA/OVERLAY has never
+            // been defined and so we can use static layers and leave it that way.
+            if (w.mAttrs.type == TYPE_APPLICATION_MEDIA) {
+                w.assignLayer(t, -2);
+            } else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
+                w.assignLayer(t, -1);
+            } else {
+                w.assignLayer(t, layer);
+            }
+            w.assignChildLayers(t);
+            layer++;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 4b5661f..54c84ea 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -602,13 +602,6 @@
         return mWin.getDisplayContent().getDisplay().getLayerStack();
     }
 
-    void updateLayerStackInTransaction() {
-        if (mSurfaceController != null) {
-            mSurfaceController.setLayerStackInTransaction(
-                    getLayerStack());
-        }
-    }
-
     void resetDrawState() {
         mDrawState = DRAW_PENDING;
 
@@ -727,16 +720,6 @@
                     + width + "x" + height + "), layer=" + mAnimLayer + " HIDE", false);
         }
 
-        // Start a new transaction and apply position & offset.
-
-        mService.openSurfaceTransaction();
-        try {
-            mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
-            mSurfaceController.setLayerStackInTransaction(getLayerStack());
-        } finally {
-            mService.closeSurfaceTransaction("createSurfaceLocked");
-        }
-
         mLastHidden = true;
 
         if (WindowManagerService.localLOGV) Slog.v(TAG, "Created surface " + this);
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 62a2abb..a3d4b71 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -125,6 +125,11 @@
     }
 
     void setExiting() {
+        if (mChildren.size() == 0) {
+            super.removeImmediately();
+            return;
+        }
+
         // This token is exiting, so allow it to be removed when it no longer contains any windows.
         mPersistOnEmpty = false;
 
@@ -255,12 +260,6 @@
         // up with goodToGo, so we don't move a window
         // to another display before the window behind
         // it is ready.
-        SurfaceControl.openTransaction();
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState win = mChildren.get(i);
-            win.mWinAnimator.updateLayerStackInTransaction();
-        }
-        SurfaceControl.closeTransaction();
 
         super.onDisplayChanged(dc);
     }
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index ae7d6da..39cc953 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -43,13 +43,14 @@
 
 using android::hardware::Return;
 using android::hardware::Void;
-using android::hardware::power::V1_0::IPower;
 using android::hardware::power::V1_0::PowerStatePlatformSleepState;
 using android::hardware::power::V1_0::PowerStateVoter;
 using android::hardware::power::V1_0::Status;
 using android::hardware::power::V1_1::PowerStateSubsystem;
 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
 using android::hardware::hidl_vec;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
 namespace android
 {
@@ -59,9 +60,9 @@
 
 static bool wakeup_init = false;
 static sem_t wakeup_sem;
-extern sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0;
-extern std::mutex gPowerHalMutex;
-extern bool getPowerHal();
+extern sp<IPowerV1_0> getPowerHalV1_0();
+extern sp<IPowerV1_1> getPowerHalV1_1();
+extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
 
 // Java methods used in getLowPowerStats
 static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -203,13 +204,13 @@
         return;
     }
 
-    std::lock_guard<std::mutex> lock(gPowerHalMutex);
-    if (!getPowerHal()) {
+    sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
+    if (powerHalV1_0 == nullptr) {
         ALOGE("Power Hal not loaded");
         return;
     }
 
-    Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
+    Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
             [&env, &jrpmStats](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
 
             if (status != Status::SUCCESS) return;
@@ -236,20 +237,17 @@
                 }
             }
     });
-    if (!ret.isOk()) {
-        ALOGE("getLowPowerStats() failed: power HAL service not available");
-        gPowerHalV1_0 = nullptr;
+    if (!processPowerHalReturn(ret, "getPlatformLowPowerStats")) {
         return;
     }
 
-    //Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
-    sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1
-            = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-    if (gPowerHal_1_1 == nullptr) {
-        //This device does not support IPower@1.1, exiting gracefully
+    // Trying to get IPower 1.1, this will succeed only for devices supporting 1.1
+    sp<IPowerV1_1> powerHal_1_1 = getPowerHalV1_1();
+    if (powerHal_1_1 == nullptr) {
+        // This device does not support IPower@1.1, exiting gracefully
         return;
     }
-    ret = gPowerHal_1_1->getSubsystemLowPowerStats(
+    ret = powerHal_1_1->getSubsystemLowPowerStats(
             [&env, &jrpmStats](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
 
         if (status != Status::SUCCESS) return;
@@ -275,11 +273,7 @@
             }
         }
     });
-    if (!ret.isOk()) {
-        ALOGE("getSubsystemLowPowerStats() failed: power HAL service not available");
-        gPowerHalV1_0 = nullptr;
-    }
-    // gPowerHalMutex released here
+    processPowerHalReturn(ret, "getSubsystemLowPowerStats");
 }
 
 static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
@@ -294,13 +288,13 @@
     }
 
     {
-        std::lock_guard<std::mutex> lock(gPowerHalMutex);
-        if (!getPowerHal()) {
+        sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
+        if (powerHalV1_0 == nullptr) {
             ALOGE("Power Hal not loaded");
             return -1;
         }
 
-        Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
+        Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
             [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
                     Status status) {
                 if (status != Status::SUCCESS)
@@ -352,9 +346,7 @@
             }
         );
 
-        if (!ret.isOk()) {
-            ALOGE("getPlatformLowPowerStats() failed: power HAL service not available");
-            gPowerHalV1_0 = nullptr;
+        if (!processPowerHalReturn(ret, "getPlatformLowPowerStats")) {
             return -1;
         }
     }
@@ -369,8 +361,8 @@
     int remaining = (int)env->GetDirectBufferCapacity(outBuf);
     int total_added = -1;
 
-	//This is a IPower 1.1 API
-    sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 = nullptr;
+    // This is a IPower 1.1 API
+    sp<IPowerV1_1> powerHal_1_1 = nullptr;
 
     if (outBuf == NULL) {
         jniThrowException(env, "java/lang/NullPointerException", "null argument");
@@ -378,20 +370,14 @@
     }
 
     {
-        std::lock_guard<std::mutex> lock(gPowerHalMutex);
-        if (!getPowerHal()) {
-            ALOGE("Power Hal not loaded");
-            return -1;
-        }
-
-        //Trying to cast to 1.1, this will succeed only for devices supporting 1.1
-        gPowerHal_1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-    	if (gPowerHal_1_1 == nullptr) {
+        // Trying to get 1.1, this will succeed only for devices supporting 1.1
+        powerHal_1_1 = getPowerHalV1_1();
+        if (powerHal_1_1 == nullptr) {
             //This device does not support IPower@1.1, exiting gracefully
             return 0;
-    	}
+        }
 
-        Return<void> ret = gPowerHal_1_1->getSubsystemLowPowerStats(
+        Return<void> ret = powerHal_1_1->getSubsystemLowPowerStats(
            [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems,
                 Status status) {
 
@@ -452,9 +438,7 @@
         }
         );
 
-        if (!ret.isOk()) {
-            ALOGE("getSubsystemLowPowerStats() failed: power HAL service not available");
-            gPowerHalV1_0 = nullptr;
+        if (!processPowerHalReturn(ret, "getSubsystemLowPowerStats")) {
             return -1;
         }
     }
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index b6c3df70..b2d35d4 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -41,10 +41,11 @@
 
 using android::hardware::Return;
 using android::hardware::Void;
-using android::hardware::power::V1_1::IPower;
 using android::hardware::power::V1_0::PowerHint;
 using android::hardware::power::V1_0::Feature;
 using android::String8;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
 namespace android {
 
@@ -57,10 +58,11 @@
 // ----------------------------------------------------------------------------
 
 static jobject gPowerManagerServiceObj;
-sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
-sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
-bool gPowerHalExists = true;
-std::mutex gPowerHalMutex;
+// Use getPowerHal* to retrieve a copy
+static sp<IPowerV1_0> gPowerHalV1_0_ = nullptr;
+static sp<IPowerV1_1> gPowerHalV1_1_ = nullptr;
+static bool gPowerHalExists = true;
+static std::mutex gPowerHalMutex;
 static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1];
 
 // Throttling interval for user activity calls.
@@ -80,26 +82,63 @@
 
 // Check validity of current handle to the power HAL service, and call getService() if necessary.
 // The caller must be holding gPowerHalMutex.
-bool getPowerHal() {
-    if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
-        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
-        if (gPowerHalV1_0 != nullptr) {
-            gPowerHalV1_1 =  android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-            ALOGI("Loaded power HAL service");
+static void connectPowerHalLocked() {
+    if (gPowerHalExists && gPowerHalV1_0_ == nullptr) {
+        gPowerHalV1_0_ = IPowerV1_0::getService();
+        if (gPowerHalV1_0_ != nullptr) {
+            ALOGI("Loaded power HAL 1.0 service");
+            // Try cast to powerHAL V1_1
+            gPowerHalV1_1_ =  IPowerV1_1::castFrom(gPowerHalV1_0_);
+            if (gPowerHalV1_1_ == nullptr) {
+            } else {
+                ALOGI("Loaded power HAL 1.1 service");
+            }
         } else {
             ALOGI("Couldn't load power HAL service");
             gPowerHalExists = false;
         }
     }
-    return gPowerHalV1_0 != nullptr;
+}
+
+// Retrieve a copy of PowerHAL V1_0
+sp<IPowerV1_0> getPowerHalV1_0() {
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+    connectPowerHalLocked();
+    return gPowerHalV1_0_;
+}
+
+// Retrieve a copy of PowerHAL V1_1
+sp<IPowerV1_1> getPowerHalV1_1() {
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+    connectPowerHalLocked();
+    return gPowerHalV1_1_;
 }
 
 // Check if a call to a power HAL function failed; if so, log the failure and invalidate the
-// current handle to the power HAL service. The caller must be holding gPowerHalMutex.
-static void processReturn(const Return<void> &ret, const char* functionName) {
+// current handle to the power HAL service.
+bool processPowerHalReturn(const Return<void> &ret, const char* functionName) {
     if (!ret.isOk()) {
         ALOGE("%s() failed: power HAL service not available.", functionName);
-        gPowerHalV1_0 = nullptr;
+        gPowerHalMutex.lock();
+        gPowerHalV1_0_ = nullptr;
+        gPowerHalV1_1_ = nullptr;
+        gPowerHalMutex.unlock();
+    }
+    return ret.isOk();
+}
+
+static void sendPowerHint(PowerHint hintId, uint32_t data) {
+    sp<IPowerV1_1> powerHalV1_1 = getPowerHalV1_1();
+    Return<void> ret;
+    if (powerHalV1_1 != nullptr) {
+        ret = powerHalV1_1->powerHintAsync(hintId, data);
+        processPowerHalReturn(ret, "powerHintAsync");
+    } else {
+        sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
+        if (powerHalV1_0 != nullptr) {
+            ret = powerHalV1_0->powerHint(hintId, data);
+            processPowerHalReturn(ret, "powerHint");
+        }
     }
 }
 
@@ -119,20 +158,8 @@
             }
             gLastEventTime[eventType] = eventTime;
 
-
             // Tell the power HAL when user activity occurs.
-            gPowerHalMutex.lock();
-            if (getPowerHal()) {
-              Return<void> ret;
-              if (gPowerHalV1_1 != nullptr) {
-                ret = gPowerHalV1_1->powerHintAsync(PowerHint::INTERACTION, 0);
-              } else {
-                ret = gPowerHalV1_0->powerHint(PowerHint::INTERACTION, 0);
-              }
-              processReturn(ret, "powerHint");
-            }
-            gPowerHalMutex.unlock();
-
+            sendPowerHint(PowerHint::INTERACTION, 0);
         }
 
         JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -150,7 +177,7 @@
     gPowerManagerServiceObj = env->NewGlobalRef(obj);
 
     gPowerHalMutex.lock();
-    getPowerHal();
+    connectPowerHalLocked();
     gPowerHalMutex.unlock();
 }
 
@@ -165,11 +192,11 @@
 }
 
 static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
-    std::lock_guard<std::mutex> lock(gPowerHalMutex);
-    if (getPowerHal()) {
+    sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
+    if (powerHalV1_0 != nullptr) {
         android::base::Timer t;
-        Return<void> ret = gPowerHalV1_0->setInteractive(enable);
-        processReturn(ret, "setInteractive");
+        Return<void> ret = powerHalV1_0->setInteractive(enable);
+        processPowerHalReturn(ret, "setInteractive");
         if (t.duration() > 20ms) {
             ALOGD("Excessive delay in setInteractive(%s) while turning screen %s",
                   enable ? "true" : "false", enable ? "on" : "off");
@@ -193,24 +220,15 @@
     }
 }
 
-static void nativeSendPowerHint(JNIEnv *env, jclass clazz, jint hintId, jint data) {
-    std::lock_guard<std::mutex> lock(gPowerHalMutex);
-    if (getPowerHal()) {
-        Return<void> ret;
-        if (gPowerHalV1_1 != nullptr) {
-            ret =  gPowerHalV1_1->powerHintAsync((PowerHint)hintId, data);
-        } else {
-            ret =  gPowerHalV1_0->powerHint((PowerHint)hintId, data);
-        }
-        processReturn(ret, "powerHint");
-    }
+static void nativeSendPowerHint(JNIEnv* /* env */, jclass /* clazz */, jint hintId, jint data) {
+    sendPowerHint(static_cast<PowerHint>(hintId), data);
 }
 
-static void nativeSetFeature(JNIEnv *env, jclass clazz, jint featureId, jint data) {
-    std::lock_guard<std::mutex> lock(gPowerHalMutex);
-    if (getPowerHal()) {
-        Return<void> ret = gPowerHalV1_0->setFeature((Feature)featureId, static_cast<bool>(data));
-        processReturn(ret, "setFeature");
+static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint featureId, jint data) {
+    sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
+    if (powerHalV1_0 != nullptr) {
+        Return<void> ret = powerHalV1_0->setFeature((Feature)featureId, static_cast<bool>(data));
+        processPowerHalReturn(ret, "setFeature");
     }
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index dddff8f..84cfabe 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import android.app.admin.IDevicePolicyManager;
+import android.content.ComponentName;
 
 import com.android.internal.R;
 import com.android.server.SystemService;
@@ -54,4 +55,6 @@
      * @see {@link SystemService#onStopUser}
      */
     abstract void handleStopUser(int userId);
+    
+    public void setSystemSetting(ComponentName who, String setting, String value){}
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d7e4a62..ddc0c23 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -315,6 +315,7 @@
     private static final Set<String> SECURE_SETTINGS_DEVICEOWNER_WHITELIST;
     private static final Set<String> GLOBAL_SETTINGS_WHITELIST;
     private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
+    private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
     static {
         SECURE_SETTINGS_WHITELIST = new ArraySet<>();
         SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -341,6 +342,11 @@
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.MODE_RINGER);
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.NETWORK_PREFERENCE);
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.WIFI_ON);
+
+        SYSTEM_SETTINGS_WHITELIST = new ArraySet<>();
+        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS);
+        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
     }
 
     /**
@@ -730,6 +736,7 @@
         private static final String TAG_ORGANIZATION_NAME = "organization-name";
         private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
         private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
+        private static final String TAG_IS_LOGOUT_BUTTON_ENABLED = "is_logout_button_enabled";
 
         final DeviceAdminInfo info;
 
@@ -779,6 +786,7 @@
         boolean requireAutoTime = false; // Can only be set by a device owner.
         boolean forceEphemeralUsers = false; // Can only be set by a device owner.
         boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
+        boolean isLogoutButtonEnabled = false; // Can only be set by a device owner.
 
         // one notification after enabling + one more after reboots
         static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
@@ -1096,6 +1104,11 @@
                 out.text(organizationName);
                 out.endTag(null, TAG_ORGANIZATION_NAME);
             }
+            if (isLogoutButtonEnabled) {
+                out.startTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutButtonEnabled));
+                out.endTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
+            }
         }
 
         void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1269,6 +1282,9 @@
                     } else {
                         Log.w(LOG_TAG, "Missing text when loading organization name");
                     }
+                } else if (TAG_IS_LOGOUT_BUTTON_ENABLED.equals(tag)) {
+                    isLogoutButtonEnabled = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -1822,6 +1838,10 @@
             Settings.Global.putString(mContext.getContentResolver(), name, value);
         }
 
+        void settingsSystemPutString(String name, String value) {
+            Settings.System.putString(mContext.getContentResolver(), name, value);
+        }
+
         void securityLogSetLoggingEnabledProperty(boolean enabled) {
             SecurityLog.setLoggingEnabledProperty(enabled);
         }
@@ -9137,6 +9157,24 @@
     }
 
     @Override
+    public void setSystemSetting(ComponentName who, String setting, String value) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
+
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+            if (!SYSTEM_SETTINGS_WHITELIST.contains(setting)) {
+                throw new SecurityException(String.format(
+                        "Permission denial: device owners cannot update %1$s", setting));
+            }
+
+            mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsSystemPutString(
+                    setting, value));
+        }
+    }
+
+    @Override
     public boolean setTime(ComponentName who, long millis) {
         Preconditions.checkNotNull(who, "ComponentName is null in setTime");
         getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -11405,4 +11443,37 @@
         }
         return false;
     }
+
+    @Override
+    public synchronized void setLogoutButtonEnabled(ComponentName admin, boolean enabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(admin);
+        getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+        if (enabled == isLogoutButtonEnabledInternalLocked()) {
+            // already in the requested state
+            return;
+        }
+        ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+        deviceOwner.isLogoutButtonEnabled = enabled;
+        saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+    }
+
+    @Override
+    public boolean isLogoutButtonEnabled() {
+        if (!mHasFeature) {
+            return false;
+        }
+        synchronized (this) {
+            return isLogoutButtonEnabledInternalLocked();
+        }
+    }
+
+    private boolean isLogoutButtonEnabledInternalLocked() {
+        ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+        return (deviceOwner != null) && deviceOwner.isLogoutButtonEnabled;
+    }
+
 }
diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java
new file mode 100644
index 0000000..cbda12d
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.service.notification.ZenModeConfig;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Slog;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ScheduleCalendarTest extends NotificationTestCase {
+
+    private ScheduleCalendar mScheduleCalendar;
+    private ZenModeConfig.ScheduleInfo mScheduleInfo;
+
+    @Before
+    public void setUp() throws Exception {
+        mScheduleCalendar = new ScheduleCalendar();
+        mScheduleInfo = new ZenModeConfig.ScheduleInfo();
+        mScheduleInfo.days = new int[] {1, 2, 3, 4, 5};
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+    }
+
+    @Test
+    public void testNullScheduleInfo() throws Exception {
+        mScheduleCalendar.setSchedule(null);
+
+        mScheduleCalendar.maybeSetNextAlarm(1000, 1999);
+        assertEquals(0, mScheduleCalendar.getNextChangeTime(1000));
+        assertFalse(mScheduleCalendar.isInSchedule(100));
+        assertFalse(mScheduleCalendar.shouldExitForAlarm(100));
+    }
+
+    @Test
+    public void testGetNextChangeTime_startToday() throws Exception {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 1);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        mScheduleInfo.days = new int[] {getTodayDay()};
+        mScheduleInfo.startHour = cal.get(Calendar.HOUR_OF_DAY) + 1;
+        mScheduleInfo.endHour = cal.get(Calendar.HOUR_OF_DAY) + 3;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = false;
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+
+        Calendar expected = new GregorianCalendar();
+        expected.setTimeInMillis(cal.getTimeInMillis());
+        expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+
+        long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
+        GregorianCalendar actual = new GregorianCalendar();
+        actual.setTimeInMillis(actualMs);
+        assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+                actualMs);
+    }
+
+    @Test
+    public void testGetNextChangeTime_endToday() throws Exception {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 2);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        mScheduleInfo.days = new int[] {getTodayDay()};
+        mScheduleInfo.startHour = cal.get(Calendar.HOUR_OF_DAY) - 1;
+        mScheduleInfo.endHour = cal.get(Calendar.HOUR_OF_DAY) + 3;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = false;
+
+        Calendar expected = new GregorianCalendar();
+        expected.setTimeInMillis(cal.getTimeInMillis());
+        expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.endHour);
+        expected.set(Calendar.MINUTE, mScheduleInfo.endMinute);
+
+        long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
+        GregorianCalendar actual = new GregorianCalendar();
+        actual.setTimeInMillis(actualMs);
+        assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+                actualMs);
+    }
+
+    @Test
+    public void testGetNextChangeTime_startTomorrow() throws Exception {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+        mScheduleInfo.startHour = 1;
+        mScheduleInfo.endHour = 3;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = false;
+
+        Calendar expected = new GregorianCalendar();
+        expected.setTimeInMillis(cal.getTimeInMillis());
+        expected.add(Calendar.DATE, 1);
+        expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+        expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
+
+        long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
+        GregorianCalendar actual = new GregorianCalendar();
+        actual.setTimeInMillis(actualMs);
+        assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+                actualMs);
+    }
+
+    @Test
+    public void testGetNextChangeTime_endTomorrow() throws Exception {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+        mScheduleInfo.startHour = 22;
+        mScheduleInfo.endHour = 3;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = false;
+
+        Calendar expected = new GregorianCalendar();
+        expected.setTimeInMillis(cal.getTimeInMillis());
+        expected.add(Calendar.DATE, 1);
+        expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.endHour);
+        expected.set(Calendar.MINUTE, mScheduleInfo.endMinute);
+
+        long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
+        GregorianCalendar actual = new GregorianCalendar();
+        actual.setTimeInMillis(actualMs);
+        assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+                actualMs);
+    }
+
+    @Test
+    public void testShouldExitForAlarm_settingOff() {
+        mScheduleInfo.exitAtAlarm = false;
+        mScheduleInfo.nextAlarm = 1000;
+
+        assertFalse(mScheduleCalendar.shouldExitForAlarm(1000));
+    }
+
+    @Test
+    public void testShouldExitForAlarm_beforeAlarm() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 1000;
+
+        assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
+    }
+
+    @Test
+    public void testShouldExitForAlarm_noAlarm() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 0;
+
+        assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
+    }
+
+    @Test
+    public void testShouldExitForAlarm() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 1000;
+
+        assertTrue(mScheduleCalendar.shouldExitForAlarm(1000));
+    }
+
+    @Test
+    public void testMaybeSetNextAlarm_settingOff() {
+        mScheduleInfo.exitAtAlarm = false;
+        mScheduleInfo.nextAlarm = 0;
+
+        mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
+
+        assertEquals(0, mScheduleInfo.nextAlarm);
+    }
+
+    @Test
+    public void testMaybeSetNextAlarm_settingOn() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 0;
+
+        mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
+
+        assertEquals(2000, mScheduleInfo.nextAlarm);
+    }
+
+    @Test
+    public void testMaybeSetNextAlarm_alarmCanceled() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 10000;
+
+        mScheduleCalendar.maybeSetNextAlarm(1000, 0);
+
+        assertEquals(0, mScheduleInfo.nextAlarm);
+    }
+
+    @Test
+    public void testMaybeSetNextAlarm_earlierAlarm() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 2000;
+
+        mScheduleCalendar.maybeSetNextAlarm(1000, 1500);
+
+        assertEquals(1500, mScheduleInfo.nextAlarm);
+    }
+
+    @Test
+    public void testMaybeSetNextAlarm_laterAlarm() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 2000;
+
+        mScheduleCalendar.maybeSetNextAlarm(1000, 3000);
+
+        assertEquals(2000, mScheduleInfo.nextAlarm);
+    }
+
+    @Test
+    public void testMaybeSetNextAlarm_expiredAlarm() {
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 998;
+
+        mScheduleCalendar.maybeSetNextAlarm(1000, 999);
+
+        assertEquals(0, mScheduleInfo.nextAlarm);
+    }
+
+    @Test
+    public void testIsInSchedule_inScheduleOvernight() {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        mScheduleInfo.days = new int[] {getTodayDay()};
+        mScheduleInfo.startHour = 22;
+        mScheduleInfo.endHour = 3;
+        mScheduleInfo.startMinute = 15;
+        mScheduleInfo.endMinute = 15;
+
+        assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
+    }
+
+    @Test
+    public void testIsInSchedule_inScheduleSingleDay() {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 14);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        mScheduleInfo.days = new int[] {getTodayDay()};
+        mScheduleInfo.startHour = 12;
+        mScheduleInfo.endHour = 3;
+        mScheduleInfo.startMinute = 16;
+        mScheduleInfo.endMinute = 15;
+
+        assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
+    }
+
+    @Test
+    public void testIsInSchedule_notToday() {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 14);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
+        mScheduleInfo.days = new int[] {Calendar.FRIDAY, Calendar.SUNDAY};
+        mScheduleInfo.startHour = 12;
+        mScheduleInfo.startMinute = 16;
+        mScheduleInfo.endHour = 15;
+        mScheduleInfo.endMinute = 15;
+
+        assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
+    }
+
+    @Test
+    public void testIsInSchedule_startingSoon() {
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 14);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 59);
+        cal.set(Calendar.MILLISECOND, 0);
+        mScheduleInfo.days = new int[] {getTodayDay()};
+        mScheduleInfo.startHour = 14;
+        mScheduleInfo.endHour = 3;
+        mScheduleInfo.startMinute = 16;
+        mScheduleInfo.endMinute = 15;
+
+        assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
+    }
+
+    private int getTodayDay() {
+        return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
new file mode 100644
index 0000000..ddf46a0
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -0,0 +1,337 @@
+package com.android.server.notification;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Looper;
+import android.service.notification.Condition;
+import android.service.notification.ZenModeConfig;
+import android.support.test.InstrumentationRegistry;
+import android.test.ServiceTestCase;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+public class ScheduleConditionProviderTest extends ServiceTestCase<ScheduleConditionProvider> {
+
+    ScheduleConditionProvider mService;
+
+    @Rule
+    public final TestableContext mContext =
+            new TestableContext(InstrumentationRegistry.getContext(), null);
+
+    public ScheduleConditionProviderTest() {
+        super(ScheduleConditionProvider.class);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        Looper.prepare();
+
+        Intent startIntent =
+                new Intent("com.android.server.notification.ScheduleConditionProvider");
+        startIntent.setPackage("android");
+        bindService(startIntent);
+        mService = spy(getService());
+   }
+
+    @Test
+    public void testIsValidConditionId_incomplete() throws Exception {
+        Uri badConditionId = Uri.EMPTY;
+        assertFalse(mService.isValidConditionId(badConditionId));
+        assertEquals(Condition.STATE_ERROR,
+                mService.evaluateSubscriptionLocked(badConditionId, null, 0, 1000).state);
+    }
+
+    @Test
+    public void testIsValidConditionId() throws Exception {
+        ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
+        info.days = new int[] {1, 2, 4};
+        info.startHour = 8;
+        info.startMinute = 56;
+        info.nextAlarm = 1000;
+        info.exitAtAlarm = true;
+        info.endHour = 12;
+        info.endMinute = 9;
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        assertTrue(mService.isValidConditionId(conditionId));
+    }
+
+    @Test
+    public void testEvaluateSubscription_noAlarmExit_InSchedule() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; starts now
+        ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
+        info.days = new int[] {Calendar.FRIDAY};
+        info.startHour = now.get(Calendar.HOUR_OF_DAY);
+        info.startMinute = now.get(Calendar.MINUTE);
+        info.nextAlarm = 0;
+        info.exitAtAlarm = false;
+        info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
+        info.endMinute = info.startMinute;
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+        assertTrue(cal.isInSchedule(now.getTimeInMillis()));
+
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
+
+        assertEquals(Condition.STATE_TRUE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_noAlarmExit_InScheduleSnoozed() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; starts now
+        ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
+        info.days = new int[] {Calendar.FRIDAY};
+        info.startHour = now.get(Calendar.HOUR_OF_DAY);
+        info.startMinute = now.get(Calendar.MINUTE);
+        info.nextAlarm = 0;
+        info.exitAtAlarm = false;
+        info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
+        info.endMinute = info.startMinute;
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+        assertTrue(cal.isInSchedule(now.getTimeInMillis()));
+
+        mService.addSnoozed(conditionId);
+
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
+
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_noAlarmExit_beforeSchedule() {
+        Calendar now = new GregorianCalendar();
+        now.set(Calendar.HOUR_OF_DAY, 14);
+        now.set(Calendar.MINUTE, 15);
+        now.set(Calendar.SECOND, 59);
+        now.set(Calendar.MILLISECOND, 0);
+        now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
+
+        // Schedule - 1 hour long; starts in 1 second
+        ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
+        info.days = new int[] {Calendar.FRIDAY};
+        info.startHour = now.get(Calendar.HOUR_OF_DAY);
+        info.startMinute = now.get(Calendar.MINUTE) + 1;
+        info.nextAlarm = 0;
+        info.exitAtAlarm = false;
+        info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
+        info.endMinute = info.startMinute;
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
+
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_noAlarmExit_endSchedule() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; ends now
+        ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
+        info.days = new int[] {Calendar.FRIDAY};
+        info.startHour = now.get(Calendar.HOUR_OF_DAY) - 1;
+        info.startMinute = now.get(Calendar.MINUTE);
+        info.nextAlarm = 0;
+        info.exitAtAlarm = false;
+        info.endHour = now.get(Calendar.HOUR_OF_DAY);
+        info.endMinute = now.get(Calendar.MINUTE);
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
+
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_alarmSetBeforeInSchedule() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; starts now, ends with alarm
+        ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+
+        // an hour before start, update with an alarm that will fire during the schedule
+        mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() - 1000, now.getTimeInMillis() + 1000);
+
+        // at start, should be in dnd
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // at alarm fire time, should exit dnd
+        assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000));
+        assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
+                cal.shouldExitForAlarm(now.getTimeInMillis() + 1000));
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 1000, 0);
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_alarmSetInSchedule() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; starts now, ends with alarm
+        ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+
+        // at start, should be in dnd
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), 0);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // in schedule, update with alarm time, should be in dnd
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // at alarm fire time, should exit dnd
+        assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000));
+        assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
+                cal.shouldExitForAlarm(now.getTimeInMillis() + 1000));
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 1000, 0);
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_earlierAlarmSet() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; starts now, ends with alarm
+        ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+
+        // at start, should be in dnd, alarm in 2000 ms
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 2000);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // in schedule, update with earlier alarm time, should be in dnd
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // at earliest alarm fire time, should exit dnd
+        assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000));
+        assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
+                cal.shouldExitForAlarm(now.getTimeInMillis() + 1000));
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 1000, 0);
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_laterAlarmSet() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; starts now, ends with alarm
+        ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+
+        // at start, should be in dnd, alarm in 500 ms
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // in schedule, update with later alarm time, should be in dnd
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 250, now.getTimeInMillis() + 1000);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // at earliest alarm fire time, should exit dnd
+        assertTrue(cal.isInSchedule(now.getTimeInMillis() + 500));
+        assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
+                cal.shouldExitForAlarm(now.getTimeInMillis() + 500));
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 500, 0);
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    @Test
+    public void testEvaluateSubscription_alarmCanceled() {
+        Calendar now = getNow();
+
+        // Schedule - 1 hour long; starts now, ends with alarm
+        ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
+        Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
+        ScheduleCalendar cal = new ScheduleCalendar();
+        cal.setSchedule(info);
+
+        // at start, should be in dnd, alarm in 500 ms
+        Condition condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // in schedule, cancel alarm
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 250, 0);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // at previous alarm time, should not exit DND
+        assertTrue(cal.isInSchedule(now.getTimeInMillis() + 500));
+        assertFalse(cal.shouldExitForAlarm(now.getTimeInMillis() + 500));
+        condition = mService.evaluateSubscriptionLocked(
+                conditionId, cal, now.getTimeInMillis() + 500, 0);
+        assertEquals(Condition.STATE_TRUE, condition.state);
+
+        // end of schedule, exit DND
+        now.add(Calendar.HOUR_OF_DAY, 1);
+        condition = mService.evaluateSubscriptionLocked(conditionId, cal, now.getTimeInMillis(), 0);
+        assertEquals(Condition.STATE_FALSE, condition.state);
+    }
+
+    private Calendar getNow() {
+        Calendar now = new GregorianCalendar();
+        now.set(Calendar.HOUR_OF_DAY, 14);
+        now.set(Calendar.MINUTE, 16);
+        now.set(Calendar.SECOND, 0);
+        now.set(Calendar.MILLISECOND, 0);
+        now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
+        return now;
+    }
+
+    private ZenModeConfig.ScheduleInfo getScheduleEndsInHour(Calendar now) {
+        ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
+        info.days = new int[] {Calendar.FRIDAY};
+        info.startHour = now.get(Calendar.HOUR_OF_DAY);
+        info.startMinute = now.get(Calendar.MINUTE);
+        info.exitAtAlarm = true;
+        info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
+        info.endMinute = now.get(Calendar.MINUTE);
+        return info;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
new file mode 100644
index 0000000..5676510
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import android.app.ActivityOptions;
+import android.app.IApplicationThread;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.service.voice.IVoiceInteractionSession;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.server.am.ActivityStarter.Factory;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+import java.util.Random;
+
+/**
+ * Tests for the {@link ActivityStartController} class.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:ActivityStartControllerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStartControllerTests extends ActivityTestsBase {
+    private ActivityManagerService mService;
+    private ActivityStartController mController;
+    private Factory mFactory;
+    private ActivityStarter mStarter;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mService = createActivityManagerService();
+        mFactory = mock(Factory.class);
+        mStarter = mock(ActivityStarter.class);
+        doReturn(mStarter).when(mFactory).getStarter(any(), any(), any(), any());
+        mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
+    }
+
+    /**
+     * Ensures that the starter is correctly invoked on
+     * {@link ActivityStartController#startActivity}
+     */
+    @Test
+    @Presubmit
+    public void testStartActivity() {
+        final Random random = new Random();
+
+        final IApplicationThread applicationThread = mock(IApplicationThread.class);
+        final Intent intent = mock(Intent.class);
+        final Intent ephemeralIntent = mock(Intent.class);
+        final String resolvedType = "TestType";
+        final ActivityInfo aInfo = mock(ActivityInfo.class);
+        final ResolveInfo rInfo = mock(ResolveInfo.class);
+        final IVoiceInteractionSession voiceInteractionSession =
+                mock(IVoiceInteractionSession.class);
+        final IVoiceInteractor voiceInteractor = mock(IVoiceInteractor.class);
+        final IBinder resultTo = mock(IBinder.class);
+        final String resultWho = "resultWho";
+        final int requestCode = random.nextInt();
+        final int callingPid = random.nextInt();
+        final int callingUid = random.nextInt();
+        final String callingPackage = "callingPackage";
+        final int realCallingPid = random.nextInt();
+        final int realCallingUid = random.nextInt();
+        final int startFlags = random.nextInt();
+        final ActivityOptions options = mock(ActivityOptions.class);
+        final boolean ignoreTargetSecurity = random.nextBoolean();
+        final boolean componentSpecified = random.nextBoolean();
+        final ActivityRecord[] outActivity = new ActivityRecord[1];
+        final TaskRecord inTask = mock(TaskRecord.class);
+        final String reason ="reason";
+
+        mController.startActivity(applicationThread, intent, ephemeralIntent, resolvedType,
+                aInfo, rInfo, voiceInteractionSession, voiceInteractor, resultTo, resultWho,
+                requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid,
+                startFlags, options, ignoreTargetSecurity, componentSpecified, outActivity, inTask,
+                reason);
+
+        // The starter should receive a start command with the originally provided parameters
+        verify(mStarter, times(1)).startActivityLocked(eq(applicationThread), eq(intent),
+                eq(ephemeralIntent), eq(resolvedType), eq(aInfo), eq(rInfo),
+                eq(voiceInteractionSession), eq(voiceInteractor), eq(resultTo), eq(resultWho),
+                eq(requestCode), eq(callingPid), eq(callingUid), eq(callingPackage),
+                eq(realCallingPid), eq(realCallingUid), eq(startFlags), eq(options),
+                eq(ignoreTargetSecurity), eq(componentSpecified), eq(outActivity), eq(inTask),
+                eq(reason));
+    }
+
+    /**
+     * Ensures that pending launches are processed.
+     */
+    @Test
+    @Presubmit
+    public void testPendingActivityLaunches() {
+        final Random random = new Random();
+
+        final ActivityRecord activity = new ActivityBuilder(mService).build();
+        final ActivityRecord source = new ActivityBuilder(mService).build();
+        final int startFlags = random.nextInt();
+        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ProcessRecord process= new ProcessRecord(null, mService.mContext.getApplicationInfo(),
+                "name", 12345);
+
+        mController.addPendingActivityLaunch(
+                new PendingActivityLaunch(activity, source, startFlags, stack, process));
+        final boolean resume = random.nextBoolean();
+        mController.doPendingActivityLaunches(resume);
+
+        verify(mStarter, times(1)).startActivity(eq(activity), eq(source), eq(null),
+                eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 1cec0d9..471726b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -65,10 +65,10 @@
 import com.android.internal.os.BatteryStatsImpl;
 
 /**
- * Tests for the {@link ActivityStack} class.
+ * Tests for the {@link ActivityStarter} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.am.ActivityStarterTests
+ *  atest FrameworksServicesTests:ActivityStarterTests
  */
 @SmallTest
 @Presubmit
@@ -76,7 +76,7 @@
 public class ActivityStarterTests extends ActivityTestsBase {
     private ActivityManagerService mService;
     private ActivityStarter mStarter;
-    private IPackageManager mPackageManager;
+    private ActivityStartController mController;
 
     private static final int PRECONDITION_NO_CALLER_APP = 1;
     private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
@@ -94,7 +94,9 @@
     public void setUp() throws Exception {
         super.setUp();
         mService = createActivityManagerService();
-        mStarter = new ActivityStarter(mService);
+        mController = mock(ActivityStartController.class);
+        mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
+                mock(ActivityStartInterceptor.class));
     }
 
     @Test
@@ -176,8 +178,10 @@
             int expectedResult) {
         final ActivityManagerService service = createActivityManagerService();
         final IPackageManager packageManager = mock(IPackageManager.class);
-        final ActivityStarter starter = new ActivityStarter(service);
+        final ActivityStartController controller = mock(ActivityStartController.class);
 
+        final ActivityStarter starter = new ActivityStarter(controller, service,
+                service.mStackSupervisor, mock(ActivityStartInterceptor.class));
         final IApplicationThread caller = mock(IApplicationThread.class);
 
         // If no caller app, return {@code null} {@link ProcessRecord}.
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 13ca10c..0d03863 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -479,7 +479,7 @@
                 () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
                 () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
-                        SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect()));
+                        SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
         assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
         assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
         assertSecurityException(expectCallable,
@@ -781,4 +781,4 @@
                     callingUid, allowed);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
index 87c587a..c40b411 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
@@ -20,6 +20,7 @@
 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_ID;
 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME;
 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -102,11 +103,40 @@
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(mMonitorMock).onEvent(bundleCaptor.capture());
         Bundle eventBundle = bundleCaptor.getValue();
-        assertThat(eventBundle.size()).isEqualTo(6);
+        assertThat(eventBundle.size()).isEqualTo(7);
         assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1);
         assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2);
         assertThat(eventBundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME)).isEqualTo("test.package");
         assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_PACKAGE_VERSION)).isEqualTo(3);
+        assertThat(eventBundle.getLong(EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)).isEqualTo(3);
+        assertThat(eventBundle.getInt("key1")).isEqualTo(4);
+        assertThat(eventBundle.getString("key2")).isEqualTo("value2");
+    }
+
+    @Test
+    public void monitorEvent_packageAndExtrasAreNotNull_fillsBundleCorrectlyLong() throws Exception {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = "test.package";
+        packageInfo.versionCode = 3;
+        packageInfo.versionCodeMajor = 10;
+        Bundle extras = new Bundle();
+        extras.putInt("key1", 4);
+        extras.putString("key2", "value2");
+
+        IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1,
+                packageInfo, 2, extras);
+
+        assertThat(result).isEqualTo(mMonitorMock);
+        ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(mMonitorMock).onEvent(bundleCaptor.capture());
+        Bundle eventBundle = bundleCaptor.getValue();
+        assertThat(eventBundle.size()).isEqualTo(7);
+        assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1);
+        assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2);
+        assertThat(eventBundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME)).isEqualTo("test.package");
+        assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_PACKAGE_VERSION)).isEqualTo(3);
+        assertThat(eventBundle.getLong(EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)).isEqualTo(
+                (10L << 32) | 3);
         assertThat(eventBundle.getInt("key1")).isEqualTo(4);
         assertThat(eventBundle.getString("key2")).isEqualTo("value2");
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index ae4b569..d168479 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -373,6 +373,11 @@
         }
 
         @Override
+        void settingsSystemPutString(String name, String value) {
+            services.settings.settingsSystemPutString(name, value);
+        }
+
+        @Override
         int settingsGlobalGetInt(String name, int def) {
             return services.settings.settingsGlobalGetInt(name, def);
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 6de3395..ca918c6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3090,6 +3090,27 @@
         assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
     }
 
+    public void testSetSystemSettingFailWithNonWhitelistedSettings() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        assertExpectException(SecurityException.class, null, () ->
+                dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS_FOR_VR, "0"));
+    }
+
+    public void testSetSystemSettingFailWithPO() throws Exception {
+        setupProfileOwner();
+        assertExpectException(SecurityException.class, null, () ->
+                dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0"));
+    }
+
+    public void testSetSystemSetting() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0");
+        verify(getServices().settings).settingsSystemPutString(
+                Settings.System.SCREEN_BRIGHTNESS, "0");
+    }
+
     public void testSetTime() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 99f54ba..4ee5ba6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -379,6 +379,9 @@
         public void settingsGlobalPutString(String name, String value) {
         }
 
+        public void settingsSystemPutString(String name, String value) {
+        }
+
         public int settingsGlobalGetInt(String name, int value) {
             return 0;
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 025ebc3..926a911 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1075,6 +1075,7 @@
         final PackageInfo ret = new PackageInfo();
         ret.packageName = pi.packageName;
         ret.versionCode = pi.versionCode;
+        ret.versionCodeMajor = pi.versionCodeMajor;
         ret.lastUpdateTime = pi.lastUpdateTime;
 
         ret.applicationInfo = new ApplicationInfo(pi.applicationInfo);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
deleted file mode 100644
index e6b4540f..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.pm;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.platform.test.annotations.GlobalPresubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.ArraySet;
-
-import com.android.internal.os.RoSystemProperties;
-import com.android.internal.util.ArrayUtils;
-import com.android.server.SystemConfig;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static junit.framework.Assert.assertTrue;
-
-
-/**
- * Presubmit tests for {@link PackageManager}.
- */
-@RunWith(AndroidJUnit4.class)
-public class PackageManagerPresubmitTest {
-
-    private Context mContext;
-
-    private PackageManager mPackageManager;
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getContext();
-        mPackageManager = mContext.getPackageManager();
-    }
-
-    /**
-     * <p>This test ensures that all signature|privileged permissions are granted to priv-apps.
-     * If CONTROL_PRIVAPP_PERMISSIONS_ENFORCE is set, the test also verifies that
-     * granted permissions are whitelisted in {@link SystemConfig}
-     */
-    @Test
-    @SmallTest
-    @GlobalPresubmit
-    public void testPrivAppPermissions() throws PackageManager.NameNotFoundException {
-        List<PackageInfo> installedPackages = mPackageManager
-                .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES | GET_PERMISSIONS);
-        for (PackageInfo packageInfo : installedPackages) {
-            if (!packageInfo.applicationInfo.isPrivilegedApp()
-                    || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(packageInfo.packageName)) {
-                continue;
-            }
-            testPackagePrivAppPermission(packageInfo);
-        }
-
-    }
-
-    private void testPackagePrivAppPermission(PackageInfo packageInfo)
-            throws PackageManager.NameNotFoundException {
-        String packageName = packageInfo.packageName;
-        ArraySet<String> privAppPermissions = SystemConfig.getInstance()
-                .getPrivAppPermissions(packageName);
-        if (ArrayUtils.isEmpty(packageInfo.requestedPermissions)) {
-            return;
-        }
-        for (int i = 0; i < packageInfo.requestedPermissions.length; i++) {
-            String pName = packageInfo.requestedPermissions[i];
-            int protectionLevel;
-            boolean platformPermission;
-            try {
-                PermissionInfo permissionInfo = mPackageManager.getPermissionInfo(pName, 0);
-                platformPermission = PackageManagerService.PLATFORM_PACKAGE_NAME.equals(
-                        permissionInfo.packageName);
-                protectionLevel = permissionInfo.protectionLevel;
-            } catch (PackageManager.NameNotFoundException e) {
-                continue;
-            }
-            if ((protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
-                boolean granted = (packageInfo.requestedPermissionsFlags[i]
-                        & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
-                // if privapp permissions are enforced, platform permissions must be whitelisted
-                // in SystemConfig
-                if (platformPermission && RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
-                    assertTrue("Permission " + pName + " should be declared in "
-                                    + "privapp-permissions-<category>.xml file for package "
-                                    + packageName,
-                            privAppPermissions != null && privAppPermissions.contains(pName));
-                }
-                assertTrue("Permission " + pName + " should be granted to " + packageName, granted);
-            }
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index fd105bc..0995f2e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -577,7 +577,7 @@
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         pkg.usesStaticLibraries = new ArrayList<>(
                 Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3"));
-        pkg.usesStaticLibrariesVersions = new int[] {2, 4, 6};
+        pkg.usesStaticLibrariesVersions = new long[] {2, 4, 6};
         settings.insertPackageSettingLPw(ps, pkg);
         assertEquals(pkg, ps.pkg);
         assertArrayEquals(pkg.usesStaticLibraries.toArray(new String[0]), ps.usesStaticLibraries);
@@ -602,6 +602,11 @@
                 Arrays.equals(a, b));
     }
 
+    private void assertArrayEquals(long[] a, long[] b) {
+        assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
+                Arrays.equals(a, b));
+    }
+
     private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
             boolean userStateChanged) {
         verifyUserState(userState, oldUserState, userStateChanged, false /*notLaunched*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 1f9a243..36cc3a0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -471,7 +471,7 @@
         pkg.usesStaticLibraries.add("foo23");
         pkg.usesStaticLibrariesCertDigests = new String[1][];
         pkg.usesStaticLibrariesCertDigests[0] = new String[] { "digest" };
-        pkg.usesStaticLibrariesVersions = new int[] { 100 };
+        pkg.usesStaticLibrariesVersions = new long[] { 100 };
 
         pkg.libraryNames = new ArrayList<>();
         pkg.libraryNames.add("foo10");
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index 7d73e82..0ea8d4f 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -1246,13 +1246,13 @@
     }
 
     private void configureUpdateAppPackageVersion(String updateAppPackageName,
-            int updataAppPackageVersion) throws Exception {
+            long updataAppPackageVersion) throws Exception {
         when(mMockPackageManagerHelper.getInstalledPackageVersion(updateAppPackageName))
                 .thenReturn(updataAppPackageVersion);
     }
 
     private void configureDataAppPackageVersion(String dataAppPackageName,
-            int dataAppPackageVersion) throws Exception {
+            long dataAppPackageVersion) throws Exception {
         when(mMockPackageManagerHelper.getInstalledPackageVersion(dataAppPackageName))
                 .thenReturn(dataAppPackageVersion);
     }
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 39d256a..b62d724 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,14 +16,17 @@
 
 package com.android.server.usage;
 
-import android.app.usage.AppStandby;
+import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+
+import android.app.usage.UsageStatsManager;
 import android.os.FileUtils;
 import android.test.AndroidTestCase;
 
 import java.io.File;
 
-import static android.app.usage.AppStandby.*;
-
 public class AppIdleHistoryTests extends AndroidTestCase {
 
     File mStorageDir;
@@ -85,12 +88,12 @@
         AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
 
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
-                AppStandby.REASON_USAGE);
+                UsageStatsManager.REASON_USAGE);
         // ACTIVE means not idle
         assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
 
         aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
-                AppStandby.REASON_USAGE);
+                UsageStatsManager.REASON_USAGE);
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
                 REASON_TIMEOUT);
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 40edfd2..87b34ab 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -16,9 +16,12 @@
 
 package com.android.server.usage;
 
-import static android.app.usage.AppStandby.*;
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -30,8 +33,8 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
-import android.app.usage.AppStandby;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -280,7 +283,7 @@
     public void testBuckets() throws Exception {
         AppStandbyController controller = setupController();
 
-        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+        assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
 
         reportEvent(controller, USER_INTERACTION, 0);
 
@@ -312,7 +315,7 @@
         AppStandbyController controller = setupController();
         mInjector.setDisplayOn(false);
 
-        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+        assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
 
         reportEvent(controller, USER_INTERACTION, 0);
 
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index cd3ae1a..26853a9 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -165,7 +165,7 @@
     }
 
     @Override
-    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
+    public long getFactoryPackageVersion(String packageName) throws NameNotFoundException {
         PackageInfo pi = null;
         Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
         if (userPackages == null) throw new NameNotFoundException();
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 2224de5..401f585 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -1512,8 +1512,8 @@
         // Ensure the API is correct before running waitForAndGetProvider
         assertEquals(firstPackage.packageName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
-        assertEquals(firstPackage.versionCode,
-                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode);
+        assertEquals(firstPackage.getLongVersionCode(),
+                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode());
         assertEquals(firstPackage.versionName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
 
@@ -1524,8 +1524,8 @@
         // Ensure the API is still correct after running waitForAndGetProvider
         assertEquals(firstPackage.packageName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
-        assertEquals(firstPackage.versionCode,
-                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode);
+        assertEquals(firstPackage.getLongVersionCode(),
+                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode());
         assertEquals(firstPackage.versionName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index 04f5e5e..e5cbdba 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -39,6 +39,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
@@ -343,4 +344,22 @@
         assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow);
         assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel);
     }
+
+    @Test
+    public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() throws Exception {
+        // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
+        // then we can drop all negative layering on the windowing side.
+
+        final WindowState anyWindow =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow");
+        final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
+                "TypeApplicationMediaChild");
+        final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
+                mDisplayContent, "TypeApplicationMediaOverlayChild");
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild);
+        assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child);
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index ee11241..5aef55b 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,10 +16,7 @@
 
 package com.android.server.usage;
 
-import static android.app.usage.AppStandby.*;
-
-import android.app.usage.AppStandby;
-import android.os.Environment;
+import android.app.usage.UsageStatsManager;
 import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
@@ -99,7 +96,8 @@
         final byte[] recent = new byte[HISTORY_SIZE];
         long lastUsedElapsedTime;
         long lastUsedScreenTime;
-        @StandbyBuckets int currentBucket;
+        @UsageStatsManager.StandbyBuckets
+        int currentBucket;
         String bucketingReason;
         int lastInformedBucket;
     }
@@ -190,14 +188,14 @@
                 + (elapsedRealtime - mElapsedSnapshot);
         appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
         appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
-        if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+        if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
             if (DEBUG) {
                 Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                         + ", reason=" + appUsageHistory.bucketingReason);
             }
         }
-        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+        appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
 
         return appUsageHistory.currentBucket;
     }
@@ -206,15 +204,15 @@
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                 elapsedRealtime, true);
-        if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
+        if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
             if (DEBUG) {
                 Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                         + ", reason=" + appUsageHistory.bucketingReason);
             }
         }
         // TODO: Should this be a different reason for partial usage?
-        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+        appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
 
         return appUsageHistory.currentBucket;
     }
@@ -271,8 +269,9 @@
             appUsageHistory = new AppUsageHistory();
             appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
             appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
-            appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_NEVER;
-            appUsageHistory.bucketingReason = REASON_DEFAULT;
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
+            appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
+            appUsageHistory.lastInformedBucket = -1;
             userHistory.put(packageName, appUsageHistory);
         }
         return appUsageHistory;
@@ -289,7 +288,7 @@
         if (appUsageHistory == null) {
             return false; // Default to not idle
         } else {
-            return appUsageHistory.currentBucket >= AppStandby.STANDBY_BUCKET_RARE;
+            return appUsageHistory.currentBucket >= UsageStatsManager.STANDBY_BUCKET_RARE;
             // Whether or not it's passed will now be externally calculated and the
             // bucket will be pushed to the history using setAppStandbyBucket()
             //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
@@ -333,12 +332,12 @@
         AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                 elapsedRealtime, true);
         if (idle) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
-            appUsageHistory.bucketingReason = REASON_FORCED;
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_RARE;
+            appUsageHistory.bucketingReason = UsageStatsManager.REASON_FORCED;
         } else {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
             // This is to pretend that the app was just used, don't freeze the state anymore.
-            appUsageHistory.bucketingReason = REASON_USAGE;
+            appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
         }
         return appUsageHistory.currentBucket;
     }
@@ -435,12 +434,12 @@
                         String currentBucketString = parser.getAttributeValue(null,
                                 ATTR_CURRENT_BUCKET);
                         appUsageHistory.currentBucket = currentBucketString == null
-                                ? AppStandby.STANDBY_BUCKET_ACTIVE
+                                ? UsageStatsManager.STANDBY_BUCKET_ACTIVE
                                 : Integer.parseInt(currentBucketString);
                         appUsageHistory.bucketingReason =
                                 parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
                         if (appUsageHistory.bucketingReason == null) {
-                            appUsageHistory.bucketingReason = REASON_DEFAULT;
+                            appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
                         }
                         userHistory.put(packageName, appUsageHistory);
                     }
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 3c099c2..4527879 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,15 +16,23 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageStatsManager.REASON_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.admin.DevicePolicyManager;
-import android.app.usage.AppStandby;
-import android.app.usage.AppStandby.StandbyBuckets;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
@@ -102,10 +110,10 @@
     };
 
     static final int[] THRESHOLD_BUCKETS = {
-            AppStandby.STANDBY_BUCKET_ACTIVE,
-            AppStandby.STANDBY_BUCKET_WORKING_SET,
-            AppStandby.STANDBY_BUCKET_FREQUENT,
-            AppStandby.STANDBY_BUCKET_RARE
+            STANDBY_BUCKET_ACTIVE,
+            STANDBY_BUCKET_WORKING_SET,
+            STANDBY_BUCKET_FREQUENT,
+            STANDBY_BUCKET_RARE
     };
 
     // To name the lock for stack traces
@@ -371,18 +379,18 @@
                 }
                 if (isSpecial) {
                     maybeInformListeners(packageName, userId, elapsedRealtime,
-                            AppStandby.STANDBY_BUCKET_ACTIVE);
+                            STANDBY_BUCKET_ACTIVE);
                 } else {
                     synchronized (mAppIdleLock) {
                         String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
                                 userId, elapsedRealtime);
                         // If the bucket was forced by the developer, leave it alone
-                        if (AppStandby.REASON_FORCED.equals(bucketingReason)) {
+                        if (REASON_FORCED.equals(bucketingReason)) {
                             continue;
                         }
                         // If the bucket was moved up due to usage, let the timeouts apply.
-                        if (AppStandby.REASON_USAGE.equals(bucketingReason)
-                                || AppStandby.REASON_TIMEOUT.equals(bucketingReason)) {
+                        if (REASON_USAGE.equals(bucketingReason)
+                                || REASON_TIMEOUT.equals(bucketingReason)) {
                             int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
                                     elapsedRealtime);
                             int newBucket = getBucketForLocked(packageName, userId,
@@ -393,7 +401,7 @@
                             }
                             if (oldBucket < newBucket) {
                                 mAppIdleHistory.setAppStandbyBucket(packageName, userId,
-                                        elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
+                                        elapsedRealtime, newBucket, REASON_TIMEOUT);
                                 maybeInformListeners(packageName, userId, elapsedRealtime,
                                         newBucket);
                             }
@@ -742,7 +750,7 @@
             long elapsedRealtime, boolean shouldObfuscateInstantApps) {
         if (shouldObfuscateInstantApps &&
                 mInjector.isPackageEphemeral(userId, packageName)) {
-            return AppStandby.STANDBY_BUCKET_ACTIVE;
+            return STANDBY_BUCKET_ACTIVE;
         }
 
         return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
@@ -811,7 +819,7 @@
     }
 
     void informListeners(String packageName, int userId, int bucket) {
-        final boolean idle = bucket >= AppStandby.STANDBY_BUCKET_RARE;
+        final boolean idle = bucket >= STANDBY_BUCKET_RARE;
         for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
             listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
         }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 65c1cef..0572771 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -20,11 +20,11 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
-import android.app.usage.AppStandby;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
-import android.app.usage.AppStandby.StandbyBuckets;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
@@ -149,6 +149,8 @@
 
         publishLocalService(UsageStatsManagerInternal.class, new LocalService());
         publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
+        // Make sure we initialize the data, in case job scheduler needs it early.
+        getUserDataAndInitializeIfNeededLocked(UserHandle.USER_SYSTEM, mSystemTimeSnapshot);
     }
 
     @Override
@@ -678,18 +680,22 @@
 
         @Override
         public int getAppStandbyBucket(String packageName, String callingPackage, int userId) {
-            if (!hasPermission(callingPackage)) {
-                throw new SecurityException("Don't have permission to query app standby bucket");
-            }
-
             final int callingUid = Binder.getCallingUid();
             try {
                 userId = ActivityManager.getService().handleIncomingUser(
-                        Binder.getCallingPid(), callingUid, userId, false, true,
+                        Binder.getCallingPid(), callingUid, userId, false, false,
                         "getAppStandbyBucket", null);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
+            // If the calling app is asking about itself, continue, else check for permission.
+            if (mPackageManagerInternal.getPackageUid(packageName, PackageManager.MATCH_ANY_USER,
+                    userId) != callingUid) {
+                if (!hasPermission(callingPackage)) {
+                    throw new SecurityException(
+                            "Don't have permission to query app standby bucket");
+                }
+            }
             final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(callingUid,
                     userId);
             final long token = Binder.clearCallingIdentity();
@@ -707,6 +713,10 @@
             getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
                     "No permission to change app standby state");
 
+            if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE
+                    || bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) {
+                throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
+            }
             final int callingUid = Binder.getCallingUid();
             try {
                 userId = ActivityManager.getService().handleIncomingUser(
@@ -717,8 +727,13 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
+                // Caller cannot set their own standby state
+                if (mPackageManagerInternal.getPackageUid(packageName,
+                        PackageManager.MATCH_ANY_USER, userId) == callingUid) {
+                    throw new IllegalArgumentException("Cannot set your own standby bucket");
+                }
                 mAppStandby.setAppStandbyBucket(packageName, userId, bucket,
-                        AppStandby.REASON_PREDICTED + ":" + callingUid,
+                        UsageStatsManager.REASON_PREDICTED + ":" + callingUid,
                         SystemClock.elapsedRealtime());
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a07f2bb..16150ba 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -858,7 +858,8 @@
          * @hide
          */
         @IntDef({HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_DEST_NOT_SUPPORTED,
-                HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED})
+                HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED,
+                HANDOVER_FAILURE_ONGOING_EMERG_CALL})
         @Retention(RetentionPolicy.SOURCE)
         public @interface HandoverFailureErrors {}
 
@@ -886,6 +887,12 @@
          */
         public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4;
 
+        /**
+         * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there
+         * is ongoing emergency call.
+         */
+        public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5;
+
 
         /**
          * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
@@ -1935,6 +1942,15 @@
         }
     }
 
+    /** {@hide} */
+    final void internalOnHandoverFailed(int error) {
+        for (CallbackRecord<Callback> record : mCallbackRecords) {
+            final Call call = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(() -> callback.onHandoverFailed(call, error));
+        }
+    }
+
     private void fireStateChanged(final int newState) {
         for (CallbackRecord<Callback> record : mCallbackRecords) {
             final Call call = this;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 2bb1c4e..3c32614 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Notification;
 import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
@@ -397,9 +396,7 @@
     /**
      * Set by the framework to indicate that a connection has an active RTT session associated with
      * it.
-     * @hide
      */
-    @TestApi
     public static final int PROPERTY_IS_RTT = 1 << 8;
 
     //**********************************************************************************************
@@ -831,9 +828,7 @@
 
     /**
      * Provides methods to read and write RTT data to/from the in-call app.
-     * @hide
      */
-    @TestApi
     public static final class RttTextStream {
         private static final int READ_BUFFER_SIZE = 1000;
         private final InputStreamReader mPipeFromInCall;
@@ -2608,10 +2603,8 @@
     /**
      * Informs listeners that a previously requested RTT session via
      * {@link ConnectionRequest#isRequestingRtt()} or
-     * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded.
-     * @hide
+     * {@link #onStartRtt(RttTextStream)} has succeeded.
      */
-    @TestApi
     public final void sendRttInitiationSuccess() {
         setRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
@@ -2619,14 +2612,11 @@
 
     /**
      * Informs listeners that a previously requested RTT session via
-     * {@link ConnectionRequest#isRequestingRtt()} or
-     * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)}
+     * {@link ConnectionRequest#isRequestingRtt()} or {@link #onStartRtt(RttTextStream)}
      * has failed.
      * @param reason One of the reason codes defined in {@link RttModifyStatus}, with the
      *               exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
-     * @hide
      */
-    @TestApi
     public final void sendRttInitiationFailure(int reason) {
         unsetRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
@@ -2635,9 +2625,7 @@
     /**
      * Informs listeners that a currently active RTT session has been terminated by the remote
      * side of the coll.
-     * @hide
      */
-    @TestApi
     public final void sendRttSessionRemotelyTerminated() {
         mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
     }
@@ -2645,9 +2633,7 @@
     /**
      * Informs listeners that the remote side of the call has requested an upgrade to include an
      * RTT session in the call.
-     * @hide
      */
-    @TestApi
     public final void sendRemoteRttRequest() {
         mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this));
     }
@@ -2864,17 +2850,13 @@
      * request, respectively.
      * @param rttTextStream The object that should be used to send text to or receive text from
      *                      the in-call app.
-     * @hide
      */
-    @TestApi
     public void onStartRtt(@NonNull RttTextStream rttTextStream) {}
 
     /**
      * Notifies this {@link Connection} that it should terminate any existing RTT communication
      * channel. No response to Telecom is needed for this method.
-     * @hide
      */
-    @TestApi
     public void onStopRtt() {}
 
     /**
@@ -2882,11 +2864,9 @@
      * request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is
      * indicated by the supplied {@link RttTextStream} being non-null, and rejection is
      * indicated by {@code rttTextStream} being {@code null}
-     * @hide
      * @param rttTextStream The object that should be used to send text to or receive text from
      *                      the in-call app.
      */
-    @TestApi
     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
 
     /**
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index e169e5f..658b473 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -310,9 +309,7 @@
      * send and receive RTT text to/from the in-call app.
      * @return An instance of {@link android.telecom.Connection.RttTextStream}, or {@code null}
      * if this connection request is not requesting an RTT session upon connection establishment.
-     * @hide
      */
-    @TestApi
     public Connection.RttTextStream getRttTextStream() {
         if (isRequestingRtt()) {
             return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
@@ -324,9 +321,7 @@
     /**
      * Convenience method for determining whether the ConnectionRequest is requesting an RTT session
      * @return {@code true} if RTT is requested, {@code false} otherwise.
-     * @hide
      */
-    @TestApi
     public boolean isRequestingRtt() {
         return mRttPipeFromInCall != null && mRttPipeToInCall != null;
     }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 7e83306..2834201 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1371,9 +1371,19 @@
                 isIncoming,
                 isUnknown);
 
-        Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
-                : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
-                : onCreateOutgoingConnection(callManagerAccount, request);
+        Connection connection = null;
+        if (request.getExtras() != null && request.getExtras().getBoolean(
+                TelecomManager.EXTRA_IS_HANDOVER,false)) {
+            if (!isIncoming) {
+                connection = onCreateOutgoingHandoverConnection(callManagerAccount, request);
+            } else {
+                // Todo: Call onCreateIncommingHandoverConnection()
+            }
+        } else {
+            connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
+                    : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
+                    : onCreateOutgoingConnection(callManagerAccount, request);
+        }
         Log.d(this, "createConnection, connection: %s", connection);
         if (connection == null) {
             connection = Connection.createFailedConnection(
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index d558bba..74fa62d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -80,6 +80,7 @@
     private static final int MSG_ON_CONNECTION_EVENT = 9;
     private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10;
     private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
+    private static final int MSG_ON_HANDOVER_FAILED = 12;
 
     /** Default Handler used to consolidate binder method calls onto a single thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -150,6 +151,12 @@
                     mPhone.internalOnRttInitiationFailure(callId, reason);
                     break;
                 }
+                case MSG_ON_HANDOVER_FAILED: {
+                    String callId = (String) msg.obj;
+                    int error = msg.arg1;
+                    mPhone.internalOnHandoverFailed(callId, error);
+                    break;
+                }
                 default:
                     break;
             }
@@ -225,6 +232,11 @@
         public void onRttInitiationFailure(String callId, int reason) {
             mHandler.obtainMessage(MSG_ON_RTT_INITIATION_FAILURE, reason, 0, callId).sendToTarget();
         }
+
+        @Override
+        public void onHandoverFailed(String callId, int error) {
+            mHandler.obtainMessage(MSG_ON_HANDOVER_FAILED, error, 0, callId).sendToTarget();
+        }
     }
 
     private Phone.Listener mPhoneListener = new Phone.Listener() {
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 421b1a4..b5394b9 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -223,6 +223,13 @@
         }
     }
 
+    final void internalOnHandoverFailed(String callId, int error) {
+        Call call = mCallByTelecomCallId.get(callId);
+        if (call != null) {
+            call.internalOnHandoverFailed(error);
+        }
+    }
+
     /**
      * Called to destroy the phone and cleanup any lingering calls.
      */
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index e8cf8e9..110109e 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -54,4 +54,6 @@
     void onRttUpgradeRequest(String callId, int id);
 
     void onRttInitiationFailure(String callId, int reason);
+
+    void onHandoverFailed(String callId, int error);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 69371a1..e9feb89 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -735,6 +735,14 @@
     public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
 
     /**
+     * Flag specifying whether signal strength is hidden in SIM Status screen,
+     * default to false.
+     * @hide
+     */
+    public static final String KEY_HIDE_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL =
+        "hide_signal_strength_in_sim_status_bool";
+
+    /**
      * Flag specifying whether an additional (client initiated) intent needs to be sent on System
      * update
      */
@@ -989,6 +997,12 @@
     public static final String KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL =
             "stk_disable_launch_browser_bool";
 
+    /**
+     * Boolean indicating if show data RAT icon on status bar even when data is disabled
+     * @hide
+     */
+    public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL =
+            "always_show_data_rat_icon_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.
@@ -1768,6 +1782,7 @@
         sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
         sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
@@ -1967,6 +1982,7 @@
         sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_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);
     }
 
     /**
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index f392570..a554c69 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -347,6 +347,7 @@
 
                     @Override
                     public void onServiceDisconnected(ComponentName name) {
+                        Log.w(LOG_TAG, "bindAndInitialize: Remote service disconnected");
                         sIsInitialized.set(false);
                         mService.set(null);
                     }
@@ -385,6 +386,7 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
@@ -438,6 +440,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return;
         }
@@ -521,6 +524,7 @@
             downloadService.download(request);
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
@@ -542,6 +546,7 @@
             return downloadService.listPendingDownloads(mSubscriptionId);
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return Collections.emptyList();
         }
@@ -583,6 +588,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return;
         }
@@ -622,6 +628,7 @@
                 }
             } catch (RemoteException e) {
                 mService.set(null);
+                sIsInitialized.set(false);
                 sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             }
         } finally {
@@ -658,6 +665,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return;
         }
@@ -686,6 +694,7 @@
             return downloadService.getDownloadStatus(downloadRequest, fileInfo);
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
             return STATUS_UNKNOWN;
         }
@@ -727,6 +736,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
+            sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 81806e5..99fc9b3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6641,6 +6641,7 @@
      * @return PRLVersion or null if error.
      * @hide
      */
+    @SystemApi
     public String getCdmaPrlVersion() {
         return getCdmaPrlVersion(getSubId());
     }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a13af5f..2507cfee 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -63,7 +63,7 @@
      * embedded SIM.
      *
      * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
-     * {@link #isEnabled} is false or if the device is already provisioned.
+     * {@link #isEnabled} is false.
      *
      * TODO(b/35851809): Make this a SystemApi.
      */
diff --git a/telephony/java/com/android/internal/telephony/ICallService.aidl b/telephony/java/com/android/internal/telephony/ICallService.aidl
deleted file mode 100644
index cb9b2e8..0000000
--- a/telephony/java/com/android/internal/telephony/ICallService.aidl
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import com.android.internal.telephony.ICallServiceAdapter;
-
-/**
- * Service interface for services which would like to provide calls to be
- * managed by the system in-call UI.
- *
- * This interface provides methods that the android framework can use to deliver commands
- * for calls provided by this call service including making new calls and disconnecting
- * existing ones. A binding to ICallService implementations exists for two conditions:
- * 1) There exists one or more live calls for that call service,
- * 2) Prior to an outbound call to test if this call service is compatible with the outgoing call.
- */
-oneway interface ICallService {
-
-    /**
-     * Determines if the CallService can make calls to the handle.
-     * TODO(santoscordon): Move this method into its own service interface long term.
-     * TODO(santoscordon): Add response callback parameter.
-     */
-    void isCompatibleWith(String handle);
-
-    /**
-     * Attempts to call the relevant party using the specified handle, be it a phone number,
-     * SIP address, or some other kind of user ID.  Note that the set of handle types is
-     * dynamically extensible since call providers should be able to implement arbitrary
-     * handle-calling systems.  See {@link #isCompatibleWith}.
-     * TODO(santoscordon): Should this have a response attached to it to ensure that the call
-     * service actually plans to make the call?
-     */
-    void call(String handle);
-
-    /**
-     * Disconnects the call identified by callId.
-     */
-    void disconnect(String callId);
-
-    /**
-     * Sets an implementation of ICallServiceAdapter which the call service can use to add new calls
-     * and communicate state changes of existing calls. This is the first method that is called
-     * after a the framework binds to the call service.
-     */
-    void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter);
-}
diff --git a/telephony/java/com/android/internal/telephony/ICallServiceAdapter.aidl b/telephony/java/com/android/internal/telephony/ICallServiceAdapter.aidl
deleted file mode 100644
index bc900f0..0000000
--- a/telephony/java/com/android/internal/telephony/ICallServiceAdapter.aidl
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import com.android.internal.telephony.CallInfo;
-
-/**
- * Provides methods for ICallService implementations to interact with the system phone app.
- */
-oneway interface ICallServiceAdapter {
-
-    /**
-     * Retrieves a new unique call id for use with newOutgoingCall and newIncomingCall.
-     */
-    void getNextCallId(/* TODO(santoscordon): Needs response object */);
-
-    /**
-     * Tells CallsManager of a new incoming call.
-     */
-    void newIncomingCall(String callId, in CallInfo info);
-
-    /**
-     * Tells CallsManager of a new outgoing call.
-     */
-    void newOutgoingCall(String callId, in CallInfo info);
-
-    /**
-     * Sets a call's state to active (e.g., an ongoing call where two parties can actively
-     * communicate).
-     */
-    void setActive(String callId);
-
-    /**
-     * Sets a call's state to ringing (e.g., an inbound ringing call).
-     */
-    void setRinging(String callId);
-
-    /**
-     * Sets a call's state to dialing (e.g., dialing an outbound call).
-     */
-    void setDialing(String callId);
-
-    /**
-     * Sets a call's state to disconnected.
-     */
-    void setDisconnected(String callId);
-}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 644ad49..5e015e0 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -18,7 +18,6 @@
 
 import android.app.PendingIntent;
 import android.telephony.SubscriptionInfo;
-import com.android.internal.telephony.ISubscriptionListener;
 
 interface ISub {
     /**
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 0c562e6..ce8019f 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -46,6 +46,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
@@ -1174,4 +1175,12 @@
             @Nullable DexModuleRegisterCallback callback) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ArtManager getArtManager() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/Android.mk b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
new file mode 100644
index 0000000..da1a08b
--- /dev/null
+++ b/tests/BackgroundDexOptServiceIntegrationTests/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+
+LOCAL_PACKAGE_NAME := BackgroundDexOptServiceIntegrationTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml
new file mode 100644
index 0000000..afae155
--- /dev/null
+++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.bgdexopttest">
+
+
+    <!-- Uses API introduced in O (26) -->
+    <uses-sdk
+        android:minSdkVersion="1"
+        android:targetSdkVersion="26" />
+
+    <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+    <uses-permission android:name="android.permission.SET_TIME" />
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.bgdexopttest"
+        android:label="Integration test for BackgroundDexOptService" />
+</manifest>
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml
new file mode 100644
index 0000000..9bb1e28
--- /dev/null
+++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidTest.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs BackgroundDexOptService Integration Tests">
+    <!--DeviceSetup should go before TimeSetter because it stops automatic update of time-->
+    <target_preparer
+        class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="auto-update-time" value="OFF"/>
+        <option name="auto-update-timezone" value="OFF"/>
+        <option name="set-property" key="pm.dexopt.downgrade_after_inactive_days" value="2"/>
+        <option name="set-property" key="pm.dexopt.disable_bg_dexopt" value="true"/>
+        <option name="set-property" key="pm.dexopt.inactive" value="verify"/>
+        <option name="set-property" key="pm.dexopt.bg-dexopt" value="speed"/>
+        <option name="restore-settings" value="true"/>
+        <option name="restore-properties" value="true"/>
+    </target_preparer>
+
+    <!--Test app needs to be installed when we change its settings below-->
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="BackgroundDexOptServiceIntegrationTests.apk"/>
+        <option name="cleanup-apks" value="true"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.SetPackagesRecentlyUsed">
+        <option name="package-recently-used-time" value="0d"/>
+        <option name="package-recently-used-name" value="com.android.frameworks.bgdexopttest"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RestartSystemServerTargetPreparer"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceStorageFiller">
+        <!--32GB-->
+        <!--necessary because a package cannot create a file larger than 100GB-->
+        <option name="free-bytes" value="34359738368"/>
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="BackgroundDexOptServiceIntegrationTests"/>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.frameworks.bgdexopttest"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
new file mode 100644
index 0000000..3734412
--- /dev/null
+++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.os.storage.StorageManager;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Integration tests for {@link BackgroundDexOptService}.
+ *
+ * Tests various scenarios around BackgroundDexOptService.
+ * 1. Under normal conditions, check that dexopt upgrades test app to
+ * $(getprop pm.dexopt.bg-dexopt).
+ * 2. Under low storage conditions and package is unused, check
+ * that dexopt downgrades test app to $(getprop pm.dexopt.inactive).
+ * 3. Under low storage conditions and package is recently used, check
+ * that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt).
+ *
+ * Each test case runs "cmd package bg-dexopt-job com.android.frameworks.bgdexopttest".
+ *
+ * The setup for these tests make sure this package has been configured to have been recently used
+ * plus installed far enough in the past. If a test case requires that this package has not been
+ * recently used, it sets the time forward more than
+ * `getprop pm.dexopt.downgrade_after_inactive_days` days.
+ *
+ * For tests that require low storage, the phone is filled up.
+ *
+ * Run with "atest BackgroundDexOptServiceIntegrationTests".
+ */
+@RunWith(JUnit4.class)
+public final class BackgroundDexOptServiceIntegrationTests {
+
+    private static final String TAG = BackgroundDexOptServiceIntegrationTests.class.getSimpleName();
+
+    // Name of package to test on.
+    private static final String PACKAGE_NAME = "com.android.frameworks.bgdexopttest";
+    // Name of file used to fill up storage.
+    private static final String BIG_FILE = "bigfile";
+    private static final String BG_DEXOPT_COMPILER_FILTER = SystemProperties.get(
+            "pm.dexopt.bg-dexopt");
+    private static final String DOWNGRADE_COMPILER_FILTER = SystemProperties.get(
+            "pm.dexopt.inactive");
+    private static final long DOWNGRADE_AFTER_DAYS = SystemProperties.getLong(
+            "pm.dexopt.downgrade_after_inactive_days", 0);
+    // Needs to be between 1.0 and 2.0.
+    private static final double LOW_STORAGE_MULTIPLIER = 1.5;
+
+    // The file used to fill up storage.
+    private File mBigFile;
+
+    // Remember start time.
+    @BeforeClass
+    public static void setUpAll() {
+        if (!SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt", false)) {
+            throw new RuntimeException(
+                    "bg-dexopt is not disabled (set pm.dexopt.disable_bg_dexopt to true)");
+        }
+        if (DOWNGRADE_AFTER_DAYS < 1) {
+            throw new RuntimeException(
+                    "pm.dexopt.downgrade_after_inactive_days must be at least 1");
+        }
+        if ("quicken".equals(BG_DEXOPT_COMPILER_FILTER)) {
+            throw new RuntimeException("pm.dexopt.bg-dexopt should not be \"quicken\"");
+        }
+        if ("quicken".equals(DOWNGRADE_COMPILER_FILTER)) {
+            throw new RuntimeException("pm.dexopt.inactive should not be \"quicken\"");
+        }
+    }
+
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    @Before
+    public void setUp() throws IOException {
+        File dataDir = getContext().getDataDir();
+        mBigFile = new File(dataDir, BIG_FILE);
+    }
+
+    @After
+    public void tearDown() {
+        if (mBigFile.exists()) {
+            boolean result = mBigFile.delete();
+            if (!result) {
+                throw new RuntimeException("Couldn't delete big file");
+            }
+        }
+    }
+
+    // Return the content of the InputStream as a String.
+    private static String inputStreamToString(InputStream is) throws IOException {
+        char[] buffer = new char[1024];
+        StringBuilder builder = new StringBuilder();
+        try (InputStreamReader reader = new InputStreamReader(is)) {
+            for (; ; ) {
+                int count = reader.read(buffer, 0, buffer.length);
+                if (count < 0) {
+                    break;
+                }
+                builder.append(buffer, 0, count);
+            }
+        }
+        return builder.toString();
+    }
+
+    // Run the command and return the stdout.
+    private static String runShellCommand(String cmd) throws IOException {
+        Log.i(TAG, String.format("running command: '%s'", cmd));
+        long startTime = System.nanoTime();
+        Process p = Runtime.getRuntime().exec(cmd);
+        int res;
+        try {
+            res = p.waitFor();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        String stdout = inputStreamToString(p.getInputStream());
+        String stderr = inputStreamToString(p.getErrorStream());
+        long elapsedTime = System.nanoTime() - startTime;
+        Log.i(TAG, String.format("ran command: '%s' in %d ms with return code %d", cmd,
+                TimeUnit.NANOSECONDS.toMillis(elapsedTime), res));
+        Log.i(TAG, "stdout");
+        Log.i(TAG, stdout);
+        Log.i(TAG, "stderr");
+        Log.i(TAG, stderr);
+        if (res != 0) {
+            throw new RuntimeException(String.format("failed command: '%s'", cmd));
+        }
+        return stdout;
+    }
+
+    // Run the command and return the stdout split by lines.
+    private static String[] runShellCommandSplitLines(String cmd) throws IOException {
+        return runShellCommand(cmd).split("\n");
+    }
+
+    // Return the compiler filter of a package.
+    private static String getCompilerFilter(String pkg) throws IOException {
+        String cmd = String.format("dumpsys package %s", pkg);
+        String[] lines = runShellCommandSplitLines(cmd);
+        final String substr = "compilation_filter=";
+        for (String line : lines) {
+            int startIndex = line.indexOf(substr);
+            if (startIndex < 0) {
+                continue;
+            }
+            startIndex += substr.length();
+            int endIndex = line.indexOf(']', startIndex);
+            return line.substring(startIndex, endIndex);
+        }
+        throw new RuntimeException("Couldn't find compiler filter in dumpsys package");
+    }
+
+    // Return the number of bytes available in the data partition.
+    private static long getDataDirUsableSpace() {
+        return Environment.getDataDirectory().getUsableSpace();
+    }
+
+    // Fill up the storage until there are bytesRemaining number of bytes available in the data
+    // partition. Writes to the current package's data directory.
+    private void fillUpStorage(long bytesRemaining) throws IOException {
+        Log.i(TAG, String.format("Filling up storage with %d bytes remaining", bytesRemaining));
+        logSpaceRemaining();
+        long numBytesToAdd = getDataDirUsableSpace() - bytesRemaining;
+        String cmd = String.format("fallocate -l %d %s", numBytesToAdd, mBigFile.getAbsolutePath());
+        runShellCommand(cmd);
+        logSpaceRemaining();
+    }
+
+    // Fill up storage so that device is in low storage condition.
+    private void fillUpToLowStorage() throws IOException {
+        fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER));
+    }
+
+    // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run
+    private static void runBackgroundDexOpt() throws IOException {
+        runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME);
+    }
+
+    // Set the time ahead of the last use time of the test app in days.
+    private static void setTimeFutureDays(long futureDays) {
+        setTimeFutureMillis(TimeUnit.DAYS.toMillis(futureDays));
+    }
+
+    // Set the time ahead of the last use time of the test app in milliseconds.
+    private static void setTimeFutureMillis(long futureMillis) {
+        long currentTime = System.currentTimeMillis();
+        setTime(currentTime + futureMillis);
+    }
+
+    private static void setTime(long time) {
+        AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+        am.setTime(time);
+    }
+
+    // Return the number of free bytes when the data partition is considered low on storage.
+    private static long getStorageLowBytes() {
+        StorageManager storageManager = (StorageManager) getContext().getSystemService(
+                Context.STORAGE_SERVICE);
+        return storageManager.getStorageLowBytes(Environment.getDataDirectory());
+    }
+
+    // Log the amount of space remaining in the data directory.
+    private static void logSpaceRemaining() throws IOException {
+        runShellCommand("df -h /data");
+    }
+
+    // Compile the given package with the given compiler filter.
+    private static void compilePackageWithFilter(String pkg, String filter) throws IOException {
+        runShellCommand(String.format("cmd package compile -f -m %s %s", filter, pkg));
+    }
+
+    // Test that background dexopt under normal conditions succeeds.
+    @Test
+    public void testBackgroundDexOpt() throws IOException {
+        // Set filter to quicken.
+        compilePackageWithFilter(PACKAGE_NAME, "verify");
+        Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME));
+
+        runBackgroundDexOpt();
+
+        // Verify that bg-dexopt is successful.
+        Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME));
+    }
+
+    // Test that background dexopt under low storage conditions upgrades used packages.
+    @Test
+    public void testBackgroundDexOptDowngradeSkipRecentlyUsedPackage() throws IOException {
+        // Should be less than DOWNGRADE_AFTER_DAYS.
+        long deltaDays = DOWNGRADE_AFTER_DAYS - 1;
+        try {
+            // Set time to future.
+            setTimeFutureDays(deltaDays);
+
+            // Set filter to quicken.
+            compilePackageWithFilter(PACKAGE_NAME, "quicken");
+            Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME));
+
+            // Fill up storage to trigger low storage threshold.
+            fillUpToLowStorage();
+
+            runBackgroundDexOpt();
+
+            // Verify that downgrade did not happen.
+            Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME));
+        } finally {
+            // Reset time.
+            setTimeFutureDays(-deltaDays);
+        }
+    }
+
+    // Test that background dexopt under low storage conditions downgrades unused packages.
+    @Test
+    public void testBackgroundDexOptDowngradeSuccessful() throws IOException {
+        // Should be more than DOWNGRADE_AFTER_DAYS.
+        long deltaDays = DOWNGRADE_AFTER_DAYS + 1;
+        try {
+            // Set time to future.
+            setTimeFutureDays(deltaDays);
+
+            // Set filter to quicken.
+            compilePackageWithFilter(PACKAGE_NAME, "quicken");
+            Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME));
+
+            // Fill up storage to trigger low storage threshold.
+            fillUpToLowStorage();
+
+            runBackgroundDexOpt();
+
+            // Verify that downgrade is successful.
+            Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME));
+        } finally {
+            // Reset time.
+            setTimeFutureDays(-deltaDays);
+        }
+    }
+
+}
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
new file mode 100644
index 0000000..6bdfdc6
--- /dev/null
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import java.util.Arrays;
+import java.util.Random;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link IpSecAlgorithm}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IpSecAlgorithmTest {
+
+    private static final byte[] KEY_MATERIAL;
+
+    static {
+        KEY_MATERIAL = new byte[128];
+        new Random().nextBytes(KEY_MATERIAL);
+    };
+
+    @Test
+    public void testDefaultTruncLen() throws Exception {
+        IpSecAlgorithm explicit =
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8), 256);
+        IpSecAlgorithm implicit =
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
+        assertTrue(
+                "Default Truncation Length Incorrect, Explicit: "
+                        + explicit
+                        + "implicit: "
+                        + implicit,
+                IpSecAlgorithm.equals(explicit, implicit));
+    }
+
+    @Test
+    public void testTruncLenValidation() throws Exception {
+        for (int truncLen : new int[] {256, 512}) {
+            new IpSecAlgorithm(
+                    IpSecAlgorithm.AUTH_HMAC_SHA512,
+                    Arrays.copyOf(KEY_MATERIAL, 512 / 8),
+                    truncLen);
+        }
+
+        for (int truncLen : new int[] {255, 513}) {
+            try {
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_HMAC_SHA512,
+                        Arrays.copyOf(KEY_MATERIAL, 512 / 8),
+                        truncLen);
+                fail("Invalid truncation length not validated");
+            } catch (IllegalArgumentException pass) {
+            }
+        }
+    }
+
+    @Test
+    public void testLenValidation() throws Exception {
+        for (int len : new int[] {128, 192, 256}) {
+            new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, len / 8));
+        }
+        try {
+            new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 384 / 8));
+            fail("Invalid key length not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    @Test
+    public void testAlgoNameValidation() throws Exception {
+        try {
+            new IpSecAlgorithm("rot13", Arrays.copyOf(KEY_MATERIAL, 128 / 8));
+            fail("Invalid algorithm name not validated");
+        } catch (IllegalArgumentException pass) {
+        }
+    }
+
+    @Test
+    public void testParcelUnparcel() throws Exception {
+        IpSecAlgorithm init =
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_HMAC_SHA512, Arrays.copyOf(KEY_MATERIAL, 512 / 8), 256);
+
+        Parcel p = Parcel.obtain();
+        p.setDataPosition(0);
+        init.writeToParcel(p, 0);
+
+        p.setDataPosition(0);
+        IpSecAlgorithm fin = IpSecAlgorithm.CREATOR.createFromParcel(p);
+        assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin));
+        p.recycle();
+    }
+}
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index 1b4bef5..efc01f2a 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -71,7 +71,7 @@
         c.setAuthentication(
                 IpSecTransform.DIRECTION_OUT,
                 new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA1,
+                        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(
@@ -82,7 +82,7 @@
         c.setAuthentication(
                 IpSecTransform.DIRECTION_IN,
                 new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA1,
+                        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);
         assertParcelingIsLossless(c);
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index fcbb9da..558dbb6 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
-import android.net.MacAddress.MacAddressType;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -37,11 +36,11 @@
 
     static class AddrTypeTestCase {
         byte[] addr;
-        MacAddressType expected;
+        int expectedType;
 
-        static AddrTypeTestCase of(MacAddressType expected, int... addr) {
+        static AddrTypeTestCase of(int expectedType, int... addr) {
             AddrTypeTestCase t = new AddrTypeTestCase();
-            t.expected = expected;
+            t.expectedType = expectedType;
             t.addr = toByteArray(addr);
             return t;
         }
@@ -50,41 +49,73 @@
     @Test
     public void testMacAddrTypes() {
         AddrTypeTestCase[] testcases = {
-            AddrTypeTestCase.of(null),
-            AddrTypeTestCase.of(null, 0),
-            AddrTypeTestCase.of(null, 1, 2, 3, 4, 5),
-            AddrTypeTestCase.of(null, 1, 2, 3, 4, 5, 6, 7),
-            AddrTypeTestCase.of(MacAddressType.UNICAST, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0),
-            AddrTypeTestCase.of(MacAddressType.BROADCAST, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
-            AddrTypeTestCase.of(MacAddressType.MULTICAST, 1, 2, 3, 4, 5, 6),
-            AddrTypeTestCase.of(MacAddressType.MULTICAST, 11, 22, 33, 44, 55, 66),
-            AddrTypeTestCase.of(MacAddressType.MULTICAST, 33, 33, 0xaa, 0xbb, 0xcc, 0xdd)
+            AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN),
+            AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 0),
+            AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5),
+            AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5, 6, 7),
+            AddrTypeTestCase.of(MacAddress.TYPE_UNICAST, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0),
+            AddrTypeTestCase.of(MacAddress.TYPE_BROADCAST, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+            AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 1, 2, 3, 4, 5, 6),
+            AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 11, 22, 33, 44, 55, 66),
+            AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 33, 33, 0xaa, 0xbb, 0xcc, 0xdd)
         };
 
         for (AddrTypeTestCase t : testcases) {
-            MacAddressType got = MacAddress.macAddressType(t.addr);
+            int got = MacAddress.macAddressType(t.addr);
             String msg = String.format("expected type of %s to be %s, but got %s",
-                    Arrays.toString(t.addr), t.expected, got);
-            assertEquals(msg, t.expected, got);
+                    Arrays.toString(t.addr), t.expectedType, got);
+            assertEquals(msg, t.expectedType, got);
 
-            if (got != null) {
-                assertEquals(got, new MacAddress(t.addr).addressType());
+            if (got != MacAddress.TYPE_UNKNOWN) {
+                assertEquals(got, MacAddress.fromBytes(t.addr).addressType());
             }
         }
     }
 
     @Test
+    public void testToSafeString() {
+        String[][] macs = {
+            {"07:00:d3:56:8a:c4", "07:00:d3:00:00:00"},
+            {"33:33:aa:bb:cc:dd", "33:33:aa:00:00:00"},
+            {"06:00:00:00:00:00", "06:00:00:00:00:00"},
+            {"07:00:d3:56:8a:c4", "07:00:d3:00:00:00"}
+        };
+
+        for (String[] pair : macs) {
+            String mac = pair[0];
+            String expected = pair[1];
+            assertEquals(expected, MacAddress.fromString(mac).toSafeString());
+        }
+    }
+
+    @Test
+    public void testHexPaddingWhenPrinting() {
+        String[] macs = {
+            "07:00:d3:56:8a:c4",
+            "33:33:aa:bb:cc:dd",
+            "06:00:00:00:00:00",
+            "07:00:d3:56:8a:c4"
+        };
+
+        for (String mac : macs) {
+            assertEquals(mac, MacAddress.fromString(mac).toString());
+            assertEquals(mac,
+                    MacAddress.stringAddrFromByteAddr(MacAddress.byteAddrFromStringAddr(mac)));
+        }
+    }
+
+    @Test
     public void testIsMulticastAddress() {
         MacAddress[] multicastAddresses = {
             MacAddress.BROADCAST_ADDRESS,
-            new MacAddress("07:00:d3:56:8a:c4"),
-            new MacAddress("33:33:aa:bb:cc:dd"),
+            MacAddress.fromString("07:00:d3:56:8a:c4"),
+            MacAddress.fromString("33:33:aa:bb:cc:dd"),
         };
         MacAddress[] unicastAddresses = {
             MacAddress.ALL_ZEROS_ADDRESS,
-            new MacAddress("00:01:44:55:66:77"),
-            new MacAddress("08:00:22:33:44:55"),
-            new MacAddress("06:00:00:00:00:00"),
+            MacAddress.fromString("00:01:44:55:66:77"),
+            MacAddress.fromString("08:00:22:33:44:55"),
+            MacAddress.fromString("06:00:00:00:00:00"),
         };
 
         for (MacAddress mac : multicastAddresses) {
@@ -100,13 +131,13 @@
     @Test
     public void testIsLocallyAssignedAddress() {
         MacAddress[] localAddresses = {
-            new MacAddress("06:00:00:00:00:00"),
-            new MacAddress("07:00:d3:56:8a:c4"),
-            new MacAddress("33:33:aa:bb:cc:dd"),
+            MacAddress.fromString("06:00:00:00:00:00"),
+            MacAddress.fromString("07:00:d3:56:8a:c4"),
+            MacAddress.fromString("33:33:aa:bb:cc:dd"),
         };
         MacAddress[] universalAddresses = {
-            new MacAddress("00:01:44:55:66:77"),
-            new MacAddress("08:00:22:33:44:55"),
+            MacAddress.fromString("00:01:44:55:66:77"),
+            MacAddress.fromString("08:00:22:33:44:55"),
         };
 
         for (MacAddress mac : localAddresses) {
@@ -123,13 +154,16 @@
     public void testMacAddressConversions() {
         final int iterations = 10000;
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.getRandomAddress();
+            MacAddress mac = MacAddress.createRandomUnicastAddress();
 
             String stringRepr = mac.toString();
             byte[] bytesRepr = mac.toByteArray();
 
-            assertEquals(mac, new MacAddress(stringRepr));
-            assertEquals(mac, new MacAddress(bytesRepr));
+            assertEquals(mac, MacAddress.fromString(stringRepr));
+            assertEquals(mac, MacAddress.fromBytes(bytesRepr));
+
+            assertEquals(mac, MacAddress.fromString(MacAddress.stringAddrFromByteAddr(bytesRepr)));
+            assertEquals(mac, MacAddress.fromBytes(MacAddress.byteAddrFromStringAddr(stringRepr)));
         }
     }
 
@@ -138,7 +172,7 @@
         final int iterations = 1000;
         final String expectedAndroidOui = "da:a1:19";
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.getRandomAddress();
+            MacAddress mac = MacAddress.createRandomUnicastAddress();
             String stringRepr = mac.toString();
 
             assertTrue(stringRepr + " expected to be a locally assigned address",
@@ -150,13 +184,14 @@
         final Random r = new Random();
         final String anotherOui = "24:5f:78";
         final String expectedLocalOui = "26:5f:78";
-        final MacAddress base = new MacAddress(anotherOui + ":0:0:0");
+        final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0");
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.getRandomAddress(base, r);
+            MacAddress mac = MacAddress.createRandomUnicastAddress(base, r);
             String stringRepr = mac.toString();
 
             assertTrue(stringRepr + " expected to be a locally assigned address",
                     mac.isLocallyAssigned());
+            assertEquals(MacAddress.TYPE_UNICAST, mac.addressType());
             assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
                     stringRepr.startsWith(expectedLocalOui));
         }
@@ -165,7 +200,6 @@
     @Test
     public void testConstructorInputValidation() {
         String[] invalidStringAddresses = {
-            null,
             "",
             "abcd",
             "1:2:3:4:5",
@@ -175,14 +209,19 @@
 
         for (String s : invalidStringAddresses) {
             try {
-                MacAddress mac = new MacAddress(s);
-                fail("new MacAddress(" + s + ") should have failed, but returned " + mac);
+                MacAddress mac = MacAddress.fromString(s);
+                fail("MacAddress.fromString(" + s + ") should have failed, but returned " + mac);
             } catch (IllegalArgumentException excepted) {
             }
         }
 
+        try {
+            MacAddress mac = MacAddress.fromString(null);
+            fail("MacAddress.fromString(null) should have failed, but returned " + mac);
+        } catch (NullPointerException excepted) {
+        }
+
         byte[][] invalidBytesAddresses = {
-            null,
             {},
             {1,2,3,4,5},
             {1,2,3,4,5,6,7},
@@ -190,12 +229,18 @@
 
         for (byte[] b : invalidBytesAddresses) {
             try {
-                MacAddress mac = new MacAddress(b);
-                fail("new MacAddress(" + Arrays.toString(b)
+                MacAddress mac = MacAddress.fromBytes(b);
+                fail("MacAddress.fromBytes(" + Arrays.toString(b)
                         + ") should have failed, but returned " + mac);
             } catch (IllegalArgumentException excepted) {
             }
         }
+
+        try {
+            MacAddress mac = MacAddress.fromBytes(null);
+            fail("MacAddress.fromBytes(null) should have failed, but returned " + mac);
+        } catch (NullPointerException excepted) {
+        }
     }
 
     static byte[] toByteArray(int... in) {
diff --git a/tests/privapp-permissions/Android.mk b/tests/privapp-permissions/Android.mk
new file mode 100644
index 0000000..b001c8c
--- /dev/null
+++ b/tests/privapp-permissions/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := PrivAppPermissionTest
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
+LOCAL_REQUIRED_MODULES := privapp-permissions-test.xml
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := privapp-permissions-test.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+LOCAL_SRC_FILES:= system/privapp-permissions-test.xml
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := VendorPrivAppPermissionTest
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
+LOCAL_VENDOR_MODULE := true
+LOCAL_REQUIRED_MODULES := vendorprivapp-permissions-test.xml
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vendorprivapp-permissions-test.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/permissions
+LOCAL_SRC_FILES:= vendor/privapp-permissions-test.xml
+include $(BUILD_PREBUILT)
+
diff --git a/tests/privapp-permissions/system/AndroidManifest.xml b/tests/privapp-permissions/system/AndroidManifest.xml
new file mode 100644
index 0000000..2099e31
--- /dev/null
+++ b/tests/privapp-permissions/system/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.framework.permission.privapp.tests.system">
+
+    <!-- MANAGE_USB is signature|privileged -->
+    <uses-permission android:name="android.permission.MANAGE_USB"/>
+</manifest>
diff --git a/tests/privapp-permissions/system/privapp-permissions-test.xml b/tests/privapp-permissions/system/privapp-permissions-test.xml
new file mode 100644
index 0000000..a0cb6bc
--- /dev/null
+++ b/tests/privapp-permissions/system/privapp-permissions-test.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<permissions>
+    <privapp-permissions package="com.android.framework.permission.privapp.tests.system">
+        <permission name="android.permission.MANAGE_USB"/>
+    </privapp-permissions>
+</permissions>
diff --git a/tests/privapp-permissions/vendor/AndroidManifest.xml b/tests/privapp-permissions/vendor/AndroidManifest.xml
new file mode 100644
index 0000000..78dedc5
--- /dev/null
+++ b/tests/privapp-permissions/vendor/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.framework.permission.privapp.tests.vendor">
+
+    <!-- BIND_IMS_SERVICE is signature|privileged|vendorPrivileged -->
+    <uses-permission android:name="android.permission.BIND_IMS_SERVICE"/>
+    <!-- MANAGE_USB is signature|privileged and thus cannot be granted to this app -->
+    <uses-permission android:name="android.permission.MANAGE_USB"/>
+</manifest>
diff --git a/tests/privapp-permissions/vendor/privapp-permissions-test.xml b/tests/privapp-permissions/vendor/privapp-permissions-test.xml
new file mode 100644
index 0000000..51c588f
--- /dev/null
+++ b/tests/privapp-permissions/vendor/privapp-permissions-test.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<permissions>
+    <privapp-permissions package="com.android.framework.permission.privapp.tests.vendor">
+        <permission name="android.permission.BIND_IMS_SERVICE"/>
+        <permission name="android.permission.MANAGE_USB"/>
+    </privapp-permissions>
+</permissions>
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a93ee2e..deb9cc0 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -68,6 +68,7 @@
           mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL),
           mBuildSharedLibrary(false),
           mBuildAppAsSharedLibrary(false),
+          mCompileSdkVersion(0),
           mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
@@ -123,6 +124,10 @@
     void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
     bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; }
     void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; }
+    const android::String8& getCompileSdkVersionCodename() { return mCompileSdkVersionCodename; }
+    void setCompileSdkVersionCodename(const android::String8& codename) { mCompileSdkVersionCodename = codename; }
+    int getCompileSdkVersion() { return mCompileSdkVersion; }
+    void setCompileSdkVersion(int version) { mCompileSdkVersion = version; }
     const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; }
     void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; }
     const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; }
@@ -344,6 +349,8 @@
     const char* mSingleCrunchOutputFile;
     bool        mBuildSharedLibrary;
     bool        mBuildAppAsSharedLibrary;
+    int         mCompileSdkVersion;
+    android::String8 mCompileSdkVersionCodename;
     android::String8 mPlatformVersionCode;
     android::String8 mPlatformVersionName;
     android::String8 mPrivateSymbolsPackage;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index cb87737..05375b0 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -293,6 +293,8 @@
     ISGAME_ATTR = 0x10103f4,
     REQUIRED_FEATURE_ATTR = 0x1010557,
     REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+    COMPILE_SDK_VERSION_ATTR = 0x01010572, // NOT FINALIZED
+    COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573, // NOT FINALIZED
 };
 
 String8 getComponentName(String8 &pkgName, String8 &componentName) {
@@ -1247,9 +1249,37 @@
                                     splitName.string()).string());
                     }
 
-                    String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
+                    String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
                             "platformBuildVersionName");
-                    printf(" platformBuildVersionName='%s'", platformVersionName.string());
+                    if (platformBuildVersionName != "") {
+                        printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
+                    }
+
+                    String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
+                            "platformBuildVersionCode");
+                    if (platformBuildVersionCode != "") {
+                        printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
+                    }
+
+                    int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
+                            COMPILE_SDK_VERSION_ATTR, &error);
+                    if (error != "") {
+                        SourcePos(manifestFile, tree.getLineNumber()).error(
+                                "ERROR getting 'android:compileSdkVersion' attribute: %s",
+                                error.string());
+                        goto bail;
+                    }
+                    if (compileSdkVersion > 0) {
+                        printf(" compileSdkVersion='%d'", compileSdkVersion);
+                    }
+
+                    String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
+                            COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
+                    if (compileSdkVersionCodename != "") {
+                        printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
+                                compileSdkVersionCodename.string()).string());
+                    }
+
                     printf("\n");
 
                     int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index bd2b2a36..ab6dced 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -918,6 +918,22 @@
         }
     }
 
+
+    if (bundle->getCompileSdkVersion() != 0) {
+        if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "compileSdkVersion",
+                    String8::format("%d", bundle->getCompileSdkVersion()),
+                    errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    if (bundle->getCompileSdkVersionCodename() != "") {
+        if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "compileSdkVersionCodename",
+                    bundle->getCompileSdkVersionCodename(), errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
     if (bundle->getPlatformBuildVersionCode() != "") {
         if (!addTagAttribute(root, "", "platformBuildVersionCode",
                     bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
@@ -1052,7 +1068,12 @@
     VERSION_NAME_ATTR = 0x0101021c,
 };
 
-static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) {
+static ssize_t extractPlatformBuildVersion(const ResTable& table, ResXMLTree& tree, Bundle* bundle) {
+    // First check if we should be recording the compileSdkVersion* attributes.
+    static const String16 compileSdkVersionName("android:attr/compileSdkVersion");
+    const bool useCompileSdkVersion = table.identifierForName(compileSdkVersionName.string(),
+                                                              compileSdkVersionName.size()) != 0u;
+
     size_t len;
     ResXMLTree::event_code_t code;
     while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
@@ -1082,6 +1103,10 @@
             bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode));
         }
 
+        if (useCompileSdkVersion && versionCode >= 0 && bundle->getCompileSdkVersion() == 0) {
+            bundle->setCompileSdkVersion(versionCode);
+        }
+
         String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error);
         if (error != "") {
             fprintf(stderr, "ERROR: failed to get platform version name\n");
@@ -1091,6 +1116,11 @@
         if (versionName != "" && bundle->getPlatformBuildVersionName() == "") {
             bundle->setPlatformBuildVersionName(versionName);
         }
+
+        if (useCompileSdkVersion && versionName != ""
+                && bundle->getCompileSdkVersionCodename() == "") {
+            bundle->setCompileSdkVersionCodename(versionName);
+        }
         return NO_ERROR;
     }
 
@@ -1121,7 +1151,7 @@
             fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
             result = UNKNOWN_ERROR;
         } else {
-            result = extractPlatformBuildVersion(tree, bundle);
+            result = extractPlatformBuildVersion(assets.getResources(true), tree, bundle);
         }
     }
 
@@ -1707,7 +1737,9 @@
     // extract them from the platform APK.
     if (packageType != ResourceTable::System &&
             (bundle->getPlatformBuildVersionCode() == "" ||
-            bundle->getPlatformBuildVersionName() == "")) {
+            bundle->getPlatformBuildVersionName() == "" ||
+            bundle->getCompileSdkVersion() == 0 ||
+            bundle->getCompileSdkVersionCodename() == "")) {
         err = extractPlatformBuildVersion(assets->getAssetManager(), bundle);
         if (err != NO_ERROR) {
             return UNKNOWN_ERROR;
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index e0c9d1c..c9987b8 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -1,7 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
 include $(CLEAR_VARS)
 
+# Target for running host unit tests on post/pre-submit.
 .PHONY: aapt2_run_host_unit_tests
 aapt2_run_host_unit_tests: PRIVATE_GTEST_OPTIONS := --gtest_output=xml:$(DIST_DIR)/gtest/aapt2_host_unit_tests_result.xml
 aapt2_run_host_unit_tests: $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests
 	-$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests $(PRIVATE_GTEST_OPTIONS) > /dev/null 2>&1
 
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 5981401..33b5a8b 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -43,13 +43,16 @@
     return {};
   }
 
-  if (apk->FindFile("resources.arsc") != nullptr) {
-    return LoadBinaryApkFromFileCollection(source, std::move(apk), diag);
-  } else if (apk->FindFile("resources.pb") != nullptr) {
-    return LoadProtoApkFromFileCollection(source, std::move(apk), diag);
+  ApkFormat apkFormat = DetermineApkFormat(apk.get());
+  switch (apkFormat) {
+    case ApkFormat::kBinary:
+      return LoadBinaryApkFromFileCollection(source, std::move(apk), diag);
+    case ApkFormat::kProto:
+      return LoadProtoApkFromFileCollection(source, std::move(apk), diag);
+    default:
+      diag->Error(DiagMessage(path) << "could not identify format of APK");
+      return {};
   }
-  diag->Error(DiagMessage(path) << "no resource table found");
-  return {};
 }
 
 std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
@@ -243,4 +246,41 @@
   return true;
 }
 
+ApkFormat LoadedApk::DetermineApkFormat(io::IFileCollection* apk) {
+  if (apk->FindFile("resources.arsc") != nullptr) {
+    return ApkFormat::kBinary;
+  } else if (apk->FindFile("resources.pb") != nullptr) {
+    return ApkFormat::kProto;
+  } else {
+    // If the resource table is not present, attempt to read the manifest.
+    io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
+    if (manifest_file == nullptr) {
+      return ApkFormat::kUnknown;
+    }
+
+    // First try in proto format.
+    std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
+    if (manifest_in != nullptr) {
+      pb::XmlNode pb_node;
+      io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
+      if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+        return ApkFormat::kProto;
+      }
+    }
+
+    // If it didn't work, try in binary format.
+    std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
+    if (manifest_data != nullptr) {
+      std::string error;
+      std::unique_ptr<xml::XmlResource> manifest =
+          xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
+      if (manifest != nullptr) {
+        return ApkFormat::kBinary;
+      }
+    }
+
+    return ApkFormat::kUnknown;
+  }
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index ef97de3..6d2257f 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -33,6 +33,12 @@
 constexpr static const char kProtoResourceTablePath[] = "resources.pb";
 constexpr static const char kAndroidManifestPath[] = "AndroidManifest.xml";
 
+enum ApkFormat {
+  kUnknown,
+  kBinary,
+  kProto,
+};
+
 // Info about an APK loaded in memory.
 class LoadedApk {
  public:
@@ -104,6 +110,8 @@
   std::unique_ptr<io::IFileCollection> apk_;
   std::unique_ptr<ResourceTable> table_;
   std::unique_ptr<xml::XmlResource> manifest_;
+
+  static ApkFormat DetermineApkFormat(io::IFileCollection* apk);
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e94c0b4..13dd93e 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <sys/stat.h>
+#include <cinttypes>
 
 #include <queue>
 #include <unordered_map>
@@ -36,6 +37,7 @@
 #include "ValueVisitor.h"
 #include "cmd/Util.h"
 #include "compile/IdAssigner.h"
+#include "compile/XmlIdCollector.h"
 #include "filter/ConfigFilter.h"
 #include "format/Archive.h"
 #include "format/Container.h"
@@ -724,6 +726,30 @@
   return true;
 }
 
+static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) {
+  using namespace android;
+
+  // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
+  // we're looking for the first attribute resource in the system package.
+  const ResTable& table = assets.getResources(true);
+  Res_value val;
+  ssize_t idx = table.getResource(0x01010000, &val, true);
+  if (idx != NO_ERROR) {
+    // Try as a bag.
+    const ResTable::bag_entry* entry;
+    ssize_t cnt = table.lockBag(0x01010000, &entry);
+    if (cnt >= 0) {
+      idx = entry->stringBlock;
+    }
+    table.unlockBag(entry);
+  }
+
+  if (idx < 0) {
+    return 0;
+  }
+  return table.getTableCookie(idx);
+}
+
 class LinkCommand {
  public:
   LinkCommand(LinkContext* context, const LinkOptions& options)
@@ -733,7 +759,65 @@
         file_collection_(util::make_unique<io::FileCollection>()) {
   }
 
+  void ExtractCompileSdkVersions(android::AssetManager* assets) {
+    using namespace android;
+
+    int32_t cookie = FindFrameworkAssetManagerCookie(*assets);
+    if (cookie == 0) {
+      // No Framework assets loaded. Not a failure.
+      return;
+    }
+
+    std::unique_ptr<Asset> manifest(
+        assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER));
+    if (manifest == nullptr) {
+      // No errors.
+      return;
+    }
+
+    std::string error;
+    std::unique_ptr<xml::XmlResource> manifest_xml =
+        xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error);
+    if (manifest_xml == nullptr) {
+      // No errors.
+      return;
+    }
+
+    xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+    if (attr != nullptr) {
+      Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
+      if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
+        switch (prim->value.dataType) {
+          case Res_value::TYPE_INT_DEC:
+            compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data));
+            break;
+          case Res_value::TYPE_INT_HEX:
+            compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data);
+            break;
+          default:
+            break;
+        }
+      } else if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+        compile_sdk_version = *str->value;
+      } else {
+        compile_sdk_version = attr->value;
+      }
+    }
+
+    attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
+    if (attr != nullptr) {
+      Maybe<std::string>& compile_sdk_version_codename =
+          options_.manifest_fixer_options.compile_sdk_version_codename;
+      if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+        compile_sdk_version_codename = *str->value;
+      } else {
+        compile_sdk_version_codename = attr->value;
+      }
+    }
+  }
+
   // Creates a SymbolTable that loads symbols from the various APKs.
+  // Pre-condition: context_->GetCompilationPackage() needs to be set.
   bool LoadSymbolsFromIncludePaths() {
     auto asset_source = util::make_unique<AssetManagerSymbolSource>();
     for (const std::string& path : options_.include_paths) {
@@ -801,6 +885,17 @@
       } else if (entry.first == kAppPackageId) {
         // Capture the included base feature package.
         included_feature_base_ = entry.second;
+      } else if (entry.first == kFrameworkPackageId) {
+        // Try to embed which version of the framework we're compiling against.
+        // First check if we should use compileSdkVersion at all. Otherwise compilation may fail
+        // when linking our synthesized 'android:compileSdkVersion' attribute.
+        std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName(
+            ResourceName("android", ResourceType::kAttr, "compileSdkVersion"));
+        if (symbol != nullptr && symbol->is_public) {
+          // The symbol is present and public, extract the android:versionName and
+          // android:versionCode from the framework AndroidManifest.xml.
+          ExtractCompileSdkVersions(asset_source->GetAssetManager());
+        }
       }
     }
 
@@ -1235,19 +1330,10 @@
     return true;
   }
 
-  bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
-    if (context_->IsVerbose()) {
-      context_->GetDiagnostics()->Note(DiagMessage()
-                                       << "merging '" << compiled_file.name
-                                       << "' from compiled file " << compiled_file.source);
-    }
-
-    if (!table_merger_->MergeFile(compiled_file, override, file)) {
-      return false;
-    }
-
+  bool MergeExportedSymbols(const Source& source,
+                            const std::vector<SourcedResourceName>& exported_symbols) {
     // Add the exports of this file to the table.
-    for (const SourcedResourceName& exported_symbol : compiled_file.exported_symbols) {
+    for (const SourcedResourceName& exported_symbol : exported_symbols) {
       ResourceName res_name = exported_symbol.name;
       if (res_name.package.empty()) {
         res_name.package = context_->GetCompilationPackage();
@@ -1259,7 +1345,7 @@
       }
 
       std::unique_ptr<Id> id = util::make_unique<Id>();
-      id->SetSource(compiled_file.source.WithLine(exported_symbol.line));
+      id->SetSource(source.WithLine(exported_symbol.line));
       bool result = final_table_.AddResourceAllowMangled(
           res_name, ConfigDescription::DefaultConfig(), std::string(), std::move(id),
           context_->GetDiagnostics());
@@ -1270,6 +1356,19 @@
     return true;
   }
 
+  bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
+    if (context_->IsVerbose()) {
+      context_->GetDiagnostics()->Note(DiagMessage()
+                                       << "merging '" << compiled_file.name
+                                       << "' from compiled file " << compiled_file.source);
+    }
+
+    if (!table_merger_->MergeFile(compiled_file, override, file)) {
+      return false;
+    }
+    return MergeExportedSymbols(compiled_file.source, compiled_file.exported_symbols);
+  }
+
   // Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
   // If override is true, conflicting resources are allowed to override each other, in order of last
   // seen.
@@ -1530,6 +1629,12 @@
       context_->SetCompilationPackage(app_info.package);
     }
 
+    // Now that the compilation package is set, load the dependencies. This will also extract
+    // the Android framework's versionCode and versionName, if they exist.
+    if (!LoadSymbolsFromIncludePaths()) {
+      return 1;
+    }
+
     ManifestFixer manifest_fixer(options_.manifest_fixer_options);
     if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
       return 1;
@@ -1558,10 +1663,6 @@
       }
     }
 
-    if (!LoadSymbolsFromIncludePaths()) {
-      return 1;
-    }
-
     TableMergerOptions table_merger_options;
     table_merger_options.auto_add_overlay = options_.auto_add_overlay;
     table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);
@@ -1573,6 +1674,19 @@
                                                        context_->GetPackageId()));
     }
 
+    // Extract symbols from AndroidManifest.xml, since this isn't merged like the other XML files
+    // in res/**/*.
+    {
+      XmlIdCollector collector;
+      if (!collector.Consume(context_, manifest_xml.get())) {
+        return false;
+      }
+
+      if (!MergeExportedSymbols(manifest_xml->file.source, manifest_xml->file.exported_symbols)) {
+        return false;
+      }
+    }
+
     for (const std::string& input : input_files) {
       if (!MergePath(input, false)) {
         context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing input");
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index f40a1df..1bdb7625 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -17,6 +17,7 @@
 #include <memory>
 #include <vector>
 
+#include "android-base/file.h"
 #include "android-base/stringprintf.h"
 
 #include "androidfw/ResourceTypes.h"
@@ -47,6 +48,7 @@
 using ::aapt::configuration::PostProcessingConfiguration;
 using ::android::ResTable_config;
 using ::android::StringPiece;
+using ::android::base::ReadFileToString;
 using ::android::base::StringAppendF;
 using ::android::base::StringPrintf;
 
@@ -286,6 +288,20 @@
   OptimizeContext* context_;
 };
 
+bool ExtractWhitelistFromConfig(const std::string& path, OptimizeContext* context,
+                                OptimizeOptions* options) {
+  std::string contents;
+  if (!ReadFileToString(path, &contents, true)) {
+    context->GetDiagnostics()->Error(DiagMessage()
+                                     << "failed to parse whitelist from config file: " << path);
+    return false;
+  }
+  for (const StringPiece& resource_name : util::Tokenize(contents, ',')) {
+    options->table_flattener_options.whitelisted_resources.insert(resource_name.to_string());
+  }
+  return true;
+}
+
 bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
                                 OptimizeOptions* out_options) {
   const xml::XmlResource* manifest = apk->GetManifest();
@@ -309,6 +325,7 @@
   OptimizeContext context;
   OptimizeOptions options;
   Maybe<std::string> config_path;
+  Maybe<std::string> whitelist_path;
   Maybe<std::string> target_densities;
   Maybe<std::string> target_abis;
   std::vector<std::string> configs;
@@ -328,6 +345,10 @@
               "All the resources that would be unused on devices of the given densities will be \n"
               "removed from the APK.",
               &target_densities)
+          .OptionalFlag("--whitelist-config-path",
+                        "Path to the whitelist.cfg file containing whitelisted resources \n"
+                        "whose names should not be altered in final resource tables.",
+                        &whitelist_path)
           .OptionalFlag(
               "--target-abis",
               "Comma separated list of the CPU ABIs that the APK will be optimized for.\n"
@@ -351,6 +372,9 @@
                           "Enables encoding sparse entries using a binary search tree.\n"
                           "This decreases APK size at the cost of resource retrieval performance.",
                           &options.table_flattener_options.use_sparse_entries)
+          .OptionalSwitch("--enable-resource-obfuscation",
+                          "Enables obfuscation of key string pool to single value",
+                          &options.table_flattener_options.collapse_key_stringpool)
           .OptionalSwitch("-v", "Enables verbose logging", &verbose);
 
   if (!flags.Parse("aapt2 optimize", args, &std::cerr)) {
@@ -445,6 +469,15 @@
     return 1;
   }
 
+  if (options.table_flattener_options.collapse_key_stringpool) {
+    if (whitelist_path) {
+      std::string& path = whitelist_path.value();
+      if (!ExtractWhitelistFromConfig(path, &context, &options)) {
+        return 1;
+      }
+    }
+  }
+
   if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
     return 1;
   }
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 2a51df3..a3034df 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -220,12 +220,15 @@
 class PackageFlattener {
  public:
   PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
-                   const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries)
+                   const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries,
+                   bool collapse_key_stringpool, const std::set<std::string>& whitelisted_resources)
       : context_(context),
         diag_(context->GetDiagnostics()),
         package_(package),
         shared_libs_(shared_libs),
-        use_sparse_entries_(use_sparse_entries) {
+        use_sparse_entries_(use_sparse_entries),
+        collapse_key_stringpool_(collapse_key_stringpool),
+        whitelisted_resources_(whitelisted_resources) {
   }
 
   bool FlattenPackage(BigBuffer* buffer) {
@@ -494,13 +497,23 @@
       // configuration available. Here we reverse this to match the binary
       // table.
       std::map<ConfigDescription, std::vector<FlatEntry>> config_to_entry_list_map;
-      for (ResourceEntry* entry : sorted_entries) {
-        const uint32_t key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
 
+      // hardcoded string uses characters which make it an invalid resource name
+      const std::string obfuscated_resource_name = "0_resource_name_obfuscated";
+
+      for (ResourceEntry* entry : sorted_entries) {
+        uint32_t local_key_index;
+        if (!collapse_key_stringpool_ ||
+            whitelisted_resources_.find(entry->name) != whitelisted_resources_.end()) {
+          local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
+        } else {
+          // resource isn't whitelisted, add it as obfuscated value
+          local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
+        }
         // Group values by configuration.
         for (auto& config_value : entry->values) {
           config_to_entry_list_map[config_value->config].push_back(
-              FlatEntry{entry, config_value->value.get(), key_index});
+              FlatEntry{entry, config_value->value.get(), local_key_index});
         }
       }
 
@@ -549,6 +562,8 @@
   bool use_sparse_entries_;
   StringPool type_pool_;
   StringPool key_pool_;
+  bool collapse_key_stringpool_;
+  const std::set<std::string>& whitelisted_resources_;
 };
 
 }  // namespace
@@ -593,7 +608,8 @@
     }
 
     PackageFlattener flattener(context, package.get(), &table->included_packages_,
-                               options_.use_sparse_entries);
+                               options_.use_sparse_entries, options_.collapse_key_stringpool,
+                               options_.whitelisted_resources);
     if (!flattener.FlattenPackage(&package_buffer)) {
       return false;
     }
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 88cbddf..c2e1d4b 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -35,6 +35,14 @@
   // This is only available on platforms O+ and will only be respected when
   // minSdk is O+.
   bool use_sparse_entries = false;
+
+  // When true, the key string pool in the final ResTable
+  // is collapsed to a single entry. All resource entries
+  // have name indices that point to this single value
+  bool collapse_key_stringpool = false;
+
+  // Set of whitelisted resource names to avoid altering in key stringpool
+  std::set<std::string> whitelisted_resources;
 };
 
 class TableFlattener : public IResourceTableConsumer {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e11890b..f0b80d2 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -127,6 +127,15 @@
              << StringPiece16(actual_name.name, actual_name.nameLen) << "'";
     }
 
+    ResourceName actual_res_name(resName.value());
+
+    if (expected_res_name.entry != actual_res_name.entry ||
+        expected_res_name.package != actual_res_name.package ||
+        expected_res_name.type != actual_res_name.type) {
+      return ::testing::AssertionFailure() << "expected resource '" << expected_res_name.to_string()
+                                           << "' but got '" << actual_res_name.to_string() << "'";
+    }
+
     if (expected_config != config) {
       return ::testing::AssertionFailure() << "expected config '" << expected_config
                                            << "' but got '" << ConfigDescription(config) << "'";
@@ -450,4 +459,113 @@
   ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result));
 }
 
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
+          .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
+          .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
+                    test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
+          .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
+          .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
+                    ResourceId(0x7f030000),
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
+          .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
+          .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
+          .Build();
+
+  TableFlattenerOptions options;
+  options.collapse_key_stringpool = true;
+
+  ResTable res_table;
+
+  ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+                     ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+                     ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+                     ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
+                     ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
+                     ResTable_config::CONFIG_VERSION));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
+                     ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC,
+                     2u, ResTable_config::CONFIG_VERSION));
+
+  std::u16string foo_str = u"foo";
+  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+  ASSERT_GE(idx, 0);
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
+                     ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+
+  std::u16string bar_path = u"res/layout/bar.xml";
+  idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
+  ASSERT_GE(idx, 0);
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
+                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+}
+
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
+          .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
+          .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
+                    test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
+          .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
+          .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
+                    ResourceId(0x7f030000),
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
+          .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
+          .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
+          .Build();
+
+  TableFlattenerOptions options;
+  options.collapse_key_stringpool = true;
+  options.whitelisted_resources.insert("test");
+  options.whitelisted_resources.insert("three");
+  ResTable res_table;
+
+  ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+                     ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+                     ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
+                     Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
+                     ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
+                     ResTable_config::CONFIG_VERSION));
+
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
+                     ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC,
+                     2u, ResTable_config::CONFIG_VERSION));
+
+  std::u16string foo_str = u"foo";
+  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
+  ASSERT_GE(idx, 0);
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
+                     Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+
+  std::u16string bar_path = u"res/layout/bar.xml";
+  idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
+  ASSERT_GE(idx, 0);
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
+                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml
index a5f202d..c8f4bda 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/AndroidManifest.xml
@@ -21,7 +21,7 @@
     <uses-permission-sdk-23 android:name="android.permission.TEST" android:maxSdkVersion="22" />
 
     <application>
-        <activity android:name=".MyActivity">
+        <activity android:name=".MyActivity" android:id="@+id/sample_generated_id">
             <layout android:defaultHeight="500dp"
                 android:defaultWidth="600dp" />
         </activity>
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml
index aaa884b..5a73429 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/res/layout/main.xml
@@ -19,7 +19,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
-    <fragment class="android.test.sample.App$Inner" />
+    <fragment class="android.test.sample.App$Inner" android:id="@id/sample_generated_id"/>
 
     <variable name="user" type="com.android.User" />
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index eaaefd5..a68df1d 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -406,6 +406,25 @@
     root->InsertChild(0, std::move(uses_sdk));
   }
 
+  if (options_.compile_sdk_version) {
+    xml::Attribute* attr = root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersion");
+
+    // Make sure we un-compile the value if it was set to something else.
+    attr->compiled_value = {};
+
+    attr->value = options_.compile_sdk_version.value();
+  }
+
+  if (options_.compile_sdk_version_codename) {
+    xml::Attribute* attr =
+        root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename");
+
+    // Make sure we un-compile the value if it was set to something else.
+    attr->compiled_value = {};
+
+    attr->value = options_.compile_sdk_version_codename.value();
+  }
+
   xml::XmlActionExecutor executor;
   if (!BuildRules(&executor, context->GetDiagnostics())) {
     return false;
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 470f65e..f5715f6 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -29,22 +29,42 @@
 namespace aapt {
 
 struct ManifestFixerOptions {
+  // The minimum SDK version to set if no 'android:minSdkVersion' is defined in a <uses-sdk> tag.
   Maybe<std::string> min_sdk_version_default;
+
+  // The target SDK version to set if no 'android:targetSdkVersion' is defined in a <uses-sdk> tag.
   Maybe<std::string> target_sdk_version_default;
+
+  // The Android package to use instead of the one defined in 'package' in <manifest>.
+  // This also renames all relative package/class names in the manifest to fully qualified
+  // Java names.
   Maybe<std::string> rename_manifest_package;
+
+  // The Android package to use instead of the one defined in 'android:targetPackage' in
+  // <instrumentation>.
   Maybe<std::string> rename_instrumentation_target_package;
+
+  // The version name to set if 'android:versionName' is not defined in <manifest>.
   Maybe<std::string> version_name_default;
+
+  // The version code to set if 'android:versionCode' is not defined in <manifest>.
   Maybe<std::string> version_code_default;
+
+  // The version of the framework being compiled against to set for 'android:compileSdkVersion' in
+  // the <manifest> tag.
+  Maybe<std::string> compile_sdk_version;
+
+  // The version codename of the framework being compiled against to set for
+  // 'android:compileSdkVersionCodename' in the <manifest> tag.
+  Maybe<std::string> compile_sdk_version_codename;
 };
 
-/**
- * Verifies that the manifest is correctly formed and inserts defaults
- * where specified with ManifestFixerOptions.
- */
+// Verifies that the manifest is correctly formed and inserts defaults where specified with
+// ManifestFixerOptions.
 class ManifestFixer : public IXmlResourceConsumer {
  public:
-  explicit ManifestFixer(const ManifestFixerOptions& options)
-      : options_(options) {}
+  explicit ManifestFixer(const ManifestFixerOptions& options) : options_(options) {
+  }
 
   bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
 
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 40085ea..1320dcd 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -19,7 +19,12 @@
 #include "test/Test.h"
 
 using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::Gt;
+using ::testing::IsNull;
+using ::testing::Ne;
 using ::testing::NotNull;
+using ::testing::StrEq;
 
 namespace aapt {
 
@@ -72,22 +77,20 @@
 };
 
 TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) {
-  EXPECT_EQ(nullptr, Verify("<other-tag />"));
-  EXPECT_EQ(nullptr, Verify("<ns:manifest xmlns:ns=\"com\" />"));
-  EXPECT_NE(nullptr, Verify("<manifest package=\"android\"></manifest>"));
+  EXPECT_THAT(Verify("<other-tag />"), IsNull());
+  EXPECT_THAT(Verify("<ns:manifest xmlns:ns=\"com\" />"), IsNull());
+  EXPECT_THAT(Verify("<manifest package=\"android\"></manifest>"), NotNull());
 }
 
 TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
-  EXPECT_NE(nullptr, Verify("<manifest package=\"android\" />"));
-  EXPECT_NE(nullptr, Verify("<manifest package=\"com.android\" />"));
-  EXPECT_NE(nullptr, Verify("<manifest package=\"com.android.google\" />"));
-  EXPECT_EQ(nullptr,
-            Verify("<manifest package=\"com.android.google.Class$1\" />"));
-  EXPECT_EQ(nullptr, Verify("<manifest "
-                            "xmlns:android=\"http://schemas.android.com/apk/"
-                            "res/android\" "
-                            "android:package=\"com.android\" />"));
-  EXPECT_EQ(nullptr, Verify("<manifest package=\"@string/str\" />"));
+  EXPECT_THAT(Verify("<manifest package=\"android\" />"), NotNull());
+  EXPECT_THAT(Verify("<manifest package=\"com.android\" />"), NotNull());
+  EXPECT_THAT(Verify("<manifest package=\"com.android.google\" />"), NotNull());
+  EXPECT_THAT(Verify("<manifest package=\"com.android.google.Class$1\" />"), IsNull());
+  EXPECT_THAT(Verify("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" "
+                     "android:package=\"com.android\" />"),
+              IsNull());
+  EXPECT_THAT(Verify("<manifest package=\"@string/str\" />"), IsNull());
 }
 
 TEST_F(ManifestFixerTest, AllowMetaData) {
@@ -105,7 +108,7 @@
           </application>
           <instrumentation android:name=".Go"><meta-data /></instrumentation>
         </manifest>)EOF");
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 }
 
 TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
@@ -117,21 +120,21 @@
         <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
       </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* el;
   xml::Attribute* attr;
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("7", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("7"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("21", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("21"));
 
   doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -139,18 +142,18 @@
         <uses-sdk android:targetSdkVersion="21" />
       </manifest>)EOF",
                           options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("8", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("8"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("21", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("21"));
 
   doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -158,35 +161,35 @@
         <uses-sdk />
       </manifest>)EOF",
                           options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("8", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("8"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("22", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("22"));
 
   doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                 package="android" />)EOF",
                           options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("8", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("8"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("22", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("22"));
 }
 
 TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) {
@@ -197,17 +200,17 @@
             <application android:name=".MainApplication" />
           </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
   ASSERT_EQ("manifest", manifest_el->name);
 
   xml::Element* application_el = manifest_el->FindChild("", "application");
-  ASSERT_NE(nullptr, application_el);
+  ASSERT_THAT(application_el, NotNull());
 
   xml::Element* uses_sdk_el = manifest_el->FindChild("", "uses-sdk");
-  ASSERT_NE(nullptr, uses_sdk_el);
+  ASSERT_THAT(uses_sdk_el, NotNull());
 
   // Check that the uses_sdk_el comes before application_el in the children
   // vector.
@@ -225,12 +228,12 @@
                      return child.get() == application_el;
                    });
 
-  ASSERT_NE(manifest_el->children.end(), uses_sdk_iter);
-  ASSERT_NE(manifest_el->children.end(), application_iter);
+  ASSERT_THAT(uses_sdk_iter, Ne(manifest_el->children.end()));
+  ASSERT_THAT(application_iter, Ne(manifest_el->children.end()));
 
   // The distance should be positive, meaning uses_sdk_iter comes before
   // application_iter.
-  EXPECT_GT(std::distance(uses_sdk_iter, application_iter), 0);
+  EXPECT_THAT(std::distance(uses_sdk_iter, application_iter), Gt(0));
 }
 
 TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
@@ -247,48 +250,49 @@
         </application>
       </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
 
   xml::Attribute* attr = nullptr;
 
   attr = manifest_el->FindAttribute({}, "package");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("com.android"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("com.android"));
 
   xml::Element* uses_split_el = manifest_el->FindChild({}, "uses-split");
-  ASSERT_NE(nullptr, uses_split_el);
+  ASSERT_THAT(uses_split_el, NotNull());
   attr = uses_split_el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("feature_a"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  // This should NOT have been affected.
+  EXPECT_THAT(attr->value, StrEq("feature_a"));
 
   xml::Element* application_el = manifest_el->FindChild({}, "application");
-  ASSERT_NE(nullptr, application_el);
+  ASSERT_THAT(application_el, NotNull());
 
   attr = application_el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("android.MainApplication"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("android.MainApplication"));
 
   attr = application_el->FindAttribute({}, "text");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("hello"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("hello"));
 
   xml::Element* el;
   el = application_el->FindChild({}, "activity");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, el);
-  EXPECT_EQ(std::string("android.activity.Start"), attr->value);
+  ASSERT_THAT(el, NotNull());
+  EXPECT_THAT(attr->value, StrEq("android.activity.Start"));
 
   el = application_el->FindChild({}, "receiver");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, el);
-  EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value);
+  ASSERT_THAT(el, NotNull());
+  EXPECT_THAT(attr->value, StrEq("com.google.android.Receiver"));
 }
 
 TEST_F(ManifestFixerTest,
@@ -302,19 +306,19 @@
         <instrumentation android:name=".TestRunner" android:targetPackage="android" />
       </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
 
   xml::Element* instrumentation_el =
       manifest_el->FindChild({}, "instrumentation");
-  ASSERT_NE(nullptr, instrumentation_el);
+  ASSERT_THAT(instrumentation_el, NotNull());
 
   xml::Attribute* attr =
       instrumentation_el->FindAttribute(xml::kSchemaAndroid, "targetPackage");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("com.android"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("com.android"));
 }
 
 TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
@@ -326,41 +330,39 @@
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                 package="android" />)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
 
   xml::Attribute* attr =
       manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("Beta"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Beta"));
 
   attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("0x10000000"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x10000000"));
 }
 
 TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
-  EXPECT_EQ(nullptr,
-            Verify("<manifest package=\"android\" coreApp=\"hello\" />"));
-  EXPECT_EQ(nullptr,
-            Verify("<manifest package=\"android\" coreApp=\"1dp\" />"));
+  EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"hello\" />"), IsNull());
+  EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"1dp\" />"), IsNull());
 
   std::unique_ptr<xml::XmlResource> doc =
       Verify("<manifest package=\"android\" coreApp=\"true\" />");
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
 
-  EXPECT_EQ("manifest", el->name);
+  EXPECT_THAT(el->name, StrEq("manifest"));
 
   xml::Attribute* attr = el->FindAttribute("", "coreApp");
-  ASSERT_NE(nullptr, attr);
+  ASSERT_THAT(attr, NotNull());
 
-  EXPECT_NE(nullptr, attr->compiled_value);
-  EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(attr->compiled_value.get()));
+  EXPECT_THAT(attr->compiled_value, NotNull());
+  EXPECT_THAT(ValueCast<BinaryPrimitive>(attr->compiled_value.get()), NotNull());
 }
 
 TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) {
@@ -375,21 +377,21 @@
             <uses-feature android:glEsVersion="2" />
           </feature-group>
         </manifest>)EOF";
-  EXPECT_NE(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), NotNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                   package="android">
           <uses-feature android:name="feature" android:glEsVersion="1" />
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                   package="android">
           <uses-feature />
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -398,7 +400,7 @@
             <uses-feature android:name="feature" android:glEsVersion="1" />
           </feature-group>
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -407,7 +409,7 @@
             <uses-feature />
           </feature-group>
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 }
 
 TEST_F(ManifestFixerTest, IgnoreNamespacedElements) {
@@ -416,7 +418,7 @@
                 package="android">
         <special:tag whoo="true" xmlns:special="http://google.com" />
       </manifest>)EOF";
-  EXPECT_NE(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), NotNull());
 }
 
 TEST_F(ManifestFixerTest, DoNotIgnoreNonNamespacedElements) {
@@ -425,7 +427,7 @@
                 package="android">
         <tag whoo="true" />
       </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 }
 
 TEST_F(ManifestFixerTest, SupportKeySets) {
@@ -446,4 +448,23 @@
   EXPECT_THAT(Verify(input), NotNull());
 }
 
+TEST_F(ManifestFixerTest, InsertCompileSdkVersions) {
+  std::string input = R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" />)";
+  ManifestFixerOptions options;
+  options.compile_sdk_version = {"28"};
+  options.compile_sdk_version_codename = {"P"};
+
+  std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
+  ASSERT_THAT(manifest, NotNull());
+
+  xml::Attribute* attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersion");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("28"));
+
+  attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("P"));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 4a2181e..b676efb 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -199,6 +199,10 @@
   std::unique_ptr<SymbolTable::Symbol> FindByReference(
       const Reference& ref) override;
 
+  android::AssetManager* GetAssetManager() {
+    return &assets_;
+  }
+
  private:
   android::AssetManager assets_;
 
diff --git a/tools/aapt2/text/Unicode_test.cpp b/tools/aapt2/text/Unicode_test.cpp
index a8e797c..16bc2e8 100644
--- a/tools/aapt2/text/Unicode_test.cpp
+++ b/tools/aapt2/text/Unicode_test.cpp
@@ -63,6 +63,7 @@
   EXPECT_FALSE(IsValidResourceEntryName("Føø/Bar"));
   EXPECT_FALSE(IsValidResourceEntryName("Føø:Bar"));
   EXPECT_FALSE(IsValidResourceEntryName("Føø;Bar"));
+  EXPECT_FALSE(IsValidResourceEntryName("0_resource_name_obfuscated"));
 }
 
 }  // namespace text
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index b0cf44a..fddb6b8 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -418,6 +418,15 @@
   return nullptr;
 }
 
+Attribute* Element::FindOrCreateAttribute(const StringPiece& ns, const StringPiece& name) {
+  Attribute* attr = FindAttribute(ns, name);
+  if (attr == nullptr) {
+    attributes.push_back(Attribute{ns.to_string(), name.to_string()});
+    attr = &attributes.back();
+  }
+  return attr;
+}
+
 Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) {
   return FindChildWithAttribute(ns, name, {}, {}, {});
 }
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index cf06ba5..8f382961 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -100,6 +100,8 @@
   Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name);
   const Attribute* FindAttribute(const android::StringPiece& ns,
                                  const android::StringPiece& name) const;
+  Attribute* FindOrCreateAttribute(const android::StringPiece& ns,
+                                   const android::StringPiece& name);
 
   Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
   const Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name) const;
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index fd42033..421e545 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -940,11 +940,14 @@
 
     for f in found.values():
         takes_handler = False
+        takes_exec = False
         for m in by_name[f.name]:
             if "android.os.Handler" in m.args:
                 takes_handler = True
-        if not takes_handler:
-            warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler")
+            if "java.util.concurrent.Executor" in m.args:
+                takes_exec = True
+        if not takes_exec:
+            warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Executor")
 
 
 def verify_context_first(clazz):
@@ -968,7 +971,7 @@
         for a in m.args:
             if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"):
                 found = True
-            elif found and a != "android.os.Handler":
+            elif found and a != "android.os.Handler" and a != "java.util.concurrent.Executor":
                 warn(clazz, m, "M3", "Listeners should always be at end of argument list")
 
 
@@ -1218,6 +1221,18 @@
             unique_binary_op(m, m.name[:-6])  # Remove 'Assign' suffix
 
 
+def verify_collections_over_arrays(clazz):
+    """Warn that [] should be Collections."""
+
+    safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"]
+    for m in clazz.methods:
+        if m.typ.endswith("[]") and m.typ not in safe:
+            warn(clazz, m, None, "Method should return Collection<> (or subclass) instead of raw array")
+        for arg in m.args:
+            if arg.endswith("[]") and arg not in safe:
+                warn(clazz, m, None, "Method argument should be Collection<> (or subclass) instead of raw array")
+
+
 def examine_clazz(clazz):
     """Find all style issues in the given class."""
 
@@ -1260,7 +1275,7 @@
     verify_manager(clazz)
     verify_boxed(clazz)
     verify_static_utils(clazz)
-    verify_overload_args(clazz)
+    # verify_overload_args(clazz)
     verify_callback_handlers(clazz)
     verify_context_first(clazz)
     verify_listener_last(clazz)
@@ -1274,6 +1289,7 @@
     verify_closable(clazz)
     verify_member_name_not_kotlin_keyword(clazz)
     verify_method_name_not_kotlin_operator(clazz)
+    verify_collections_over_arrays(clazz)
 
 
 def examine_stream(stream):
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 674bee1..7650795 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -59,24 +59,31 @@
  *
  * #include "section_list.h"
  * ...
- * Privacy WindowState_state { 1, 9, NULL, LOCAL, NULL }; // first two integers are values for field id and proto type.
- * Privacy WindowState_child_windows { 3, 11, NULL, DEFAULT, NULL }; // reserved for WindowState_LIST
- * Privacy* WindowState_MSG_[] = {
+ * Privacy WindowState__state { 1, 9, NULL, LOCAL, NULL }; // first two integers are values for field id and proto type.
+ * Privacy WindowState__child_windows { 3, 11, NULL, UNSET, NULL }; // reserved for WindowState_LIST
+ * Privacy* WindowState__MSG__UNSET[] = {
  *     &WindowState_state,
  *     // display id is default, nothing is generated.
  *     &WindowState_child_windows,
  *     NULL  // terminator of the array
  * };
- * Privacy WindowState_my_window { 1, 11, WindowState_my_window_LIST, DEFAULT, NULL };
+ * Privacy WindowState__my_window { 1, 11, WindowState__MSG__UNSET, UNSET, NULL };
  *
  * createList() {
  *    ...
- *    WindowState_child_windows.children = WindowState_my_window_LIST; // point to its own definition after the list is defined.
+ *    WindowState_child_windows.children = WindowState__MSG_UNSET; // point to its own definition after the list is defined.
  *    ...
  * }
  *
  * const Privacy** PRIVACY_POLICY_LIST = createList();
  * const int PRIVACY_POLICY_COUNT = 1;
+ *
+ * Privacy Value Inheritance rules:
+ * 1. Both field and message can be tagged with DESTINATION: LOCAL(L), EXPLICIT(E), AUTOMATIC(A).
+ * 2. Primitives inherits containing message's tag unless defined explicitly.
+ * 3. Containing message's tag doesn't apply to message fields, even when unset (in this case, uses its default message tag).
+ * 4. Message field tag overrides its default message tag.
+ * 5. UNSET tag defaults to EXPLICIT.
  */
 
 // The assignments will be called when constructs PRIVACY_POLICY_LIST, has to be global variable
@@ -164,14 +171,13 @@
     return result;
 }
 
-static string getFieldName(const FieldDescriptor* field) {
-    return replaceAll(field->full_name(), '.', "__");
+static inline void printPrivacy(const string& name, const FieldDescriptor* field, const string& children,
+        const Destination dest, const string& patterns, const string& comments = "") {
+    printf("Privacy %s = { %d, %d, %s, %d, %s };%s\n", name.c_str(), field->number(), field->type(),
+        children.c_str(), dest, patterns.c_str(), comments.c_str());
 }
 
-static string getMessageTypeName(const Descriptor* descriptor) {
-    return replaceAll(descriptor->full_name(), '.', "_") + "_MSG_";
-}
-
+// Get Custom Options ================================================================================
 static inline SectionFlags getSectionFlags(const FieldDescriptor* field) {
     return field->options().GetExtension(section);
 }
@@ -180,24 +186,64 @@
     return field->options().GetExtension(privacy);
 }
 
-static inline bool isDefaultField(const FieldDescriptor* field) {
-    return getPrivacyFlags(field).dest() == PrivacyFlags::default_instance().dest();
+static inline PrivacyFlags getPrivacyFlags(const Descriptor* descriptor) {
+    return descriptor->options().GetExtension(msg_privacy);
 }
 
-static bool isDefaultMessageImpl(const Descriptor* descriptor, set<string>* parents) {
-    int N = descriptor->field_count();
+// Get Destinations ===================================================================================
+static inline Destination getMessageDest(const Descriptor* descriptor, const Destination overridden) {
+    return overridden != DEST_UNSET ? overridden : getPrivacyFlags(descriptor).dest();
+}
+
+// Returns field's own dest, when it is a message field, uses its message default tag if unset.
+static inline Destination getFieldDest(const FieldDescriptor* field) {
+    Destination fieldDest = getPrivacyFlags(field).dest();
+    return field->type() != FieldDescriptor::TYPE_MESSAGE ? fieldDest :
+            getMessageDest(field->message_type(), fieldDest);
+}
+
+// Get Names ===========================================================================================
+static inline string getFieldName(const FieldDescriptor* field) {
+    // replace . with double underscores to avoid name conflicts since fields use snake naming convention
+    return replaceAll(field->full_name(), '.', "__");
+}
+
+
+static inline string getMessageName(const Descriptor* descriptor, const Destination overridden) {
+    // replace . with one underscore since messages use camel naming convention
+    return replaceAll(descriptor->full_name(), '.', "_") + "__MSG__" +
+            to_string(getMessageDest(descriptor, overridden));
+}
+
+// IsDefault ============================================================================================
+// Returns true if a field is default. Default is defined as this field has same dest as its containing message.
+// For message fields, it only looks at its field tag and own default mesaage tag, doesn't recursively go deeper.
+static inline bool isDefaultField(const FieldDescriptor* field, const Destination containerDest) {
+    Destination fieldDest = getFieldDest(field);
+    if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+        return fieldDest == containerDest || (fieldDest == DEST_UNSET);
+    } else {
+        return fieldDest == containerDest ||
+            (containerDest == DEST_UNSET && fieldDest == DEST_EXPLICIT) ||
+            (containerDest == DEST_EXPLICIT && fieldDest == DEST_UNSET);
+    }
+}
+
+static bool isDefaultMessageImpl(const Descriptor* descriptor, const Destination dest, set<string>* parents) {
+    const int N = descriptor->field_count();
+    const Destination messageDest = getMessageDest(descriptor, dest);
     parents->insert(descriptor->full_name());
     for (int i=0; i<N; ++i) {
         const FieldDescriptor* field = descriptor->field(i);
-        // look at if the current field is default or not, return false immediately
-        if (!isDefaultField(field)) return false;
-
+        const Destination fieldDest = getFieldDest(field);
+        // If current field is not default, return false immediately
+        if (!isDefaultField(field, messageDest)) return false;
         switch (field->type()) {
             case FieldDescriptor::TYPE_MESSAGE:
                 // if self recursion, don't go deep.
                 if (parents->find(field->message_type()->full_name()) != parents->end()) break;
                 // if is a default message, just continue
-                if (isDefaultMessageImpl(field->message_type(), parents)) break;
+                if (isDefaultMessageImpl(field->message_type(), fieldDest, parents)) break;
                 // sub message is not default, so this message is always not default
                 return false;
             case FieldDescriptor::TYPE_STRING:
@@ -210,106 +256,118 @@
     return true;
 }
 
-static bool isDefaultMessage(const Descriptor* descriptor) {
+// Recursively look at if this message is default, meaning all its fields and sub-messages
+// can be described by the same dest.
+static bool isDefaultMessage(const Descriptor* descriptor, const Destination dest) {
     set<string> parents;
-    return isDefaultMessageImpl(descriptor, &parents);
+    return isDefaultMessageImpl(descriptor, dest, &parents);
 }
 
-// This function is called for looking at privacy tags for a message type and recursively its sub-messages
-// It prints out each fields's privacy tags and a List of Privacy of the message itself (don't print default values)
-// Returns false if the descriptor doesn't have any non default privacy flags set, including its submessages
-static bool generatePrivacyFlags(const Descriptor* descriptor, map<string, bool> &msgNames, set<string>* parents) {
-    bool hasDefaultFlags[descriptor->field_count()];
+// ===============================================================================================================
+static bool numberInOrder(const FieldDescriptor* f1, const FieldDescriptor* f2) {
+    return f1->number() < f2->number();
+}
 
-    string messageTypeName = getMessageTypeName(descriptor);
-    // if the message is already defined, skip it.
-    if (msgNames.find(messageTypeName) != msgNames.end()) {
-        bool hasDefault = msgNames[messageTypeName];
-        return !hasDefault; // don't generate if it has default privacy.
+// field numbers are possibly out of order, sort them here.
+static vector<const FieldDescriptor*> sortFields(const Descriptor* descriptor) {
+    vector<const FieldDescriptor*> fields;
+    fields.reserve(descriptor->field_count());
+    for (int i=0; i<descriptor->field_count(); i++) {
+        fields.push_back(descriptor->field(i));
+    }
+    std::sort(fields.begin(), fields.end(), numberInOrder);
+    return fields;
+}
+
+// This function looks for privacy tags of a message type and recursively its sub-messages.
+// It generates Privacy objects for each non-default fields including non-default sub-messages.
+// And if the message has Privacy objects generated, it returns a list of them.
+// Returns false if the descriptor doesn't have any non default privacy flags set, including its submessages
+static bool generatePrivacyFlags(const Descriptor* descriptor, const Destination overridden,
+        map<string, bool> &variableNames, set<string>* parents) {
+    const string messageName = getMessageName(descriptor, overridden);
+    const Destination messageDest = getMessageDest(descriptor, overridden);
+
+    if (variableNames.find(messageName) != variableNames.end()) {
+        bool hasDefault = variableNames[messageName];
+        return !hasDefault; // if has default, then don't generate privacy flags.
     }
     // insert the message type name so sub-message will figure out if self-recursion occurs
-    parents->insert(messageTypeName);
+    parents->insert(messageName);
 
-    // iterate though its field and generate sub flags first
-    for (int i=0; i<descriptor->field_count(); i++) {
-        hasDefaultFlags[i] = true; // set default to true
-
-        const FieldDescriptor* field = descriptor->field(i);
+    // sort fields based on number, iterate though them and generate sub flags first
+    vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
+    bool hasDefaultFlags[fieldsInOrder.size()];
+    for (size_t i=0; i<fieldsInOrder.size(); i++) {
+        const FieldDescriptor* field = fieldsInOrder[i];
         const string fieldName = getFieldName(field);
-        // check if the same field name is already defined.
-        if (msgNames.find(fieldName) != msgNames.end()) {
-            hasDefaultFlags[i] = msgNames[fieldName];
-            continue;
-        };
+        const Destination fieldDest = getFieldDest(field);
 
-        PrivacyFlags p = getPrivacyFlags(field);
+        if (variableNames.find(fieldName) != variableNames.end()) {
+            hasDefaultFlags[i] = variableNames[fieldName];
+            continue;
+        }
+        hasDefaultFlags[i] = isDefaultField(field, messageDest);
+
         string fieldMessageName;
+        PrivacyFlags p = getPrivacyFlags(field);
         switch (field->type()) {
             case FieldDescriptor::TYPE_MESSAGE:
-                fieldMessageName = getMessageTypeName(field->message_type());
+                fieldMessageName = getMessageName(field->message_type(), fieldDest);
                 if (parents->find(fieldMessageName) != parents->end()) { // Self-Recursion proto definition
-                    if (isDefaultField(field)) {
-                        hasDefaultFlags[i] = isDefaultMessage(field->message_type());
-                    } else {
-                        hasDefaultFlags[i] = false;
+                    if (hasDefaultFlags[i]) {
+                        hasDefaultFlags[i] = isDefaultMessage(field->message_type(), fieldDest);
                     }
                     if (!hasDefaultFlags[i]) {
-                        printf("Privacy %s = { %d, %d, NULL, %d, NULL }; // self recursion field of %s\n",
-                                fieldName.c_str(), field->number(), field->type(), p.dest(), fieldMessageName.c_str());
+                        printPrivacy(fieldName, field, "NULL", fieldDest, "NULL",
+                            " // self recursion field of " + fieldMessageName);
                         // generate the assignment and used to construct createList function later on.
                         gSelfRecursionAssignments.push_back(fieldName + ".children = " + fieldMessageName);
                     }
-                    break;
-                } else if (generatePrivacyFlags(field->message_type(), msgNames, parents)) {
-                    printf("Privacy %s = { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(),
-                            field->type(), fieldMessageName.c_str(), p.dest());
-                } else if (isDefaultField(field)) {
-                    // don't create a new privacy if the value is default.
-                    break;
-                } else {
-                    printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
-                            field->type(), p.dest());
+                } else if (generatePrivacyFlags(field->message_type(), p.dest(), variableNames, parents)) {
+                    if (variableNames.find(fieldName) == variableNames.end()) {
+                        printPrivacy(fieldName, field, fieldMessageName, fieldDest, "NULL");
+                    }
+                    hasDefaultFlags[i] = false;
+                } else if (!hasDefaultFlags[i]) {
+                    printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
                 }
-                hasDefaultFlags[i] = false;
                 break;
             case FieldDescriptor::TYPE_STRING:
-                if (isDefaultField(field) && p.patterns_size() == 0) break;
-
-                printf("const char* %s_patterns[] = {\n", fieldName.c_str());
-                for (int i=0; i<p.patterns_size(); i++) {
-                    // the generated string need to escape backslash as well, need to dup it here
-                    printf("    \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str());
+                if (p.patterns_size() != 0) { // if patterns are specified
+                    if (hasDefaultFlags[i]) break;
+                    printf("const char* %s_patterns[] = {\n", fieldName.c_str());
+                    for (int j=0; j<p.patterns_size(); j++) {
+                        // generated string needs to escape backslash too, duplicate it to allow escape again.
+                        printf("    \"%s\",\n", replaceAll(p.patterns(j), '\\', "\\\\").c_str());
+                    }
+                    printf("    NULL };\n");
+                    printPrivacy(fieldName, field, "NULL", fieldDest, fieldName + "_patterns");
+                    break;
                 }
-                printf("    NULL };\n");
-                printf("Privacy %s = { %d, %d, NULL, %d, %s_patterns };\n", fieldName.c_str(), field->number(),
-                        field->type(), p.dest(), fieldName.c_str());
-                hasDefaultFlags[i] = false;
-                break;
+                // else treat string field as primitive field and goes to default
             default:
-                if (isDefaultField(field)) break;
-                printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
-                        field->type(), p.dest());
-                hasDefaultFlags[i] = false;
+                if (!hasDefaultFlags[i]) printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
         }
-        // add the field name to message map, true means it has default flags
-        msgNames[fieldName] = hasDefaultFlags[i];
+        // Don't generate a variable twice
+        if (!hasDefaultFlags[i]) variableNames[fieldName] = false;
     }
 
     bool allDefaults = true;
-    for (int i=0; i<descriptor->field_count(); i++) {
+    for (size_t i=0; i<fieldsInOrder.size(); i++) {
         allDefaults &= hasDefaultFlags[i];
     }
 
-    parents->erase(messageTypeName); // erase the message type name when exit the message.
-    msgNames[messageTypeName] = allDefaults; // store the privacy tags of the message here to avoid overhead.
+    parents->erase(messageName); // erase the message type name when exit the message.
+    variableNames[messageName] = allDefaults; // store the privacy tags of the message here to avoid overhead.
 
     if (allDefaults) return false;
 
     emptyline();
     int policyCount = 0;
-    printf("Privacy* %s[] = {\n", messageTypeName.c_str());
-    for (int i=0; i<descriptor->field_count(); i++) {
-        const FieldDescriptor* field = descriptor->field(i);
+    printf("Privacy* %s[] = {\n", messageName.c_str());
+    for (size_t i=0; i<fieldsInOrder.size(); i++) {
+        const FieldDescriptor* field = fieldsInOrder[i];
         if (hasDefaultFlags[i]) continue;
         printf("    &%s,\n", getFieldName(field).c_str());
         policyCount++;
@@ -359,29 +417,30 @@
 
     // generates PRIVACY_POLICY_LIST
     printf("// Generate PRIVACY_POLICY_LIST.\n\n");
-    map<string, bool> messageNames;
+    map<string, bool> variableNames;
     set<string> parents;
-    bool skip[descriptor->field_count()];
+    vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
+    bool skip[fieldsInOrder.size()];
+    const Destination incidentDest = getPrivacyFlags(descriptor).dest();
 
-    for (int i=0; i<descriptor->field_count(); i++) {
-        const FieldDescriptor* field = descriptor->field(i);
+    for (size_t i=0; i<fieldsInOrder.size(); i++) {
+        const FieldDescriptor* field = fieldsInOrder[i];
         const string fieldName = getFieldName(field);
-        PrivacyFlags p = getPrivacyFlags(field);
+        const Destination fieldDest = getFieldDest(field);
+        const string fieldMessageName = getMessageName(field->message_type(), fieldDest);
 
         skip[i] = true;
 
         if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
             continue;
         }
-        // generate privacy flags for each field.
-        if (generatePrivacyFlags(field->message_type(), messageNames, &parents)) {
-            printf("Privacy %s { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(),
-                    field->type(), getMessageTypeName(field->message_type()).c_str(), p.dest());
-        } else if (isDefaultField(field)) {
+        // generate privacy flags for each section.
+        if (generatePrivacyFlags(field->message_type(), fieldDest, variableNames, &parents)) {
+            printPrivacy(fieldName, field, fieldMessageName, fieldDest, "NULL");
+        } else if (isDefaultField(field, incidentDest)) {
             continue; // don't create a new privacy if the value is default.
         } else {
-            printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
-                    field->type(), p.dest());
+            printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
         }
         skip[i] = false;
     }
@@ -391,16 +450,16 @@
     int policyCount = 0;
     if (gSelfRecursionAssignments.empty()) {
         printf("Privacy* privacyArray[] = {\n");
-        for (int i=0; i<descriptor->field_count(); i++) {
+        for (size_t i=0; i<fieldsInOrder.size(); i++) {
             if (skip[i]) continue;
-            printf("    &%s,\n", getFieldName(descriptor->field(i)).c_str());
+            printf("    &%s,\n", getFieldName(fieldsInOrder[i]).c_str());
             policyCount++;
         }
         printf("};\n\n");
         printf("const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(privacyArray);\n\n");
         printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
     } else {
-        for (int i=0; i<descriptor->field_count(); i++) {
+        for (size_t i=0; i<fieldsInOrder.size(); i++) {
             if (!skip[i]) policyCount++;
         }
 
@@ -410,9 +469,9 @@
         }
         printf("    Privacy** privacyArray = (Privacy**)malloc(%d * sizeof(Privacy**));\n", policyCount);
         policyCount = 0; // reset
-        for (int i=0; i<descriptor->field_count(); i++) {
+        for (size_t i=0; i<fieldsInOrder.size(); i++) {
             if (skip[i]) continue;
-            printf("    privacyArray[%d] = &%s;\n", policyCount++, getFieldName(descriptor->field(i)).c_str());
+            printf("    privacyArray[%d] = &%s;\n", policyCount++, getFieldName(fieldsInOrder[i]).c_str());
         }
         printf("    return const_cast<const Privacy**>(privacyArray);\n");
         printf("}\n\n");
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index abbb82a..0dd964c 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -164,12 +164,6 @@
     void enableAggressiveHandover(int enabled);
     int getAggressiveHandover();
 
-    void setAllowScansWithTraffic(int enabled);
-    int getAllowScansWithTraffic();
-
-    boolean setEnableAutoJoinWhenAssociated(boolean enabled);
-    boolean getEnableAutoJoinWhenAssociated();
-
     void enableWifiConnectivityManager(boolean enabled);
 
     WifiConnectionStatistics getConnectionStatistics();
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index bf8fed1..3eb13ce 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -22,7 +22,6 @@
 import android.net.NetworkUtils;
 import android.text.TextUtils;
 
-import java.lang.Math;
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.UnknownHostException;
@@ -126,143 +125,35 @@
     public long rxSuccess;
 
     /**
-     * Average rate of lost transmitted packets, in units of packets per 5 seconds.
+     * Average rate of lost transmitted packets, in units of packets per second.
      * @hide
      */
     public double txBadRate;
     /**
-     * Average rate of transmitted retry packets, in units of packets per 5 seconds.
+     * Average rate of transmitted retry packets, in units of packets per second.
      * @hide
      */
     public double txRetriesRate;
     /**
-     * Average rate of successfully transmitted unicast packets, in units of packets per 5 seconds.
+     * Average rate of successfully transmitted unicast packets, in units of packets per second.
      * @hide
      */
     public double txSuccessRate;
     /**
-     * Average rate of received unicast data packets, in units of packets per 5 seconds.
+     * Average rate of received unicast data packets, in units of packets per second.
      * @hide
      */
     public double rxSuccessRate;
 
-    private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
-    private static final long FILTER_TIME_CONSTANT = 3000;
-    /**
-     * This factor is used to adjust the rate output under the new algorithm
-     * such that the result is comparable to the previous algorithm.
-     * This actually converts from unit 'packets per second' to 'packets per 5 seconds'.
-     */
-    private static final long OUTPUT_SCALE_FACTOR = 5;
-    private long mLastPacketCountUpdateTimeStamp;
-
-    /**
-     * @hide
-     */
-    public int badRssiCount;
-
-    /**
-     * @hide
-     */
-    public int linkStuckCount;
-
-    /**
-     * @hide
-     */
-    public int lowRssiCount;
-
     /**
      * @hide
      */
     public int score;
 
     /**
-     * @hide
+     * Flag indicating that AP has hinted that upstream connection is metered,
+     * and sensitive to heavy data transfers.
      */
-    public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) {
-        if (stats != null) {
-            long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo;
-            long txretries = stats.retries_be + stats.retries_bk
-                    + stats.retries_vi + stats.retries_vo;
-            long rxgood = stats.rxmpdu_be + stats.rxmpdu_bk + stats.rxmpdu_vi + stats.rxmpdu_vo;
-            long txbad = stats.lostmpdu_be + stats.lostmpdu_bk
-                    + stats.lostmpdu_vi + stats.lostmpdu_vo;
-
-            if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
-                    && mLastPacketCountUpdateTimeStamp < timeStamp
-                    && txBad <= txbad
-                    && txSuccess <= txgood
-                    && rxSuccess <= rxgood
-                    && txRetries <= txretries) {
-                    long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp;
-                    double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT);
-                    double currentSampleWeight = 1.0 - lastSampleWeight;
-
-                    txBadRate = txBadRate * lastSampleWeight
-                        + (txbad - txBad) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
-                        * currentSampleWeight;
-                    txSuccessRate = txSuccessRate * lastSampleWeight
-                        + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
-                        * currentSampleWeight;
-                    rxSuccessRate = rxSuccessRate * lastSampleWeight
-                        + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
-                        * currentSampleWeight;
-                    txRetriesRate = txRetriesRate * lastSampleWeight
-                        + (txretries - txRetries) * OUTPUT_SCALE_FACTOR * 1000/ timeDelta
-                        * currentSampleWeight;
-            } else {
-                txBadRate = 0;
-                txSuccessRate = 0;
-                rxSuccessRate = 0;
-                txRetriesRate = 0;
-            }
-            txBad = txbad;
-            txSuccess = txgood;
-            rxSuccess = rxgood;
-            txRetries = txretries;
-            mLastPacketCountUpdateTimeStamp = timeStamp;
-        } else {
-            txBad = 0;
-            txSuccess = 0;
-            rxSuccess = 0;
-            txRetries = 0;
-            txBadRate = 0;
-            txSuccessRate = 0;
-            rxSuccessRate = 0;
-            txRetriesRate = 0;
-            mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
-        }
-    }
-
-
-    /**
-     * This function is less powerful and used if the WifiLinkLayerStats API is not implemented
-     * at the Wifi HAL
-     * @hide
-     */
-    public void updatePacketRates(long txPackets, long rxPackets) {
-        //paranoia
-        txBad = 0;
-        txRetries = 0;
-        txBadRate = 0;
-        txRetriesRate = 0;
-        if (txSuccess <= txPackets && rxSuccess <= rxPackets) {
-            txSuccessRate = (txSuccessRate * 0.5)
-                    + ((double) (txPackets - txSuccess) * 0.5);
-            rxSuccessRate = (rxSuccessRate * 0.5)
-                    + ((double) (rxPackets - rxSuccess) * 0.5);
-        } else {
-            txBadRate = 0;
-            txRetriesRate = 0;
-        }
-        txSuccess = txPackets;
-        rxSuccess = rxPackets;
-    }
-
-        /**
-         * Flag indicating that AP has hinted that upstream connection is metered,
-         * and sensitive to heavy data transfers.
-         */
     private boolean mMeteredHint;
 
     /** @hide */
@@ -274,7 +165,6 @@
         mRssi = INVALID_RSSI;
         mLinkSpeed = -1;
         mFrequency = -1;
-        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
     }
 
     /** @hide */
@@ -296,11 +186,7 @@
         txSuccessRate = 0;
         rxSuccessRate = 0;
         txRetriesRate = 0;
-        lowRssiCount = 0;
-        badRssiCount = 0;
-        linkStuckCount = 0;
         score = 0;
-        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
     }
 
     /**
@@ -328,12 +214,7 @@
             txRetriesRate = source.txRetriesRate;
             txSuccessRate = source.txSuccessRate;
             rxSuccessRate = source.rxSuccessRate;
-            mLastPacketCountUpdateTimeStamp =
-                source.mLastPacketCountUpdateTimeStamp;
             score = source.score;
-            badRssiCount = source.badRssiCount;
-            lowRssiCount = source.lowRssiCount;
-            linkStuckCount = source.linkStuckCount;
         }
     }
 
@@ -452,22 +333,6 @@
     }
 
     /**
-     * @hide
-     * This returns txSuccessRate in packets per second.
-     */
-    public double getTxSuccessRatePps() {
-        return txSuccessRate / OUTPUT_SCALE_FACTOR;
-    }
-
-    /**
-     * @hide
-     * This returns rxSuccessRate in packets per second.
-     */
-    public double getRxSuccessRatePps() {
-        return rxSuccessRate / OUTPUT_SCALE_FACTOR;
-    }
-
-    /**
      * Record the MAC address of the WLAN interface
      * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
      * @hide
@@ -658,8 +523,6 @@
         dest.writeDouble(txRetriesRate);
         dest.writeDouble(txBadRate);
         dest.writeDouble(rxSuccessRate);
-        dest.writeInt(badRssiCount);
-        dest.writeInt(lowRssiCount);
         mSupplicantState.writeToParcel(dest, flags);
     }
 
@@ -689,8 +552,6 @@
                 info.txRetriesRate = in.readDouble();
                 info.txBadRate = in.readDouble();
                 info.rxSuccessRate = in.readDouble();
-                info.badRssiCount = in.readInt();
-                info.lowRssiCount = in.readInt();
                 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
                 return info;
             }
diff --git a/wifi/java/android/net/wifi/WifiLinkLayerStats.java b/wifi/java/android/net/wifi/WifiLinkLayerStats.java
deleted file mode 100644
index edd400b..0000000
--- a/wifi/java/android/net/wifi/WifiLinkLayerStats.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcelable;
-import android.os.Parcel;
-
-import java.util.Arrays;
-
-/**
- * A class representing link layer statistics collected over a Wifi Interface.
- */
-/** {@hide} */
-public class WifiLinkLayerStats implements Parcelable {
-    private static final String TAG = "WifiLinkLayerStats";
-
-    /**
-     * The current status of this network configuration entry.
-     * @see Status
-     */
-    /** {@hide} */
-    public int status;
-
-    /**
-     * The network's SSID. Can either be an ASCII string,
-     * which must be enclosed in double quotation marks
-     * (e.g., {@code "MyNetwork"}, or a string of
-     * hex digits,which are not enclosed in quotes
-     * (e.g., {@code 01a243f405}).
-     */
-    /** {@hide} */
-    public String SSID;
-    /**
-     * When set. this is the BSSID the radio is currently associated with.
-     * The value is a string in the format of an Ethernet MAC address, e.g.,
-     * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit.
-     */
-    /** {@hide} */
-    public String BSSID;
-
-    /* number beacons received from our own AP */
-    /** {@hide} */
-    public int beacon_rx;
-
-    /* RSSI taken on management frames */
-    /** {@hide} */
-    public int rssi_mgmt;
-
-    /* packets counters */
-    /** {@hide} */
-    /* WME Best Effort Access Category (receive mpdu, transmit mpdu, lost mpdu, number of retries)*/
-    public long rxmpdu_be;
-    /** {@hide} */
-    public long txmpdu_be;
-    /** {@hide} */
-    public long lostmpdu_be;
-    /** {@hide} */
-    public long retries_be;
-    /** {@hide} */
-    /* WME Background Access Category (receive mpdu, transmit mpdu, lost mpdu, number of retries) */
-    public long rxmpdu_bk;
-    /** {@hide} */
-    public long txmpdu_bk;
-    /** {@hide} */
-    public long lostmpdu_bk;
-    /** {@hide} */
-    public long retries_bk;
-    /** {@hide} */
-    /* WME Video Access Category (receive mpdu, transmit mpdu, lost mpdu, number of retries) */
-    public long rxmpdu_vi;
-    /** {@hide} */
-    public long txmpdu_vi;
-    /** {@hide} */
-    public long lostmpdu_vi;
-    /** {@hide} */
-    public long retries_vi;
-    /** {@hide} */
-    /* WME Voice Access Category (receive mpdu, transmit mpdu, lost mpdu, number of retries) */
-    public long rxmpdu_vo;
-    /** {@hide} */
-    public long txmpdu_vo;
-    /** {@hide} */
-    public long lostmpdu_vo;
-    /** {@hide} */
-    public long retries_vo;
-
-    /** {@hide} */
-    public int on_time;
-    /** {@hide} */
-    public int tx_time;
-    /** {@hide} */
-    public int[] tx_time_per_level;
-    /** {@hide} */
-    public int rx_time;
-    /** {@hide} */
-    public int on_time_scan;
-
-    /** {@hide} */
-    public WifiLinkLayerStats() {
-    }
-
-    @Override
-    /** {@hide} */
-    public String toString() {
-        StringBuilder sbuf = new StringBuilder();
-        sbuf.append(" WifiLinkLayerStats: ").append('\n');
-
-        if (this.SSID != null) {
-            sbuf.append(" SSID: ").append(this.SSID).append('\n');
-        }
-        if (this.BSSID != null) {
-            sbuf.append(" BSSID: ").append(this.BSSID).append('\n');
-        }
-
-        sbuf.append(" my bss beacon rx: ").append(Integer.toString(this.beacon_rx)).append('\n');
-        sbuf.append(" RSSI mgmt: ").append(Integer.toString(this.rssi_mgmt)).append('\n');
-        sbuf.append(" BE : ").append(" rx=").append(Long.toString(this.rxmpdu_be))
-                .append(" tx=").append(Long.toString(this.txmpdu_be))
-                .append(" lost=").append(Long.toString(this.lostmpdu_be))
-                .append(" retries=").append(Long.toString(this.retries_be)).append('\n');
-        sbuf.append(" BK : ").append(" rx=").append(Long.toString(this.rxmpdu_bk))
-                .append(" tx=").append(Long.toString(this.txmpdu_bk))
-                .append(" lost=").append(Long.toString(this.lostmpdu_bk))
-                .append(" retries=").append(Long.toString(this.retries_bk)).append('\n');
-        sbuf.append(" VI : ").append(" rx=").append(Long.toString(this.rxmpdu_vi))
-                .append(" tx=").append(Long.toString(this.txmpdu_vi))
-                .append(" lost=").append(Long.toString(this.lostmpdu_vi))
-                .append(" retries=").append(Long.toString(this.retries_vi)).append('\n');
-        sbuf.append(" VO : ").append(" rx=").append(Long.toString(this.rxmpdu_vo))
-                .append(" tx=").append(Long.toString(this.txmpdu_vo))
-                .append(" lost=").append(Long.toString(this.lostmpdu_vo))
-                .append(" retries=").append(Long.toString(this.retries_vo)).append('\n');
-        sbuf.append(" on_time : ").append(Integer.toString(this.on_time))
-                .append(" rx_time=").append(Integer.toString(this.rx_time))
-                .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n')
-                .append(" tx_time=").append(Integer.toString(this.tx_time))
-                .append(" tx_time_per_level=" + Arrays.toString(tx_time_per_level));
-        return sbuf.toString();
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public int describeContents() {
-        return 0;
-    }
-
-    /** {@hide} */
-    public String getPrintableSsid() {
-        if (SSID == null) return "";
-        final int length = SSID.length();
-        if (length > 2 && (SSID.charAt(0) == '"') && SSID.charAt(length - 1) == '"') {
-            return SSID.substring(1, length - 1);
-        }
-
-        /** The ascii-encoded string format is P"<ascii-encoded-string>"
-         * The decoding is implemented in the supplicant for a newly configured
-         * network.
-         */
-        if (length > 3 && (SSID.charAt(0) == 'P') && (SSID.charAt(1) == '"') &&
-                (SSID.charAt(length-1) == '"')) {
-            WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(
-                    SSID.substring(2, length - 1));
-            return wifiSsid.toString();
-        }
-        return SSID;
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(SSID);
-        dest.writeString(BSSID);
-        dest.writeInt(on_time);
-        dest.writeInt(tx_time);
-        dest.writeIntArray(tx_time_per_level);
-        dest.writeInt(rx_time);
-        dest.writeInt(on_time_scan);
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public static final Creator<WifiLinkLayerStats> CREATOR =
-        new Creator<WifiLinkLayerStats>() {
-            public WifiLinkLayerStats createFromParcel(Parcel in) {
-                WifiLinkLayerStats stats = new WifiLinkLayerStats();
-                stats.SSID = in.readString();
-                stats.BSSID = in.readString();
-                stats.on_time = in.readInt();
-                stats.tx_time = in.readInt();
-                stats.tx_time_per_level = in.createIntArray();
-                stats.rx_time = in.readInt();
-                stats.on_time_scan = in.readInt();
-                return stats;
-            };
-            public WifiLinkLayerStats[] newArray(int size) {
-                return new WifiLinkLayerStats[size];
-            }
-
-        };
-}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 558004c..a158d94 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3488,27 +3488,23 @@
     }
 
     /**
-     * Set setting for allowing Scans when traffic is ongoing.
+     * Deprecated
+     * Does nothing
      * @hide
+     * @deprecated
      */
     public void setAllowScansWithTraffic(int enabled) {
-        try {
-            mService.setAllowScansWithTraffic(enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return;
     }
 
     /**
-     * Get setting for allowing Scans when traffic is ongoing.
+     * Deprecated
+     * returns value for 'disabled'
      * @hide
+     * @deprecated
      */
     public int getAllowScansWithTraffic() {
-        try {
-            return mService.getAllowScansWithTraffic();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return 0;
     }
 
     /**
@@ -3538,29 +3534,23 @@
     }
 
     /**
-     * Framework layer autojoin enable/disable when device is associated
-     * this will enable/disable autojoin scan and switch network when connected
-     * @return true -- if set successful false -- if set failed
+     * Deprecated
+     * returns false
      * @hide
+     * @deprecated
      */
     public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
-        try {
-            return mService.setEnableAutoJoinWhenAssociated(enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return false;
     }
 
     /**
-     * Get setting for Framework layer autojoin enable status
+     * Deprecated
+     * returns false
      * @hide
+     * @deprecated
      */
     public boolean getEnableAutoJoinWhenAssociated() {
-        try {
-            return mService.getEnableAutoJoinWhenAssociated();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return false;
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/rtt/ResponderConfig.java b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
index b503769..1090bfa 100644
--- a/wifi/java/android/net/wifi/rtt/ResponderConfig.java
+++ b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
@@ -248,7 +248,10 @@
      * Point (AP), which can be obtained from {@link android.net.wifi.WifiManager#getScanResults()}.
      */
     public static ResponderConfig fromScanResult(ScanResult scanResult) {
-        byte[] macAddress = new MacAddress(scanResult.BSSID).toByteArray();
+        byte[] macAddress = null;
+        if (scanResult.BSSID != null) {
+            macAddress = MacAddress.byteAddrFromStringAddr(scanResult.BSSID);
+        }
         int responderType = RESPONDER_AP;
         boolean supports80211mc = scanResult.is80211mcResponder();
         int channelWidth = translcateScanResultChannelWidth(scanResult.channelWidth);