Merge "BatteryStats: Add WorkChain support for WiFi events."
diff --git a/Android.mk b/Android.mk
index a19f2d9..8199c57 100644
--- a/Android.mk
+++ b/Android.mk
@@ -32,232 +32,26 @@
 # ============================================================
 include $(CLEAR_VARS)
 
-aidl_files := \
-        frameworks/base/telephony/java/android/telephony/mbms/DownloadRequest.aidl \
-        frameworks/base/telephony/java/android/telephony/mbms/FileInfo.aidl \
-        frameworks/base/telephony/java/android/telephony/mbms/FileServiceInfo.aidl \
-        frameworks/base/telephony/java/android/telephony/mbms/ServiceInfo.aidl \
-        frameworks/base/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl \
-	frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
-	frameworks/base/telephony/java/android/telephony/SubscriptionInfo.aidl \
-	frameworks/base/telephony/java/android/telephony/CellIdentityCdma.aidl \
-	frameworks/base/telephony/java/android/telephony/CellIdentityGsm.aidl \
-	frameworks/base/telephony/java/android/telephony/CellIdentityLte.aidl \
-	frameworks/base/telephony/java/android/telephony/CellIdentityWcdma.aidl \
-	frameworks/base/telephony/java/android/telephony/CellInfo.aidl \
-	frameworks/base/telephony/java/android/telephony/SignalStrength.aidl \
-	frameworks/base/telephony/java/android/telephony/IccOpenLogicalChannelResponse.aidl \
-	frameworks/base/telephony/java/android/telephony/NeighboringCellInfo.aidl \
-	frameworks/base/telephony/java/android/telephony/ModemActivityInfo.aidl \
-	frameworks/base/telephony/java/android/telephony/UiccAccessRule.aidl \
-	frameworks/base/telephony/java/android/telephony/data/DataCallResponse.aidl \
-	frameworks/base/telephony/java/android/telephony/data/DataProfile.aidl \
-	frameworks/base/telephony/java/android/telephony/euicc/DownloadableSubscription.aidl \
-	frameworks/base/telephony/java/android/telephony/euicc/EuiccInfo.aidl \
-	frameworks/base/location/java/android/location/Location.aidl \
-	frameworks/base/location/java/android/location/Address.aidl \
-	frameworks/base/location/java/android/location/Criteria.aidl \
-	frameworks/base/media/java/android/media/MediaMetadata.aidl \
-	frameworks/base/media/java/android/media/MediaDescription.aidl \
-	frameworks/base/media/java/android/media/Rating.aidl \
-	frameworks/base/media/java/android/media/AudioAttributes.aidl \
-	frameworks/base/media/java/android/media/AudioFocusInfo.aidl \
-	frameworks/base/media/java/android/media/session/PlaybackState.aidl \
-	frameworks/base/media/java/android/media/session/MediaSession.aidl \
-	frameworks/base/media/java/android/media/tv/TvInputInfo.aidl \
-	frameworks/base/media/java/android/media/tv/TvTrackInfo.aidl \
-	frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \
-	frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \
-	frameworks/base/wifi/java/android/net/wifi/aware/ConfigRequest.aidl \
-	frameworks/base/wifi/java/android/net/wifi/aware/PublishConfig.aidl \
-	frameworks/base/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl \
-	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \
-	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \
-	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \
-	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl \
-	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl \
-	frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl \
-	frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \
-	frameworks/base/wifi/java/android/net/wifi/rtt/RangingRequest.aidl \
-	frameworks/base/wifi/java/android/net/wifi/rtt/RangingResult.aidl \
-	frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \
-	frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \
-	frameworks/base/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl \
-	frameworks/base/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl \
-	frameworks/base/wifi/java/android/net/wifi/WifiConfiguration.aidl \
-	frameworks/base/wifi/java/android/net/wifi/WifiInfo.aidl \
-	frameworks/base/graphics/java/android/graphics/Region.aidl \
-	frameworks/base/graphics/java/android/graphics/Bitmap.aidl \
-	frameworks/base/graphics/java/android/graphics/Point.aidl \
-	frameworks/base/graphics/java/android/graphics/PointF.aidl \
-	frameworks/base/graphics/java/android/graphics/RectF.aidl \
-	frameworks/base/graphics/java/android/graphics/Rect.aidl \
-	frameworks/base/graphics/java/android/graphics/drawable/Icon.aidl \
-	frameworks/base/core/java/android/accounts/AuthenticatorDescription.aidl \
-	frameworks/base/core/java/android/accounts/Account.aidl \
-	frameworks/base/core/java/android/app/admin/ConnectEvent.aidl \
-	frameworks/base/core/java/android/app/admin/DnsEvent.aidl \
-	frameworks/base/core/java/android/app/admin/NetworkEvent.aidl \
-	frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
-	frameworks/base/core/java/android/app/admin/PasswordMetrics.aidl \
-	frameworks/base/core/java/android/app/slice/ISliceManager.aidl \
-	frameworks/base/core/java/android/app/slice/ISliceListener.aidl \
-	frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
-	frameworks/base/core/java/android/print/PageRange.aidl \
-	frameworks/base/core/java/android/print/PrintAttributes.aidl \
-	frameworks/base/core/java/android/print/PrinterCapabilitiesInfo.aidl \
-	frameworks/base/core/java/android/print/PrinterId.aidl \
-	frameworks/base/core/java/android/print/PrintJobInfo.aidl \
-	frameworks/base/core/java/android/print/PrinterInfo.aidl \
-	frameworks/base/core/java/android/print/PrintJobId.aidl \
-	frameworks/base/core/java/android/printservice/recommendation/RecommendationInfo.aidl \
-	frameworks/base/core/java/android/hardware/radio/ProgramSelector.aidl \
-	frameworks/base/core/java/android/hardware/radio/RadioManager.aidl \
-	frameworks/base/core/java/android/hardware/radio/RadioMetadata.aidl \
-	frameworks/base/core/java/android/hardware/usb/UsbDevice.aidl \
-	frameworks/base/core/java/android/hardware/usb/UsbInterface.aidl \
-	frameworks/base/core/java/android/hardware/usb/UsbEndpoint.aidl \
-	frameworks/base/core/java/android/hardware/usb/UsbAccessory.aidl \
-	frameworks/base/core/java/android/os/Messenger.aidl \
-	frameworks/base/core/java/android/os/PatternMatcher.aidl \
-	frameworks/base/core/java/android/os/Message.aidl \
-	frameworks/base/core/java/android/os/UserHandle.aidl \
-	frameworks/base/core/java/android/os/ParcelUuid.aidl \
-	frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
-	frameworks/base/core/java/android/os/ResultReceiver.aidl \
-	frameworks/base/core/java/android/os/WorkSource.aidl \
-	frameworks/base/core/java/android/os/DropBoxManager.aidl \
-	frameworks/base/core/java/android/os/Bundle.aidl \
-	frameworks/base/core/java/android/os/Debug.aidl \
-	frameworks/base/core/java/android/os/SharedMemory.aidl \
-	frameworks/base/core/java/android/os/StrictMode.aidl \
-	frameworks/base/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl \
-	frameworks/base/core/java/android/net/Network.aidl \
-	frameworks/base/core/java/android/net/RouteInfo.aidl \
-	frameworks/base/core/java/android/net/NetworkInfo.aidl \
-	frameworks/base/core/java/android/net/IpPrefix.aidl \
-	frameworks/base/core/java/android/net/NetworkCapabilities.aidl \
-	frameworks/base/core/java/android/net/DhcpInfo.aidl \
-	frameworks/base/core/java/android/net/ProxyInfo.aidl \
-	frameworks/base/core/java/android/net/LinkProperties.aidl \
-	frameworks/base/core/java/android/net/Uri.aidl \
-	frameworks/base/core/java/android/net/NetworkRequest.aidl \
-	frameworks/base/core/java/android/net/LinkAddress.aidl \
-	frameworks/base/core/java/android/util/MemoryIntArray.aidl \
-	frameworks/base/core/java/android/view/Display.aidl \
-	frameworks/base/core/java/android/view/InputDevice.aidl \
-	frameworks/base/core/java/android/view/InputEvent.aidl \
-	frameworks/native/aidl/gui/android/view/Surface.aidl \
-	frameworks/base/core/java/android/view/WindowContentFrameStats.aidl \
-	frameworks/base/core/java/android/view/inputmethod/InputMethodSubtype.aidl \
-	frameworks/base/core/java/android/view/inputmethod/CursorAnchorInfo.aidl \
-	frameworks/base/core/java/android/view/inputmethod/CompletionInfo.aidl \
-	frameworks/base/core/java/android/view/inputmethod/ExtractedText.aidl \
-	frameworks/base/core/java/android/view/inputmethod/EditorInfo.aidl \
-	frameworks/base/core/java/android/view/inputmethod/InputMethodInfo.aidl \
-	frameworks/base/core/java/android/view/inputmethod/CorrectionInfo.aidl \
-	frameworks/base/core/java/android/view/inputmethod/InputBinding.aidl \
-	frameworks/base/core/java/android/view/inputmethod/ExtractedTextRequest.aidl \
-	frameworks/base/core/java/android/view/DragEvent.aidl \
-	frameworks/base/core/java/android/view/KeyEvent.aidl \
-	frameworks/base/core/java/android/view/WindowManager.aidl \
-	frameworks/base/core/java/android/view/WindowAnimationFrameStats.aidl \
-	frameworks/base/core/java/android/view/MotionEvent.aidl \
-	frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl \
-	frameworks/base/core/java/android/view/accessibility/AccessibilityRecord.aidl \
-	frameworks/base/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl \
-	frameworks/base/core/java/android/view/accessibility/AccessibilityEvent.aidl \
-	frameworks/base/core/java/android/view/textservice/SpellCheckerSubtype.aidl \
-	frameworks/base/core/java/android/view/textservice/TextInfo.aidl \
-	frameworks/base/core/java/android/view/textservice/SpellCheckerInfo.aidl \
-	frameworks/base/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl \
-	frameworks/base/core/java/android/view/textservice/SuggestionsInfo.aidl \
-	frameworks/base/core/java/android/service/carrier/CarrierIdentifier.aidl \
-	frameworks/base/core/java/android/service/carrier/MessagePdu.aidl \
-	frameworks/base/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.aidl \
-	frameworks/base/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.aidl \
-	frameworks/base/core/java/android/service/euicc/GetEuiccProfileInfoListResult.aidl \
-	frameworks/base/core/java/android/service/notification/Adjustment.aidl \
-	frameworks/base/core/java/android/service/notification/Condition.aidl \
-	frameworks/base/core/java/android/service/notification/SnoozeCriterion.aidl \
-	frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
-	frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
-	frameworks/base/core/java/android/service/resolver/ResolverTarget.aidl \
-	frameworks/base/core/java/android/speech/tts/Voice.aidl \
-	frameworks/base/core/java/android/app/usage/CacheQuotaHint.aidl \
-	frameworks/base/core/java/android/app/usage/ExternalStorageStats.aidl \
-	frameworks/base/core/java/android/app/usage/StorageStats.aidl \
-	frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
-	frameworks/base/core/java/android/app/Notification.aidl \
-	frameworks/base/core/java/android/app/NotificationManager.aidl \
-	frameworks/base/core/java/android/app/WallpaperInfo.aidl \
-	frameworks/base/core/java/android/app/AppOpsManager.aidl \
-	frameworks/base/core/java/android/app/ActivityManager.aidl \
-	frameworks/base/core/java/android/app/PendingIntent.aidl \
-	frameworks/base/core/java/android/app/AlarmManager.aidl \
-	frameworks/base/core/java/android/app/SearchableInfo.aidl \
-	frameworks/base/core/java/android/app/VoiceInteractor.aidl \
-	frameworks/base/core/java/android/app/assist/AssistContent.aidl \
-	frameworks/base/core/java/android/app/assist/AssistStructure.aidl \
-	frameworks/base/core/java/android/app/job/JobParameters.aidl \
-	frameworks/base/core/java/android/app/job/JobInfo.aidl \
-	frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
-	frameworks/base/core/java/android/content/ClipDescription.aidl \
-	frameworks/base/core/java/android/content/IntentFilter.aidl \
-	frameworks/base/core/java/android/content/Intent.aidl \
-	frameworks/base/core/java/android/content/res/Configuration.aidl \
-	frameworks/base/core/java/android/content/res/ObbInfo.aidl \
-	frameworks/base/core/java/android/content/RestrictionEntry.aidl \
-	frameworks/base/core/java/android/content/ClipData.aidl \
-	frameworks/base/core/java/android/content/SyncAdapterType.aidl \
-	frameworks/base/core/java/android/content/SyncRequest.aidl \
-	frameworks/base/core/java/android/content/PeriodicSync.aidl \
-	frameworks/base/core/java/android/content/SyncResult.aidl \
-	frameworks/base/core/java/android/content/pm/FeatureInfo.aidl \
-	frameworks/base/core/java/android/content/pm/InstrumentationInfo.aidl \
-	frameworks/base/core/java/android/content/pm/PackageInstaller.aidl \
-	frameworks/base/core/java/android/content/pm/ServiceInfo.aidl \
-	frameworks/base/core/java/android/content/pm/Signature.aidl \
-	frameworks/base/core/java/android/content/pm/ApplicationInfo.aidl \
-	frameworks/base/core/java/android/content/pm/PermissionInfo.aidl \
-	frameworks/base/core/java/android/content/pm/ActivityInfo.aidl \
-	frameworks/base/core/java/android/content/pm/ConfigurationInfo.aidl \
-	frameworks/base/core/java/android/content/pm/PackageInfo.aidl \
-	frameworks/base/core/java/android/content/pm/ResolveInfo.aidl \
-	frameworks/base/core/java/android/content/pm/ProviderInfo.aidl \
-	frameworks/base/core/java/android/content/pm/PackageStats.aidl \
-	frameworks/base/core/java/android/content/pm/PermissionGroupInfo.aidl \
-	frameworks/base/core/java/android/content/pm/ShortcutInfo.aidl \
-	frameworks/base/core/java/android/content/pm/LabeledIntent.aidl \
-	frameworks/base/core/java/android/content/ComponentName.aidl \
-	frameworks/base/core/java/android/content/SyncStats.aidl \
-	frameworks/base/core/java/android/content/ContentValues.aidl \
-	frameworks/base/core/java/android/content/SyncInfo.aidl \
-	frameworks/base/core/java/android/content/IntentSender.aidl \
-	frameworks/base/core/java/android/widget/RemoteViews.aidl \
-	frameworks/base/core/java/android/text/style/SuggestionSpan.aidl \
-	frameworks/base/core/java/android/nfc/Tag.aidl \
-	frameworks/base/core/java/android/nfc/NdefRecord.aidl \
-	frameworks/base/core/java/android/nfc/NdefMessage.aidl \
-	frameworks/base/core/java/android/database/CursorWindow.aidl \
-	frameworks/base/core/java/android/service/quicksettings/Tile.aidl \
-	frameworks/native/aidl/binder/android/os/PersistableBundle.aidl \
-	system/bt/binder/android/bluetooth/BluetoothHealthAppConfiguration.aidl \
-	system/bt/binder/android/bluetooth/le/AdvertiseSettings.aidl \
-	system/bt/binder/android/bluetooth/le/ScanSettings.aidl \
-	system/bt/binder/android/bluetooth/le/AdvertiseData.aidl \
-	system/bt/binder/android/bluetooth/le/ScanFilter.aidl \
-	system/bt/binder/android/bluetooth/le/ScanResult.aidl \
-	system/bt/binder/android/bluetooth/BluetoothDevice.aidl \
-	system/netd/server/binder/android/net/UidRange.aidl \
-	frameworks/base/telephony/java/android/telephony/PcoData.aidl \
+aidl_parcelables :=
+define stubs-to-aidl-parcelables
+  gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/$1.aidl
+  aidl_parcelables += $$(gen)
+  $$(gen): $(call java-lib-header-files,$1) | $(HOST_OUT_EXECUTABLES)/sdkparcelables
+	@echo Extract SDK parcelables: $$@
+	rm -f $$@
+	$(HOST_OUT_EXECUTABLES)/sdkparcelables $$< $$@
+endef
+
+$(foreach stubs,android_stubs_current android_test_stubs_current android_system_stubs_current,\
+  $(eval $(call stubs-to-aidl-parcelables,$(stubs))))
 
 gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
-$(gen): PRIVATE_SRC_FILES := $(aidl_files)
-ALL_SDK_FILES += $(gen)
-$(gen): $(aidl_files) | $(AIDL)
-		@echo Aidl Preprocess: $@
-		$(hide) $(AIDL) --preprocess $@ $(PRIVATE_SRC_FILES)
+.KATI_RESTAT: $(gen)
+$(gen): $(aidl_parcelables)
+	@echo Combining SDK parcelables: $@
+	rm -f $@.tmp
+	cat $^ | sort -u > $@.tmp
+	$(call commit-change-for-toc,$@)
 
 # the documentation
 # ============================================================
@@ -554,8 +348,6 @@
 
 include $(BUILD_DROIDDOC)
 
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
 $(INTERNAL_PLATFORM_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 
@@ -591,8 +383,6 @@
 
 include $(BUILD_DROIDDOC)
 
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
 $(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 
@@ -629,8 +419,6 @@
 
 include $(BUILD_DROIDDOC)
 
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
 $(INTERNAL_PLATFORM_TEST_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
 
@@ -660,9 +448,6 @@
 
 include $(BUILD_DROIDDOC)
 
-# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
-$(full_target): $(gen)
-
 # Run this for checkbuild
 checkbuild: doc-comment-check-docs
 # Check comment when you are updating the API
diff --git a/api/current.txt b/api/current.txt
index 8925846..11f6760 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6338,7 +6338,7 @@
     method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
-    method public android.security.AttestedKeyPair generateKeyPair(android.content.ComponentName, java.lang.String, android.security.keystore.KeyGenParameterSpec);
+    method public android.security.AttestedKeyPair generateKeyPair(android.content.ComponentName, java.lang.String, android.security.keystore.KeyGenParameterSpec, int);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public java.util.Set<java.lang.String> getAffiliationIds(android.content.ComponentName);
@@ -6572,6 +6572,10 @@
     field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
     field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
     field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
+    field public static final int ID_TYPE_BASE_INFO = 1; // 0x1
+    field public static final int ID_TYPE_IMEI = 4; // 0x4
+    field public static final int ID_TYPE_MEID = 8; // 0x8
+    field public static final int ID_TYPE_SERIAL = 2; // 0x2
     field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
     field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
     field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20
@@ -6749,6 +6753,7 @@
     method public java.lang.CharSequence getText();
     method public int getTextBackgroundColor();
     method public int getTextColor();
+    method public java.lang.String getTextIdEntry();
     method public int[] getTextLineBaselines();
     method public int[] getTextLineCharOffsets();
     method public int getTextSelectionEnd();
@@ -7042,6 +7047,7 @@
     field public static final java.lang.String HINT_SUMMARY = "summary";
     field public static final java.lang.String HINT_TITLE = "title";
     field public static final java.lang.String SUBTYPE_COLOR = "color";
+    field public static final java.lang.String SUBTYPE_CONTENT_DESCRIPTION = "content_description";
     field public static final java.lang.String SUBTYPE_MESSAGE = "message";
     field public static final java.lang.String SUBTYPE_PRIORITY = "priority";
     field public static final java.lang.String SUBTYPE_SLIDER = "slider";
@@ -15513,6 +15519,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_FACING;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_APERTURES;
@@ -31570,6 +31577,7 @@
     method public final boolean postAtTime(java.lang.Runnable, long);
     method public final boolean postAtTime(java.lang.Runnable, java.lang.Object, long);
     method public final boolean postDelayed(java.lang.Runnable, long);
+    method public final boolean postDelayed(java.lang.Runnable, java.lang.Object, long);
     method public final void removeCallbacks(java.lang.Runnable);
     method public final void removeCallbacks(java.lang.Runnable, java.lang.Object);
     method public final void removeCallbacksAndMessages(java.lang.Object);
@@ -37722,18 +37730,12 @@
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
   }
 
-  public final class EditDistanceScorer implements android.os.Parcelable android.service.autofill.Scorer {
-    method public int describeContents();
-    method public static android.service.autofill.EditDistanceScorer getInstance();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
-  }
-
   public final class FieldClassification {
     method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
   }
 
   public static final class FieldClassification.Match {
+    method public java.lang.String getAlgorithm();
     method public java.lang.String getRemoteId();
     method public float getScore();
   }
@@ -37885,9 +37887,6 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public abstract interface Scorer {
-  }
-
   public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
     method public int describeContents();
@@ -37900,6 +37899,7 @@
 
   public final class UserData implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getFieldClassificationAlgorithm();
     method public static int getMaxFieldClassificationIdsSize();
     method public static int getMaxUserDataSize();
     method public static int getMaxValueLength();
@@ -37909,9 +37909,10 @@
   }
 
   public static final class UserData.Builder {
-    ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
+    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();
+    method public android.service.autofill.UserData.Builder setFieldClassificationAlgorithm(java.lang.String, android.os.Bundle);
   }
 
   public abstract interface Validator {
@@ -41195,7 +41196,7 @@
 
   public class SubscriptionManager {
     method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
-    method public static android.telephony.SubscriptionManager from(android.content.Context);
+    method public static deprecated android.telephony.SubscriptionManager from(android.content.Context);
     method public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
     method public int getActiveSubscriptionInfoCount();
     method public int getActiveSubscriptionInfoCountMax();
@@ -47257,6 +47258,7 @@
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
+    method public void setTextIdEntry(java.lang.String);
     method public abstract void setTextLines(int[], int[]);
     method public abstract void setTextStyle(float, int, int, int);
     method public abstract void setTransformation(android.graphics.Matrix);
@@ -47776,6 +47778,7 @@
     method public java.lang.CharSequence getPackageName();
     method public android.view.accessibility.AccessibilityRecord getRecord(int);
     method public int getRecordCount();
+    method public int getWindowChanges();
     method public void initFromParcel(android.os.Parcel);
     method public static android.view.accessibility.AccessibilityEvent obtain(int);
     method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
@@ -47820,6 +47823,17 @@
     field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
     field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
     field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
+    field public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 128; // 0x80
+    field public static final int WINDOWS_CHANGE_ACTIVE = 32; // 0x20
+    field public static final int WINDOWS_CHANGE_ADDED = 1; // 0x1
+    field public static final int WINDOWS_CHANGE_BOUNDS = 8; // 0x8
+    field public static final int WINDOWS_CHANGE_CHILDREN = 512; // 0x200
+    field public static final int WINDOWS_CHANGE_FOCUSED = 64; // 0x40
+    field public static final int WINDOWS_CHANGE_LAYER = 16; // 0x10
+    field public static final int WINDOWS_CHANGE_PARENT = 256; // 0x100
+    field public static final int WINDOWS_CHANGE_PIP = 1024; // 0x400
+    field public static final int WINDOWS_CHANGE_REMOVED = 2; // 0x2
+    field public static final int WINDOWS_CHANGE_TITLE = 4; // 0x4
   }
 
   public abstract interface AccessibilityEventSource {
@@ -48531,6 +48545,8 @@
     method public void commit();
     method public void disableAutofillServices();
     method public android.content.ComponentName getAutofillServiceComponentName();
+    method public java.util.List<java.lang.String> getAvailableFieldClassificationAlgorithms();
+    method public java.lang.String getDefaultFieldClassificationAlgorithm();
     method public android.service.autofill.UserData getUserData();
     method public boolean hasEnabledAutofillServices();
     method public boolean isAutofillSupported();
diff --git a/api/system-current.txt b/api/system-current.txt
index 3e78167..ec9ac82 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1340,9 +1340,30 @@
 
 package android.hardware.location {
 
+  public class ContextHubClient implements java.io.Closeable {
+    method public void close();
+    method public android.hardware.location.ContextHubInfo getAttachedHub();
+    method public int sendMessageToNanoApp(android.hardware.location.NanoAppMessage);
+  }
+
+  public class ContextHubClientCallback {
+    ctor public ContextHubClientCallback();
+    method public void onHubReset(android.hardware.location.ContextHubClient);
+    method public void onMessageFromNanoApp(android.hardware.location.ContextHubClient, android.hardware.location.NanoAppMessage);
+    method public void onNanoAppAborted(android.hardware.location.ContextHubClient, long, int);
+    method public void onNanoAppDisabled(android.hardware.location.ContextHubClient, long);
+    method public void onNanoAppEnabled(android.hardware.location.ContextHubClient, long);
+    method public void onNanoAppLoaded(android.hardware.location.ContextHubClient, long);
+    method public void onNanoAppUnloaded(android.hardware.location.ContextHubClient, long);
+  }
+
   public class ContextHubInfo implements android.os.Parcelable {
     ctor public ContextHubInfo();
     method public int describeContents();
+    method public byte getChreApiMajorVersion();
+    method public byte getChreApiMinorVersion();
+    method public short getChrePatchVersion();
+    method public long getChrePlatformId();
     method public int getId();
     method public int getMaxPacketLengthBytes();
     method public android.hardware.location.MemoryRegion[] getMemoryRegions();
@@ -1362,19 +1383,27 @@
   }
 
   public final class ContextHubManager {
-    method public int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter);
-    method public int[] getContextHubHandles();
-    method public android.hardware.location.ContextHubInfo getContextHubInfo(int);
-    method public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
-    method public int loadNanoApp(int, android.hardware.location.NanoApp);
-    method public int registerCallback(android.hardware.location.ContextHubManager.Callback);
-    method public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
-    method public int sendMessage(int, int, android.hardware.location.ContextHubMessage);
-    method public int unloadNanoApp(int);
-    method public int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
+    method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback, java.util.concurrent.Executor);
+    method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback);
+    method public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(android.hardware.location.ContextHubInfo, long);
+    method public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(android.hardware.location.ContextHubInfo, long);
+    method public deprecated int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter);
+    method public deprecated int[] getContextHubHandles();
+    method public deprecated android.hardware.location.ContextHubInfo getContextHubInfo(int);
+    method public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs();
+    method public deprecated android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
+    method public deprecated int loadNanoApp(int, android.hardware.location.NanoApp);
+    method public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(android.hardware.location.ContextHubInfo, android.hardware.location.NanoAppBinary);
+    method public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(android.hardware.location.ContextHubInfo);
+    method public deprecated int registerCallback(android.hardware.location.ContextHubManager.Callback);
+    method public deprecated int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
+    method public deprecated int sendMessage(int, int, android.hardware.location.ContextHubMessage);
+    method public deprecated int unloadNanoApp(int);
+    method public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(android.hardware.location.ContextHubInfo, long);
+    method public deprecated int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
   }
 
-  public static abstract class ContextHubManager.Callback {
+  public static abstract deprecated class ContextHubManager.Callback {
     ctor protected ContextHubManager.Callback();
     method public abstract void onMessageReceipt(int, int, android.hardware.location.ContextHubMessage);
   }
@@ -1392,6 +1421,37 @@
     field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubMessage> CREATOR;
   }
 
+  public class ContextHubTransaction<T> {
+    method public int getType();
+    method public void setOnCompleteListener(android.hardware.location.ContextHubTransaction.OnCompleteListener<T>, java.util.concurrent.Executor);
+    method public void setOnCompleteListener(android.hardware.location.ContextHubTransaction.OnCompleteListener<T>);
+    method public static java.lang.String typeToString(int, boolean);
+    method public android.hardware.location.ContextHubTransaction.Response<T> waitForResponse(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    field public static final int RESULT_FAILED_AT_HUB = 5; // 0x5
+    field public static final int RESULT_FAILED_BAD_PARAMS = 2; // 0x2
+    field public static final int RESULT_FAILED_BUSY = 4; // 0x4
+    field public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; // 0x8
+    field public static final int RESULT_FAILED_SERVICE_INTERNAL_FAILURE = 7; // 0x7
+    field public static final int RESULT_FAILED_TIMEOUT = 6; // 0x6
+    field public static final int RESULT_FAILED_UNINITIALIZED = 3; // 0x3
+    field public static final int RESULT_FAILED_UNKNOWN = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+    field public static final int TYPE_DISABLE_NANOAPP = 3; // 0x3
+    field public static final int TYPE_ENABLE_NANOAPP = 2; // 0x2
+    field public static final int TYPE_LOAD_NANOAPP = 0; // 0x0
+    field public static final int TYPE_QUERY_NANOAPPS = 4; // 0x4
+    field public static final int TYPE_UNLOAD_NANOAPP = 1; // 0x1
+  }
+
+  public static abstract interface ContextHubTransaction.OnCompleteListener<L> {
+    method public abstract void onComplete(android.hardware.location.ContextHubTransaction<L>, android.hardware.location.ContextHubTransaction.Response<L>);
+  }
+
+  public static class ContextHubTransaction.Response<R> {
+    method public R getContents();
+    method public int getResult();
+  }
+
   public final class GeofenceHardware {
     method public boolean addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback);
     method public int[] getMonitoringTypes();
@@ -1508,6 +1568,25 @@
     field public static final android.os.Parcelable.Creator<android.hardware.location.NanoApp> CREATOR;
   }
 
+  public final class NanoAppBinary implements android.os.Parcelable {
+    ctor public NanoAppBinary(byte[]);
+    method public int describeContents();
+    method public byte[] getBinary();
+    method public byte[] getBinaryNoHeader();
+    method public int getFlags();
+    method public int getHeaderVersion();
+    method public long getHwHubType();
+    method public long getNanoAppId();
+    method public int getNanoAppVersion();
+    method public byte getTargetChreApiMajorVersion();
+    method public byte getTargetChreApiMinorVersion();
+    method public boolean hasValidHeader();
+    method public boolean isEncrypted();
+    method public boolean isSigned();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppBinary> CREATOR;
+  }
+
   public class NanoAppFilter {
     ctor public NanoAppFilter(long, int, int, long);
     method public int describeContents();
@@ -1541,6 +1620,28 @@
     field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppInstanceInfo> CREATOR;
   }
 
+  public final class NanoAppMessage implements android.os.Parcelable {
+    method public static android.hardware.location.NanoAppMessage createMessageFromNanoApp(long, int, byte[], boolean);
+    method public static android.hardware.location.NanoAppMessage createMessageToNanoApp(long, int, byte[]);
+    method public int describeContents();
+    method public byte[] getMessageBody();
+    method public int getMessageType();
+    method public long getNanoAppId();
+    method public boolean isBroadcastMessage();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppMessage> CREATOR;
+  }
+
+  public final class NanoAppState implements android.os.Parcelable {
+    ctor public NanoAppState(long, int, boolean);
+    method public int describeContents();
+    method public long getNanoAppId();
+    method public long getNanoAppVersion();
+    method public boolean isEnabled();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppState> CREATOR;
+  }
+
 }
 
 package android.hardware.radio {
@@ -4070,6 +4171,7 @@
   public class SubscriptionManager {
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
     method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
+    field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
   }
 
   public final class SubscriptionPlan implements android.os.Parcelable {
@@ -4368,10 +4470,10 @@
   }
 
   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 boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
+    method public byte[] getData(long);
     method public byte[] getMetadata();
-    method public boolean removeConfiguration(java.lang.String);
+    method public boolean removeConfiguration(long);
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index d56b0856..9be538b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -165,6 +165,8 @@
     method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
     method public abstract java.lang.String getPermissionControllerPackageName();
     method public abstract boolean isPermissionReviewModeEnabled();
+    field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
+    field public static final java.lang.String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
   }
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -435,6 +437,10 @@
     field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
   }
 
+  public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+    field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
+  }
+
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
@@ -473,7 +479,8 @@
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
   }
 
-  public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
+  public final class EditDistanceScorer {
+    method public static android.service.autofill.EditDistanceScorer getInstance();
     method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
   }
 
@@ -489,11 +496,6 @@
     ctor public InternalSanitizer();
   }
 
-  public abstract class InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
-    ctor public InternalScorer();
-    method public abstract float getScore(android.view.autofill.AutofillValue, java.lang.String);
-  }
-
   public abstract class InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     ctor public InternalTransformation();
   }
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index fb8ef63..11d3e49 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -72,9 +72,7 @@
 gen_src_dir:=
 GEN:=
 
-ifeq ($(BUILD_WITH_INCIDENTD_RC), true)
 LOCAL_INIT_RC := incidentd.rc
-endif
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 77e8efa..2bb7edc 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -16,16 +16,10 @@
 
 package com.android.commands.sm;
 
-import static android.os.storage.StorageManager.PROP_ADOPTABLE_FBE;
-import static android.os.storage.StorageManager.PROP_HAS_ADOPTABLE;
-import static android.os.storage.StorageManager.PROP_VIRTUAL_DISK;
-
-import android.os.IBinder;
 import android.os.IVoldTaskListener;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.storage.DiskInfo;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
@@ -145,15 +139,7 @@
     }
 
     public void runHasAdoptable() {
-        final boolean hasHardware = SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false)
-                || SystemProperties.getBoolean(PROP_VIRTUAL_DISK, false);
-        final boolean hasSoftware;
-        if (StorageManager.isFileEncryptedNativeOnly()) {
-            hasSoftware = SystemProperties.getBoolean(PROP_ADOPTABLE_FBE, false);
-        } else {
-            hasSoftware = true;
-        }
-        System.out.println(hasHardware && hasSoftware);
+        System.out.println(StorageManager.hasAdoptable());
     }
 
     public void runGetPrimaryStorageUuid() throws RemoteException {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index a365f54..d829243 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -19,6 +19,7 @@
     ../../core/java/android/os/IStatsManager.aidl \
     src/stats_log.proto \
     src/statsd_config.proto \
+    src/statsd_internal.proto \
     src/atoms.proto \
     src/field_util.cpp \
     src/stats_log_util.cpp \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 6da1243..288ebe9 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -21,8 +21,8 @@
 namespace os {
 namespace statsd {
 
-android::hash_t hashDimensionsValue(const DimensionsValue& value) {
-    android::hash_t hash = 0;
+android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value) {
+    android::hash_t hash = seed;
     hash = android::JenkinsHashMix(hash, android::hash_type(value.field()));
 
     hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case()));
@@ -63,6 +63,10 @@
     return JenkinsHashWhiten(hash);
 }
 
+android::hash_t hashDimensionsValue(const DimensionsValue& value) {
+    return hashDimensionsValue(0, value);
+}
+
 using std::string;
 
 
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 3a4ffce..85c317f 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -53,6 +53,7 @@
     DimensionsValue mDimensionsValue;
 };
 
+android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value);
 android::hash_t hashDimensionsValue(const DimensionsValue& value);
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 13f332e..991badc 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -54,7 +54,7 @@
 const int FIELD_ID_REPORTS = 2;
 // for ConfigKey
 const int FIELD_ID_UID = 1;
-const int FIELD_ID_NAME = 2;
+const int FIELD_ID_ID = 2;
 // for ConfigMetricsReport
 const int FIELD_ID_METRICS = 1;
 const int FIELD_ID_UID_MAP = 2;
@@ -127,7 +127,7 @@
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
         newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
-        if (config.log_source().package().size() > 0) {
+        if (newMetricsManager->shouldAddUidMapListener()) {
             // We have to add listener after the MetricsManager is constructed because it's
             // not safe to create wp or sp from this pointer inside its constructor.
             mUidMap->addListener(newMetricsManager.get());
@@ -150,14 +150,15 @@
     return it->second->byteSize();
 }
 
-void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report) {
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs,
+                                     ConfigMetricsReportList* report) {
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
         return;
     }
     report->mutable_config_key()->set_uid(key.GetUid());
-    report->mutable_config_key()->set_name(key.GetName());
+    report->mutable_config_key()->set_id(key.GetId());
     ConfigMetricsReport* configMetricsReport = report->add_reports();
     it->second->onDumpReport(dumpTimeStampNs, configMetricsReport);
     // TODO: dump uid mapping.
@@ -181,7 +182,7 @@
     // Start of ConfigKey.
     long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
     proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
-    proto.write(FIELD_TYPE_STRING | FIELD_ID_NAME, key.GetName());
+    proto.write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
     proto.end(configKeyToken);
     // End of ConfigKey.
 
@@ -278,8 +279,8 @@
         vector<uint8_t> data;
         onDumpReport(key, &data);
         // TODO: Add a guardrail to prevent accumulation of file on disk.
-        string file_name = StringPrintf("%s/%d-%s-%ld", STATS_DATA_DIR, key.GetUid(),
-                                        key.GetName().c_str(), time(nullptr));
+        string file_name = StringPrintf("%s/%d-%lld-%ld", STATS_DATA_DIR, key.GetUid(),
+                                        (long long)key.GetId(), time(nullptr));
         StorageManager::writeFile(file_name.c_str(), &data[0], data.size());
     }
 }
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index e8b0bd2..45f1ea1 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -335,7 +335,7 @@
         print_cmd_help(out);
         return UNKNOWN_ERROR;
     }
-    auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name));
+    auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, StrToInt64(name)));
     sp<IStatsCompanionService> sc = getStatsCompanionService();
     if (sc != nullptr) {
         sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
@@ -404,13 +404,13 @@
                 }
 
                 // Add / update the config.
-                mConfigManager->UpdateConfig(ConfigKey(uid, name), config);
+                mConfigManager->UpdateConfig(ConfigKey(uid, StrToInt64(name)), config);
             } else {
                 if (argCount == 2) {
                     cmd_remove_all_configs(out);
                 } else {
                     // Remove the config.
-                    mConfigManager->RemoveConfig(ConfigKey(uid, name));
+                    mConfigManager->RemoveConfig(ConfigKey(uid, StrToInt64(name)));
                 }
             }
 
@@ -459,7 +459,7 @@
         }
         if (good) {
             vector<uint8_t> data;
-            mProcessor->onDumpReport(ConfigKey(uid, name), &data);
+            mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), &data);
             // TODO: print the returned StatsLogReport to file instead of printing to logcat.
             if (proto) {
                 for (size_t i = 0; i < data.size(); i ++) {
@@ -699,12 +699,11 @@
     mProcessor->OnLogEvent(event);
 }
 
-Status StatsService::getData(const String16& key, vector<uint8_t>* output) {
+Status StatsService::getData(int64_t key, vector<uint8_t>* output) {
     IPCThreadState* ipc = IPCThreadState::self();
     VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
     if (checkCallingPermission(String16(kPermissionDump))) {
-        string keyStr = string(String8(key).string());
-        ConfigKey configKey(ipc->getCallingUid(), keyStr);
+        ConfigKey configKey(ipc->getCallingUid(), key);
         mProcessor->onDumpReport(configKey, output);
         return Status::ok();
     } else {
@@ -724,14 +723,13 @@
     }
 }
 
-Status StatsService::addConfiguration(const String16& key,
+Status StatsService::addConfiguration(int64_t key,
                                       const vector <uint8_t>& config,
                                       const String16& package, const String16& cls,
                                       bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
-        string keyString = string(String8(key).string());
-        ConfigKey configKey(ipc->getCallingUid(), keyString);
+        ConfigKey configKey(ipc->getCallingUid(), key);
         StatsdConfig cfg;
         if (!cfg.ParseFromArray(&config[0], config.size())) {
             *success = false;
@@ -748,11 +746,10 @@
     }
 }
 
-Status StatsService::removeConfiguration(const String16& key, bool* success) {
+Status StatsService::removeConfiguration(int64_t key, bool* success) {
     IPCThreadState* ipc = IPCThreadState::self();
     if (checkCallingPermission(String16(kPermissionDump))) {
-        string keyStr = string(String8(key).string());
-        mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr));
+        mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), key));
         *success = true;
         return Status::ok();
     } else {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 08fcdac..c0424f3 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -77,25 +77,27 @@
     /**
      * Binder call for clients to request data for this configuration key.
      */
-    virtual Status getData(const String16& key, vector<uint8_t>* output) override;
+    virtual Status getData(int64_t 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.
      */
-    virtual Status addConfiguration(const String16& key, const vector <uint8_t>& config,
-                                   const String16& package, const String16& cls, bool* success)
+    virtual Status addConfiguration(int64_t key, const vector <uint8_t>& config,
+                                    const String16& package, const String16& cls, bool* success)
     override;
 
     /**
      * Binder call to allow clients to remove the specified configuration.
      */
-    virtual Status removeConfiguration(const String16& key, bool* success) override;
+    virtual Status removeConfiguration(int64_t key, bool* success) override;
 
     // TODO: public for testing since statsd doesn't run when system starts. Change to private
     // later.
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index f8a9413..05c68e1 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -34,11 +34,11 @@
 AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
     : mAlert(alert),
       mConfigKey(configKey),
-      mNumOfPastBuckets(mAlert.number_of_buckets() - 1) {
+      mNumOfPastBuckets(mAlert.num_buckets() - 1) {
     VLOG("AnomalyTracker() called");
-    if (mAlert.number_of_buckets() <= 0) {
+    if (mAlert.num_buckets() <= 0) {
         ALOGE("Cannot create AnomalyTracker with %lld buckets",
-              (long long)mAlert.number_of_buckets());
+              (long long)mAlert.num_buckets());
         return;
     }
     if (!mAlert.has_trigger_if_sum_gt()) {
@@ -205,23 +205,21 @@
     // TODO: If we had access to the bucket_size_millis, consider calling resetStorage()
     // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) { resetStorage(); }
 
-    if (mAlert.has_incidentd_details()) {
-        if (mAlert.has_name()) {
-            ALOGI("An anomaly (%s) has occurred! Informing incidentd.",
-                  mAlert.name().c_str());
+    if (!mSubscriptions.empty()) {
+        if (mAlert.has_id()) {
+            ALOGI("An anomaly (%llu) has occurred! Informing subscribers.",mAlert.id());
+            informSubscribers();
         } else {
-            // TODO: Can construct a name based on the criteria (and/or relay the criteria).
-            ALOGI("An anomaly (nameless) has occurred! Informing incidentd.");
+            ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers.");
         }
-        informIncidentd();
     } else {
-        ALOGI("An anomaly has occurred! (But informing incidentd not requested.)");
+        ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
     }
 
-    StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.name());
+    StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
 
     android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
-                               mConfigKey.GetName().c_str(), mAlert.name().c_str());
+                               mConfigKey.GetId(), mAlert.id());
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
@@ -246,27 +244,46 @@
             timestampNs - mLastAnomalyTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC;
 }
 
-void AnomalyTracker::informIncidentd() {
-    VLOG("informIncidentd called.");
-    if (!mAlert.has_incidentd_details()) {
-        ALOGE("Attempted to call incidentd without any incidentd_details.");
-        return;
-    }
-    sp<IIncidentManager> service = interface_cast<IIncidentManager>(
-            defaultServiceManager()->getService(android::String16("incident")));
-    if (service == NULL) {
-        ALOGW("Couldn't get the incident service.");
+void AnomalyTracker::informSubscribers() {
+    VLOG("informSubscribers called.");
+    if (mSubscriptions.empty()) {
+        ALOGE("Attempt to call with no subscribers.");
         return;
     }
 
-    IncidentReportArgs incidentReport;
-    const Alert::IncidentdDetails& details = mAlert.incidentd_details();
-    for (int i = 0; i < details.section_size(); i++) {
-        incidentReport.addSection(details.section(i));
+    std::set<int> incidentdSections;
+    for (const Subscription& subscription : mSubscriptions) {
+        switch (subscription.subscriber_information_case()) {
+            case Subscription::SubscriberInformationCase::kIncidentdDetails:
+                for (int i = 0; i < subscription.incidentd_details().section_size(); i++) {
+                    incidentdSections.insert(subscription.incidentd_details().section(i));
+                }
+                break;
+            case Subscription::SubscriberInformationCase::kPerfettoDetails:
+                ALOGW("Perfetto reports not implemented.");
+                break;
+            default:
+                break;
+        }
     }
-    // TODO: Pass in mAlert.name() into the addHeader?
-
-    service->reportIncident(incidentReport);
+    if (!incidentdSections.empty()) {
+        sp<IIncidentManager> service = interface_cast<IIncidentManager>(
+                defaultServiceManager()->getService(android::String16("incident")));
+        if (service != NULL) {
+            IncidentReportArgs incidentReport;
+            for (const auto section : incidentdSections) {
+                incidentReport.addSection(section);
+            }
+            int64_t alertId = mAlert.id();
+            std::vector<uint8_t> header;
+            uint8_t* src = static_cast<uint8_t*>(static_cast<void*>(&alertId));
+            header.insert(header.end(), src, src + sizeof(int64_t));
+            incidentReport.addHeader(header);
+            service->reportIncident(incidentReport);
+        } else {
+            ALOGW("Couldn't get the incident service.");
+        }
+    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 48f0203..2d5ab86 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -40,6 +40,11 @@
 
     virtual ~AnomalyTracker();
 
+    // Add subscriptions that depend on this alert.
+    void addSubscription(const Subscription& subscription) {
+        mSubscriptions.push_back(subscription);
+    }
+
     // Adds a bucket.
     // Bucket index starts from 0.
     void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
@@ -97,6 +102,9 @@
     // statsd_config.proto Alert message that defines this tracker.
     const Alert mAlert;
 
+    // The subscriptions that depend on this alert.
+    std::vector<Subscription> mSubscriptions;
+
     // A reference to the Alert's config key.
     const ConfigKey& mConfigKey;
 
@@ -104,7 +112,7 @@
     // for the anomaly detection (since the current bucket is not in the past).
     int mNumOfPastBuckets;
 
-    // The exisiting bucket list.
+    // The existing bucket list.
     std::vector<shared_ptr<DimToValMap>> mPastBuckets;
 
     // Sum over all existing buckets cached in mPastBuckets.
@@ -133,8 +141,8 @@
     // Resets all bucket data. For use when all the data gets stale.
     virtual void resetStorage();
 
-    // Informs the incident service that an anomaly has occurred.
-    void informIncidentd();
+    // Informs the subscribers that an anomaly has occurred.
+    void informSubscribers();
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1ee86f0..221a554 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -845,11 +845,11 @@
     // Uid that owns the config whose anomaly detection alert fired.
     optional int32 config_uid = 1;
 
-    // Name of the config whose anomaly detection alert fired.
-    optional string config_name = 2;
+    // Id of the config whose anomaly detection alert fired.
+    optional int64 config_id = 2;
 
-    // Name of the alert (i.e. name of the anomaly that was detected).
-    optional string alert_name = 3;
+    // Id of the alert (i.e. name of the anomaly that was detected).
+    optional int64 alert_id = 3;
 }
 
 /**
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 52b83d8..afa26f6 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -30,20 +30,20 @@
 using std::unordered_map;
 using std::vector;
 
-CombinationConditionTracker::CombinationConditionTracker(const string& name, const int index)
-    : ConditionTracker(name, index) {
-    VLOG("creating CombinationConditionTracker %s", mName.c_str());
+CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
+    : ConditionTracker(id, index) {
+    VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
 }
 
 CombinationConditionTracker::~CombinationConditionTracker() {
-    VLOG("~CombinationConditionTracker() %s", mName.c_str());
+    VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId);
 }
 
 bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
                                        const vector<sp<ConditionTracker>>& allConditionTrackers,
-                                       const unordered_map<string, int>& conditionNameIndexMap,
+                                       const unordered_map<int64_t, int>& conditionIdIndexMap,
                                        vector<bool>& stack) {
-    VLOG("Combination predicate init() %s", mName.c_str());
+    VLOG("Combination predicate init() %lld", (long long)mConditionId);
     if (mInitialized) {
         return true;
     }
@@ -62,11 +62,11 @@
         return false;
     }
 
-    for (string child : combinationCondition.predicate()) {
-        auto it = conditionNameIndexMap.find(child);
+    for (auto child : combinationCondition.predicate()) {
+        auto it = conditionIdIndexMap.find(child);
 
-        if (it == conditionNameIndexMap.end()) {
-            ALOGW("Predicate %s not found in the config", child.c_str());
+        if (it == conditionIdIndexMap.end()) {
+            ALOGW("Predicate %lld not found in the config", (long long)child);
             return false;
         }
 
@@ -79,13 +79,13 @@
         }
 
         bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
-                                                     conditionNameIndexMap, stack);
+                                                     conditionIdIndexMap, stack);
 
         if (!initChildSucceeded) {
-            ALOGW("Child initialization failed %s ", child.c_str());
+            ALOGW("Child initialization failed %lld ", (long long)child);
             return false;
         } else {
-            ALOGW("Child initialization success %s ", child.c_str());
+            ALOGW("Child initialization success %lld ", (long long)child);
         }
 
         mChildren.push_back(childIndex);
@@ -154,8 +154,8 @@
             }
         }
         nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
-        ALOGD("CombinationPredicate %s sliced may changed? %d", mName.c_str(),
-              conditionChangedCache[mIndex] == true);
+        ALOGD("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
+            conditionChangedCache[mIndex] == true);
     }
 }
 
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 8942833..dfd3837 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -26,13 +26,13 @@
 
 class CombinationConditionTracker : public virtual ConditionTracker {
 public:
-    CombinationConditionTracker(const std::string& name, const int index);
+    CombinationConditionTracker(const int64_t& id, const int index);
 
     ~CombinationConditionTracker();
 
     bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
-              const std::unordered_map<std::string, int>& conditionNameIndexMap,
+              const std::unordered_map<int64_t, int>& conditionIdIndexMap,
               std::vector<bool>& stack) override;
 
     void evaluateCondition(const LogEvent& event,
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 1154b6f..773860f 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -32,8 +32,8 @@
 
 class ConditionTracker : public virtual RefBase {
 public:
-    ConditionTracker(const std::string& name, const int index)
-        : mName(name),
+    ConditionTracker(const int64_t& id, const int index)
+        : mConditionId(id),
           mIndex(index),
           mInitialized(false),
           mTrackerIndex(),
@@ -42,7 +42,7 @@
 
     virtual ~ConditionTracker(){};
 
-    inline const string& getName() { return mName; }
+    inline const int64_t& getId() { return mConditionId; }
 
     // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
     // be done in the constructor, but we do it separately because (1) easy to return a bool to
@@ -50,11 +50,11 @@
     // allConditionConfig: the list of all Predicate config from statsd_config.
     // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
     //                       need to call init() on children conditions)
-    // conditionNameIndexMap: the mapping from condition name to its index.
+    // conditionIdIndexMap: the mapping from condition id to its index.
     // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
     virtual bool init(const std::vector<Predicate>& allConditionConfig,
                       const std::vector<sp<ConditionTracker>>& allConditionTrackers,
-                      const std::unordered_map<std::string, int>& conditionNameIndexMap,
+                      const std::unordered_map<int64_t, int>& conditionIdIndexMap,
                       std::vector<bool>& stack) = 0;
 
     // evaluate current condition given the new event.
@@ -99,9 +99,7 @@
     }
 
 protected:
-    // We don't really need the string name, but having a name here makes log messages
-    // easy to debug.
-    const std::string mName;
+    const int64_t mConditionId;
 
     // the index of this condition in the manager's condition list.
     const int mIndex;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 1803cbb..2525721 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -33,17 +33,17 @@
 using std::vector;
 
 SimpleConditionTracker::SimpleConditionTracker(
-        const ConfigKey& key, const string& name, const int index,
+        const ConfigKey& key, const int64_t& id, const int index,
         const SimplePredicate& simplePredicate,
-        const unordered_map<string, int>& trackerNameIndexMap)
-    : ConditionTracker(name, index), mConfigKey(key) {
-    VLOG("creating SimpleConditionTracker %s", mName.c_str());
+        const unordered_map<int64_t, int>& trackerNameIndexMap)
+    : ConditionTracker(id, index), mConfigKey(key) {
+    VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
     mCountNesting = simplePredicate.count_nesting();
 
     if (simplePredicate.has_start()) {
         auto pair = trackerNameIndexMap.find(simplePredicate.start());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Start matcher %s not found in the config", simplePredicate.start().c_str());
+            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
             return;
         }
         mStartLogMatcherIndex = pair->second;
@@ -55,7 +55,7 @@
     if (simplePredicate.has_stop()) {
         auto pair = trackerNameIndexMap.find(simplePredicate.stop());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop matcher %s not found in the config", simplePredicate.stop().c_str());
+            ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
             return;
         }
         mStopLogMatcherIndex = pair->second;
@@ -67,7 +67,7 @@
     if (simplePredicate.has_stop_all()) {
         auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop all matcher %s not found in the config", simplePredicate.stop().c_str());
+            ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
             return;
         }
         mStopAllLogMatcherIndex = pair->second;
@@ -99,15 +99,15 @@
 
 bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
                                   const vector<sp<ConditionTracker>>& allConditionTrackers,
-                                  const unordered_map<string, int>& conditionNameIndexMap,
+                                  const unordered_map<int64_t, int>& conditionIdIndexMap,
                                   vector<bool>& stack) {
     // SimpleConditionTracker does not have dependency on other conditions, thus we just return
     // if the initialization was successful.
     return mInitialized;
 }
 
-void print(map<HashableDimensionKey, int>& conditions, const string& name) {
-    VLOG("%s DUMP:", name.c_str());
+void print(map<HashableDimensionKey, int>& conditions, const int64_t& id) {
+    VLOG("%lld DUMP:", (long long)id);
     for (const auto& pair : conditions) {
         VLOG("\t%s : %d", pair.first.c_str(), pair.second);
     }
@@ -135,10 +135,11 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mSlicedConditionState.size() + 1;
-        StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount);
+        StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("Predicate %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
+            ALOGE("Predicate %lld dropping data for dimension key %s",
+                (long long)mConditionId, newKey.c_str());
             return true;
         }
     }
@@ -222,13 +223,13 @@
 
     // dump all dimensions for debugging
     if (DEBUG) {
-        print(mSlicedConditionState, mName);
+        print(mSlicedConditionState, mConditionId);
     }
 
     conditionChangedCache[mIndex] = changed;
     conditionCache[mIndex] = newCondition;
 
-    VLOG("SimplePredicate %s nonSlicedChange? %d", mName.c_str(),
+    VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
          conditionChangedCache[mIndex] == true);
 }
 
@@ -239,7 +240,8 @@
                                                vector<bool>& conditionChangedCache) {
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
-        VLOG("Yes, already evaluated, %s %d", mName.c_str(), conditionCache[mIndex]);
+        VLOG("Yes, already evaluated, %lld %d",
+            (long long)mConditionId, conditionCache[mIndex]);
         return;
     }
 
@@ -320,11 +322,11 @@
         const ConditionKey& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions,
         vector<ConditionState>& conditionCache) const {
-    const auto pair = conditionParameters.find(mName);
+    const auto pair = conditionParameters.find(mConditionId);
 
     if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
-        ALOGE("Predicate %s output has dimension, but it's not specified in the query!",
-              mName.c_str());
+        ALOGE("Predicate %lld output has dimension, but it's not specified in the query!",
+              (long long)mConditionId);
         conditionCache[mIndex] = mInitialValue;
         return;
     }
@@ -343,7 +345,7 @@
         }
     }
     conditionCache[mIndex] = conditionState;
-    VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]);
+    VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 5048635..815b445 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -29,15 +29,15 @@
 
 class SimpleConditionTracker : public virtual ConditionTracker {
 public:
-    SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index,
+    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
                            const SimplePredicate& simplePredicate,
-                           const std::unordered_map<std::string, int>& trackerNameIndexMap);
+                           const std::unordered_map<int64_t, int>& trackerNameIndexMap);
 
     ~SimpleConditionTracker();
 
     bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
-              const std::unordered_map<std::string, int>& conditionNameIndexMap,
+              const std::unordered_map<int64_t, int>& conditionIdIndexMap,
               std::vector<bool>& stack) override;
 
     void evaluateCondition(const LogEvent& event,
diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp
index a365dc0..d791f86 100644
--- a/cmds/statsd/src/config/ConfigKey.cpp
+++ b/cmds/statsd/src/config/ConfigKey.cpp
@@ -27,10 +27,10 @@
 ConfigKey::ConfigKey() {
 }
 
-ConfigKey::ConfigKey(const ConfigKey& that) : mName(that.mName), mUid(that.mUid) {
+ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) {
 }
 
-ConfigKey::ConfigKey(int uid, const string& name) : mName(name), mUid(uid) {
+ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) {
 }
 
 ConfigKey::~ConfigKey() {
@@ -38,10 +38,21 @@
 
 string ConfigKey::ToString() const {
     ostringstream out;
-    out << '(' << mUid << ',' << mName << ')';
+    out << '(' << mUid << ',' << mId << ')';
     return out.str();
 }
 
+
+int64_t StrToInt64(const string& str) {
+    char* endp;
+    int64_t value;
+    value = strtoll(str.c_str(), &endp, 0);
+    if (endp == str.c_str() || *endp != '\0') {
+        value = 0;
+    }
+    return value;
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
index 3489c43..3ad0eed 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -37,14 +37,14 @@
 public:
     ConfigKey();
     explicit ConfigKey(const ConfigKey& that);
-    ConfigKey(int uid, const string& name);
+    ConfigKey(int uid, const int64_t& id);
     ~ConfigKey();
 
     inline int GetUid() const {
         return mUid;
     }
-    inline const string& GetName() const {
-        return mName;
+    inline const int64_t& GetId() const {
+        return mId;
     }
 
     inline bool operator<(const ConfigKey& that) const {
@@ -54,17 +54,17 @@
         if (mUid > that.mUid) {
             return false;
         }
-        return mName < that.mName;
+        return mId < that.mId;
     };
 
     inline bool operator==(const ConfigKey& that) const {
-        return mUid == that.mUid && mName == that.mName;
+        return mUid == that.mUid && mId == that.mId;
     };
 
     string ToString() const;
 
 private:
-    string mName;
+    int64_t mId;
     int mUid;
 };
 
@@ -72,6 +72,8 @@
     return os << config.ToString();
 }
 
+int64_t StrToInt64(const string& str);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
@@ -87,7 +89,7 @@
 template <>
 struct hash<ConfigKey> {
     std::size_t operator()(const ConfigKey& key) const {
-        return (7 * key.GetUid()) ^ ((hash<string>()(key.GetName())));
+        return (7 * key.GetUid()) ^ ((hash<long long>()(key.GetId())));
     }
 };
 
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index addc111..184f69e 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -49,15 +49,13 @@
 void ConfigManager::Startup() {
     map<ConfigKey, StatsdConfig> configsFromDisk;
     StorageManager::readConfigFromDisk(configsFromDisk);
-    // TODO(b/70667694): Make the configs from disk be used. And remove the fake config,
-    // and tests shouldn't call this Startup(), maybe call StartupForTest() so we don't read
-    // configs from disk for tests.
-    // for (const auto& pair : configsFromDisk) {
-    //    UpdateConfig(pair.first, pair.second);
-    //}
+    for (const auto& pair : configsFromDisk) {
+        UpdateConfig(pair.first, pair.second);
+    }
+}
 
-    // Uncomment the following line and use the hard coded config for development.
-    // UpdateConfig(ConfigKey(1000, "fake"), build_fake_config());
+void ConfigManager::StartupForTest() {
+    // Dummy function to avoid reading configs from disks for tests.
 }
 
 void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
@@ -103,7 +101,7 @@
 }
 
 void ConfigManager::remove_saved_configs(const ConfigKey& key) {
-    string prefix = StringPrintf("%d-%s", key.GetUid(), key.GetName().c_str());
+    string prefix = StringPrintf("%d-%lld", key.GetUid(), (long long)key.GetId());
     StorageManager::deletePrefixedFiles(STATS_SERVICE_DIR, prefix.c_str());
 }
 
@@ -173,7 +171,7 @@
     fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
     fprintf(out, "     uid name\n");
     for (const auto& key : mConfigs) {
-        fprintf(out, "  %6d %s\n", key.GetUid(), key.GetName().c_str());
+        fprintf(out, "  %6d %lld\n", key.GetUid(), (long long)key.GetId());
         auto receiverIt = mConfigReceivers.find(key);
         if (receiverIt != mConfigReceivers.end()) {
             fprintf(out, "    -> received by %s, %s\n", receiverIt->second.first.c_str(),
@@ -189,8 +187,8 @@
     remove_saved_configs(key);
 
     // Then we save the latest config.
-    string file_name = StringPrintf("%s/%d-%s-%ld", STATS_SERVICE_DIR, key.GetUid(),
-                                    key.GetName().c_str(), time(nullptr));
+    string file_name = StringPrintf("%s/%d-%lld-%ld", STATS_SERVICE_DIR, key.GetUid(),
+                                    (long long)key.GetId(), time(nullptr));
     const int numBytes = config.ByteSize();
     vector<uint8_t> buffer(numBytes);
     config.SerializeToArray(&buffer[0], numBytes);
@@ -200,7 +198,7 @@
 StatsdConfig build_fake_config() {
     // HACK: Hard code a test metric for counting screen on events...
     StatsdConfig config;
-    config.set_name("CONFIG_12345");
+    config.set_id(12345);
 
     int WAKE_LOCK_TAG_ID = 1111;  // put a fake id here to make testing easier.
     int WAKE_LOCK_UID_KEY_ID = 1;
@@ -232,14 +230,14 @@
 
     // Count Screen ON events.
     CountMetric* metric = config.add_count_metric();
-    metric->set_name("METRIC_1");
-    metric->set_what("SCREEN_TURNED_ON");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    metric->set_id(1);  // METRIC_1
+    metric->set_what(102);  //  "SCREEN_TURNED_ON"
+    metric->set_bucket(ONE_MINUTE);
 
     // Anomaly threshold for screen-on count.
     // TODO(b/70627390): Uncomment once the bug is fixed.
     /*Alert* alert = config.add_alert();
-    alert->set_name("ALERT_1");
+    alert->set_id("ALERT_1");
     alert->set_metric_name("METRIC_1");
     alert->set_number_of_buckets(6);
     alert->set_trigger_if_sum_gt(10);
@@ -248,17 +246,17 @@
     details->add_section(12);
     details->add_section(13);*/
 
-    AllowedLogSource* logSource = config.mutable_log_source();
-    logSource->add_uid(1000);
-    logSource->add_uid(0);
-    logSource->add_package("com.android.statsd.dogfood");
-    logSource->add_package("com.android.bluetooth");
+    config.add_allowed_log_source("AID_ROOT");
+    config.add_allowed_log_source("AID_SYSTEM");
+    config.add_allowed_log_source("AID_BLUETOOTH");
+    config.add_allowed_log_source("com.android.statsd.dogfood");
+    config.add_allowed_log_source("com.android.systemui");
 
     // Count process state changes, slice by uid.
     metric = config.add_count_metric();
-    metric->set_name("METRIC_2");
-    metric->set_what("PROCESS_STATE_CHANGE");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    metric->set_id(2);  // "METRIC_2"
+    metric->set_what(104);
+    metric->set_bucket(ONE_MINUTE);
     FieldMatcher* dimensions = metric->mutable_dimensions();
     dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
     dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
@@ -267,7 +265,7 @@
     // TODO(b/70627390): Uncomment once the bug is fixed.
     /*
     alert = config.add_alert();
-    alert->set_name("ALERT_2");
+    alert->set_id("ALERT_2");
     alert->set_metric_name("METRIC_2");
     alert->set_number_of_buckets(4);
     alert->set_trigger_if_sum_gt(30);
@@ -278,28 +276,28 @@
 
     // Count process state changes, slice by uid, while SCREEN_IS_OFF
     metric = config.add_count_metric();
-    metric->set_name("METRIC_3");
-    metric->set_what("PROCESS_STATE_CHANGE");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    metric->set_id(3);
+    metric->set_what(104);
+    metric->set_bucket(ONE_MINUTE);
 
     dimensions = metric->mutable_dimensions();
     dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
     dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
-    metric->set_condition("SCREEN_IS_OFF");
+    metric->set_condition(202);
 
     // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
     metric = config.add_count_metric();
-    metric->set_name("METRIC_4");
-    metric->set_what("APP_GET_WL");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    metric->set_id(4);
+    metric->set_what(107);
+    metric->set_bucket(ONE_MINUTE);
     dimensions = metric->mutable_dimensions();
     dimensions->set_field(WAKE_LOCK_TAG_ID);
     dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
 
 
-    metric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
+    metric->set_condition(204);
     MetricConditionLink* link = metric->add_links();
-    link->set_condition("APP_IS_BACKGROUND");
+    link->set_condition(203);
     link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
     link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
     link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
@@ -307,16 +305,16 @@
 
     // Duration of an app holding any wl, while screen on and app in background, slice by uid
     DurationMetric* durationMetric = config.add_duration_metric();
-    durationMetric->set_name("METRIC_5");
-    durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    durationMetric->set_id(5);
+    durationMetric->set_bucket(ONE_MINUTE);
     durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
     dimensions = durationMetric->mutable_dimensions();
     dimensions->set_field(WAKE_LOCK_TAG_ID);
     dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
-    durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
+    durationMetric->set_what(205);
+    durationMetric->set_condition(204);
     link = durationMetric->add_links();
-    link->set_condition("APP_IS_BACKGROUND");
+    link->set_condition(203);
     link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
     link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
     link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
@@ -324,16 +322,16 @@
 
     // max Duration of an app holding any wl, while screen on and app in background, slice by uid
     durationMetric = config.add_duration_metric();
-    durationMetric->set_name("METRIC_6");
-    durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    durationMetric->set_id(6);
+    durationMetric->set_bucket(ONE_MINUTE);
     durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
     dimensions = durationMetric->mutable_dimensions();
     dimensions->set_field(WAKE_LOCK_TAG_ID);
     dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
-    durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
+    durationMetric->set_what(205);
+    durationMetric->set_condition(204);
     link = durationMetric->add_links();
-    link->set_condition("APP_IS_BACKGROUND");
+    link->set_condition(203);
     link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
     link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
     link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
@@ -341,13 +339,13 @@
 
     // Duration of an app holding any wl, while screen on and app in background
     durationMetric = config.add_duration_metric();
-    durationMetric->set_name("METRIC_7");
-    durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    durationMetric->set_id(7);
+    durationMetric->set_bucket(ONE_MINUTE);
     durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
-    durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
+    durationMetric->set_what(205);
+    durationMetric->set_condition(204);
     link = durationMetric->add_links();
-    link->set_condition("APP_IS_BACKGROUND");
+    link->set_condition(203);
     link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
     link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
     link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
@@ -356,17 +354,17 @@
 
     // Duration of screen on time.
     durationMetric = config.add_duration_metric();
-    durationMetric->set_name("METRIC_8");
-    durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
+    durationMetric->set_id(8);
+    durationMetric->set_bucket(ONE_MINUTE);
     durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-    durationMetric->set_what("SCREEN_IS_ON");
+    durationMetric->set_what(201);
 
     // Anomaly threshold for background count.
     // TODO(b/70627390): Uncomment once the bug is fixed.
     /*
     alert = config.add_alert();
-    alert->set_name("ALERT_8");
-    alert->set_metric_name("METRIC_8");
+    alert->set_id(308);
+    alert->set_metric_id(8);
     alert->set_number_of_buckets(4);
     alert->set_trigger_if_sum_gt(2000000000); // 2 seconds
     alert->set_refractory_period_secs(120);
@@ -375,38 +373,39 @@
 
     // Value metric to count KERNEL_WAKELOCK when screen turned on
     ValueMetric* valueMetric = config.add_value_metric();
-    valueMetric->set_name("METRIC_6");
-    valueMetric->set_what("KERNEL_WAKELOCK");
-    valueMetric->set_value_field(KERNEL_WAKELOCK_COUNT_KEY);
-    valueMetric->set_condition("SCREEN_IS_ON");
+    valueMetric->set_id(11);
+    valueMetric->set_what(109);
+    valueMetric->mutable_value_field()->set_field(KERNEL_WAKELOCK_TAG_ID);
+    valueMetric->mutable_value_field()->add_child()->set_field(KERNEL_WAKELOCK_COUNT_KEY);
+    valueMetric->set_condition(201);
     dimensions = valueMetric->mutable_dimensions();
     dimensions->set_field(KERNEL_WAKELOCK_TAG_ID);
     dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY);
     // This is for testing easier. We should never set bucket size this small.
-    valueMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
+    durationMetric->set_bucket(ONE_MINUTE);
 
     // Add an EventMetric to log process state change events.
     EventMetric* eventMetric = config.add_event_metric();
-    eventMetric->set_name("METRIC_9");
-    eventMetric->set_what("SCREEN_TURNED_ON");
+    eventMetric->set_id(9);
+    eventMetric->set_what(102); // "SCREEN_TURNED_ON"
 
     // Add an GaugeMetric.
     GaugeMetric* gaugeMetric = config.add_gauge_metric();
-    gaugeMetric->set_name("METRIC_10");
-    gaugeMetric->set_what("DEVICE_TEMPERATURE");
+    gaugeMetric->set_id(10);
+    gaugeMetric->set_what(101);
     auto gaugeFieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(DEVICE_TEMPERATURE_TAG_ID);
     gaugeFieldMatcher->add_child()->set_field(DEVICE_TEMPERATURE_KEY);
-    gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
+    durationMetric->set_bucket(ONE_MINUTE);
 
     // Event matchers.
     AtomMatcher* temperatureAtomMatcher = config.add_atom_matcher();
-    temperatureAtomMatcher->set_name("DEVICE_TEMPERATURE");
+    temperatureAtomMatcher->set_id(101);  // "DEVICE_TEMPERATURE"
     temperatureAtomMatcher->mutable_simple_atom_matcher()->set_atom_id(
         DEVICE_TEMPERATURE_TAG_ID);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_TURNED_ON");
+    eventMatcher->set_id(102);  // "SCREEN_TURNED_ON"
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
     FieldValueMatcher* fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
@@ -414,7 +413,7 @@
     fieldValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_TURNED_OFF");
+    eventMatcher->set_id(103);  // "SCREEN_TURNED_OFF"
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
     fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
@@ -422,12 +421,12 @@
     fieldValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("PROCESS_STATE_CHANGE");
+    eventMatcher->set_id(104);  // "PROCESS_STATE_CHANGE"
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(UID_PROCESS_STATE_TAG_ID);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("APP_GOES_BACKGROUND");
+    eventMatcher->set_id(105);  // "APP_GOES_BACKGROUND"
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
     fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
@@ -435,7 +434,7 @@
     fieldValueMatcher->set_eq_int(APP_USAGE_BACKGROUND);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("APP_GOES_FOREGROUND");
+    eventMatcher->set_id(106);  // "APP_GOES_FOREGROUND"
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
     fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
@@ -443,7 +442,7 @@
     fieldValueMatcher->set_eq_int(APP_USAGE_FOREGROUND);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("APP_GET_WL");
+    eventMatcher->set_id(107);  // "APP_GET_WL"
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
     fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
@@ -451,7 +450,7 @@
     fieldValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("APP_RELEASE_WL");
+    eventMatcher->set_id(108);  //"APP_RELEASE_WL"
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
     fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
@@ -460,47 +459,47 @@
 
     // pulled events
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("KERNEL_WAKELOCK");
+    eventMatcher->set_id(109);  // "KERNEL_WAKELOCK"
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(KERNEL_WAKELOCK_TAG_ID);
 
     // Predicates.............
     Predicate* predicate = config.add_predicate();
-    predicate->set_name("SCREEN_IS_ON");
+    predicate->set_id(201);  // "SCREEN_IS_ON"
     SimplePredicate* simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start("SCREEN_TURNED_ON");
-    simplePredicate->set_stop("SCREEN_TURNED_OFF");
+    simplePredicate->set_start(102);  // "SCREEN_TURNED_ON"
+    simplePredicate->set_stop(103);
     simplePredicate->set_count_nesting(false);
 
     predicate = config.add_predicate();
-    predicate->set_name("SCREEN_IS_OFF");
+    predicate->set_id(202);  // "SCREEN_IS_OFF"
     simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start("SCREEN_TURNED_OFF");
-    simplePredicate->set_stop("SCREEN_TURNED_ON");
+    simplePredicate->set_start(103);
+    simplePredicate->set_stop(102);  // "SCREEN_TURNED_ON"
     simplePredicate->set_count_nesting(false);
 
     predicate = config.add_predicate();
-    predicate->set_name("APP_IS_BACKGROUND");
+    predicate->set_id(203);  // "APP_IS_BACKGROUND"
     simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start("APP_GOES_BACKGROUND");
-    simplePredicate->set_stop("APP_GOES_FOREGROUND");
+    simplePredicate->set_start(105);
+    simplePredicate->set_stop(106);
     FieldMatcher* predicate_dimension1 = simplePredicate->mutable_dimensions();
     predicate_dimension1->set_field(APP_USAGE_TAG_ID);
     predicate_dimension1->add_child()->set_field(APP_USAGE_UID_KEY_ID);
     simplePredicate->set_count_nesting(false);
 
     predicate = config.add_predicate();
-    predicate->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
+    predicate->set_id(204);  // "APP_IS_BACKGROUND_AND_SCREEN_ON"
     Predicate_Combination* combination_predicate = predicate->mutable_combination();
     combination_predicate->set_operation(LogicalOperation::AND);
-    combination_predicate->add_predicate("APP_IS_BACKGROUND");
-    combination_predicate->add_predicate("SCREEN_IS_ON");
+    combination_predicate->add_predicate(203);
+    combination_predicate->add_predicate(201);
 
     predicate = config.add_predicate();
-    predicate->set_name("WL_HELD_PER_APP_PER_NAME");
+    predicate->set_id(205);  // "WL_HELD_PER_APP_PER_NAME"
     simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start("APP_GET_WL");
-    simplePredicate->set_stop("APP_RELEASE_WL");
+    simplePredicate->set_start(107);
+    simplePredicate->set_stop(108);
     FieldMatcher* predicate_dimension = simplePredicate->mutable_dimensions();
     predicate_dimension1->set_field(WAKE_LOCK_TAG_ID);
     predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
@@ -508,10 +507,10 @@
     simplePredicate->set_count_nesting(true);
 
     predicate = config.add_predicate();
-    predicate->set_name("WL_HELD_PER_APP");
+    predicate->set_id(206);  // "WL_HELD_PER_APP"
     simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start("APP_GET_WL");
-    simplePredicate->set_stop("APP_RELEASE_WL");
+    simplePredicate->set_start(107);
+    simplePredicate->set_stop(108);
     simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
     predicate_dimension = simplePredicate->mutable_dimensions();
     predicate_dimension->set_field(WAKE_LOCK_TAG_ID);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index ea42a35..ad666bc 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -48,6 +48,11 @@
      */
     void Startup();
 
+    /*
+     * Dummy initializer for tests.
+     */
+    void StartupForTest();
+
     /**
      * Someone else wants to know about the configs.
      */
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
index 886a33b..45b3586 100644
--- a/cmds/statsd/src/dimension.cpp
+++ b/cmds/statsd/src/dimension.cpp
@@ -17,6 +17,7 @@
 #include "Log.h"
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h"
 #include "dimension.h"
 #include "field_util.h"
 
@@ -351,6 +352,23 @@
     }
 }
 
+long getLongFromDimenValue(const DimensionsValue& dimensionValue) {
+    switch (dimensionValue.value_case()) {
+        case DimensionsValue::ValueCase::kValueInt:
+            return dimensionValue.value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return dimensionValue.value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return dimensionValue.value_bool() ? 1 : 0;
+        case DimensionsValue::ValueCase::kValueFloat:
+            return (int64_t)dimensionValue.value_float();
+        case DimensionsValue::ValueCase::kValueTuple:
+        case DimensionsValue::ValueCase::kValueStr:
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+            return 0;
+    }
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index c866958..5bb64a9 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -56,6 +56,8 @@
 
 bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub);
 
+// Helper function to get long value from the DimensionsValue proto.
+long getLongFromDimenValue(const DimensionsValue& dimensionValue);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h
index 5907e17..3e7d54c 100644
--- a/cmds/statsd/src/field_util.h
+++ b/cmds/statsd/src/field_util.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 
 #include <unordered_map>
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index bf277f0..33927aa 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -80,7 +80,7 @@
 
     StatsdStatsReport_ConfigStats configStats;
     configStats.set_uid(key.GetUid());
-    configStats.set_name(key.GetName());
+    configStats.set_id(key.GetId());
     configStats.set_creation_time_sec(nowTimeSec);
     configStats.set_metric_count(metricsCount);
     configStats.set_condition_count(conditionsCount);
@@ -196,34 +196,34 @@
     mUidMapStats.set_bytes_used(bytes);
 }
 
-void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) {
+void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
     lock_guard<std::mutex> lock(mLock);
     // if name doesn't exist before, it will create the key with count 0.
     auto& conditionSizeMap = mConditionStats[key];
-    if (size > conditionSizeMap[name]) {
-        conditionSizeMap[name] = size;
+    if (size > conditionSizeMap[id]) {
+        conditionSizeMap[id] = size;
     }
 }
 
-void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const string& name, int size) {
+void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
     lock_guard<std::mutex> lock(mLock);
     // if name doesn't exist before, it will create the key with count 0.
     auto& metricsDimensionMap = mMetricsStats[key];
-    if (size > metricsDimensionMap[name]) {
-        metricsDimensionMap[name] = size;
+    if (size > metricsDimensionMap[id]) {
+        metricsDimensionMap[id] = size;
     }
 }
 
-void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) {
+void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
     lock_guard<std::mutex> lock(mLock);
     auto& matcherStats = mMatcherStats[key];
-    matcherStats[name]++;
+    matcherStats[id]++;
 }
 
-void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const string& name) {
+void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) {
     lock_guard<std::mutex> lock(mLock);
     auto& alertStats = mAlertStats[key];
-    alertStats[name]++;
+    alertStats[id]++;
 }
 
 void StatsdStats::noteRegisteredAnomalyAlarmChanged() {
@@ -279,9 +279,10 @@
         const auto& matcherStats = mMatcherStats[key];
         for (const auto& stats : matcherStats) {
             auto output = configStats.add_matcher_stats();
-            output->set_name(stats.first);
+            output->set_id(stats.first);
             output->set_matched_times(stats.second);
-            VLOG("matcher %s matched %d times", stats.first.c_str(), stats.second);
+            VLOG("matcher %lld matched %d times",
+                (long long)stats.first, stats.second);
         }
     }
     // Add condition stats
@@ -289,9 +290,10 @@
         const auto& conditionStats = mConditionStats[key];
         for (const auto& stats : conditionStats) {
             auto output = configStats.add_condition_stats();
-            output->set_name(stats.first);
+            output->set_id(stats.first);
             output->set_max_tuple_counts(stats.second);
-            VLOG("condition %s max output tuple size %d", stats.first.c_str(), stats.second);
+            VLOG("condition %lld max output tuple size %d",
+                (long long)stats.first, stats.second);
         }
     }
     // Add metrics stats
@@ -299,9 +301,10 @@
         const auto& conditionStats = mMetricsStats[key];
         for (const auto& stats : conditionStats) {
             auto output = configStats.add_metric_stats();
-            output->set_name(stats.first);
+            output->set_id(stats.first);
             output->set_max_tuple_counts(stats.second);
-            VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second);
+            VLOG("metrics %lld max output tuple size %d",
+                (long long)stats.first, stats.second);
         }
     }
     // Add anomaly detection alert stats
@@ -309,9 +312,9 @@
         const auto& alertStats = mAlertStats[key];
         for (const auto& stats : alertStats) {
             auto output = configStats.add_alert_stats();
-            output->set_name(stats.first);
+            output->set_id(stats.first);
             output->set_alerted_times(stats.second);
-            VLOG("alert %s declared %d times", stats.first.c_str(), stats.second);
+            VLOG("alert %lld declared %d times", (long long)stats.first, stats.second);
         }
     }
 }
@@ -343,9 +346,9 @@
         // in production.
         if (DEBUG) {
             VLOG("*****ICEBOX*****");
-            VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+            VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
                  "#matcher=%d, #alert=%d,  #valid=%d",
-                 configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
+                 configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
                  configStats.deletion_time_sec(), configStats.metric_count(),
                  configStats.condition_count(), configStats.matcher_count(),
                  configStats.alert_count(), configStats.is_valid());
@@ -364,9 +367,9 @@
         auto& configStats = pair.second;
         if (DEBUG) {
             VLOG("********Active Configs***********");
-            VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+            VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
                  "#matcher=%d, #alert=%d,  #valid=%d",
-                 configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
+                 configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
                  configStats.deletion_time_sec(), configStats.metric_count(),
                  configStats.condition_count(), configStats.matcher_count(),
                  configStats.alert_count(), configStats.is_valid());
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index cb868e1f..45aa192 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -99,10 +99,10 @@
      * count > kDimensionKeySizeSoftLimit.
      *
      * [key]: The config key that this condition belongs to.
-     * [name]: The name of the condition.
+     * [id]: The id of the condition.
      * [size]: The output tuple size.
      */
-    void noteConditionDimensionSize(const ConfigKey& key, const std::string& name, int size);
+    void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size);
 
     /**
      * Report the size of output tuple of a metric.
@@ -111,26 +111,26 @@
      * count > kDimensionKeySizeSoftLimit.
      *
      * [key]: The config key that this metric belongs to.
-     * [name]: The name of the metric.
+     * [id]: The id of the metric.
      * [size]: The output tuple size.
      */
-    void noteMetricDimensionSize(const ConfigKey& key, const std::string& name, int size);
+    void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
 
     /**
      * Report a matcher has been matched.
      *
      * [key]: The config key that this matcher belongs to.
-     * [name]: The name of the matcher.
+     * [id]: The id of the matcher.
      */
-    void noteMatcherMatched(const ConfigKey& key, const std::string& name);
+    void noteMatcherMatched(const ConfigKey& key, const int64_t& id);
 
     /**
      * Report that an anomaly detection alert has been declared.
      *
      * [key]: The config key that this alert belongs to.
-     * [name]: The name of the alert.
+     * [id]: The id of the alert.
      */
-    void noteAnomalyDeclared(const ConfigKey& key, const std::string& name);
+    void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id);
 
     /**
      * Report an atom event has been logged.
@@ -187,12 +187,12 @@
     // Stores the number of output tuple of condition trackers when it's bigger than
     // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
     // it means some data has been dropped.
-    std::map<const ConfigKey, std::map<const std::string, int>> mConditionStats;
+    std::map<const ConfigKey, std::map<const int64_t, int>> mConditionStats;
 
     // Stores the number of output tuple of metric producers when it's bigger than
     // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
     // it means some data has been dropped.
-    std::map<const ConfigKey, std::map<const std::string, int>> mMetricsStats;
+    std::map<const ConfigKey, std::map<const int64_t, int>> mMetricsStats;
 
     // Stores the number of times a pushed atom is logged.
     // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
@@ -206,10 +206,10 @@
 
     // Stores the number of times an anomaly detection alert has been declared
     // (per config, per alert name).
-    std::map<const ConfigKey, std::map<const std::string, int>> mAlertStats;
+    std::map<const ConfigKey, std::map<const int64_t, int>> mAlertStats;
 
     // Stores how many times a matcher have been matched.
-    std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats;
+    std::map<const ConfigKey, std::map<const int64_t, int>> mMatcherStats;
 
     void noteConfigRemovedInternalLocked(const ConfigKey& key);
 
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
index bd5e3cb..15c067e 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -29,8 +29,8 @@
 using std::unordered_map;
 using std::vector;
 
-CombinationLogMatchingTracker::CombinationLogMatchingTracker(const string& name, const int index)
-    : LogMatchingTracker(name, index) {
+CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index)
+    : LogMatchingTracker(id, index) {
 }
 
 CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
@@ -38,7 +38,7 @@
 
 bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
                                          const vector<sp<LogMatchingTracker>>& allTrackers,
-                                         const unordered_map<string, int>& matcherMap,
+                                         const unordered_map<int64_t, int>& matcherMap,
                                          vector<bool>& stack) {
     if (mInitialized) {
         return true;
@@ -60,10 +60,10 @@
         return false;
     }
 
-    for (const string& child : matcher.matcher()) {
+    for (const auto& child : matcher.matcher()) {
         auto pair = matcherMap.find(child);
         if (pair == matcherMap.end()) {
-            ALOGW("Matcher %s not found in the config", child.c_str());
+            ALOGW("Matcher %lld not found in the config", (long long)child);
             return false;
         }
 
@@ -76,7 +76,7 @@
         }
 
         if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) {
-            ALOGW("child matcher init failed %s", child.c_str());
+            ALOGW("child matcher init failed %lld", (long long)child);
             return false;
         }
 
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
index 81f6e80..2a3f08d 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -31,11 +31,11 @@
 // Represents a AtomMatcher_Combination in the StatsdConfig.
 class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
 public:
-    CombinationLogMatchingTracker(const std::string& name, const int index);
+    CombinationLogMatchingTracker(const int64_t& id, const int index);
 
     bool init(const std::vector<AtomMatcher>& allLogMatchers,
               const std::vector<sp<LogMatchingTracker>>& allTrackers,
-              const std::unordered_map<std::string, int>& matcherMap,
+              const std::unordered_map<int64_t, int>& matcherMap,
               std::vector<bool>& stack);
 
     ~CombinationLogMatchingTracker();
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index 567944f..4f30a04 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -33,8 +33,8 @@
 
 class LogMatchingTracker : public virtual RefBase {
 public:
-    LogMatchingTracker(const std::string& name, const int index)
-        : mName(name), mIndex(index), mInitialized(false){};
+    LogMatchingTracker(const int64_t& id, const int index)
+        : mId(id), mIndex(index), mInitialized(false){};
 
     virtual ~LogMatchingTracker(){};
 
@@ -48,7 +48,7 @@
     //        circle dependency.
     virtual bool init(const std::vector<AtomMatcher>& allLogMatchers,
                       const std::vector<sp<LogMatchingTracker>>& allTrackers,
-                      const std::unordered_map<std::string, int>& matcherMap,
+                      const std::unordered_map<int64_t, int>& matcherMap,
                       std::vector<bool>& stack) = 0;
 
     // Called when a log event comes.
@@ -69,13 +69,13 @@
         return mAtomIds;
     }
 
-    const std::string& getName() const {
-        return mName;
+    const int64_t& getId() const {
+        return mId;
     }
 
 protected:
     // Name of this matching. We don't really need the name, but it makes log message easy to debug.
-    const std::string mName;
+    const int64_t mId;
 
     // Index of this LogMatchingTracker in MetricsManager's container.
     const int mIndex;
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
index 91ef034..31b3db5 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -29,10 +29,10 @@
 using std::vector;
 
 
-SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index,
+SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index,
                                                    const SimpleAtomMatcher& matcher,
                                                    const UidMap& uidMap)
-    : LogMatchingTracker(name, index), mMatcher(matcher), mUidMap(uidMap) {
+    : LogMatchingTracker(id, index), mMatcher(matcher), mUidMap(uidMap) {
     if (!matcher.has_atom_id()) {
         mInitialized = false;
     } else {
@@ -46,7 +46,7 @@
 
 bool SimpleLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
                                     const vector<sp<LogMatchingTracker>>& allTrackers,
-                                    const unordered_map<string, int>& matcherMap,
+                                    const unordered_map<int64_t, int>& matcherMap,
                                     vector<bool>& stack) {
     // no need to do anything.
     return mInitialized;
@@ -56,7 +56,7 @@
                                           const vector<sp<LogMatchingTracker>>& allTrackers,
                                           vector<MatchingState>& matcherResults) {
     if (matcherResults[mIndex] != MatchingState::kNotComputed) {
-        VLOG("Matcher %s already evaluated ", mName.c_str());
+        VLOG("Matcher %lld already evaluated ", (long long)mId);
         return;
     }
 
@@ -67,7 +67,7 @@
 
     bool matched = matchesSimple(mUidMap, mMatcher, event);
     matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
-    VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched);
+    VLOG("Stats SimpleLogMatcher %lld matched? %d", (long long)mId, matched);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index 68518f8..28b339c 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -32,7 +32,7 @@
 
 class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
 public:
-    SimpleLogMatchingTracker(const std::string& name, const int index,
+    SimpleLogMatchingTracker(const int64_t& id, const int index,
                              const SimpleAtomMatcher& matcher,
                              const UidMap& uidMap);
 
@@ -40,7 +40,7 @@
 
     bool init(const std::vector<AtomMatcher>& allLogMatchers,
               const std::vector<sp<LogMatchingTracker>>& allTrackers,
-              const std::unordered_map<std::string, int>& matcherMap,
+              const std::unordered_map<int64_t, int>& matcherMap,
               std::vector<bool>& stack) override;
 
     void onLogEvent(const LogEvent& event,
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5bf3cff..3e98098 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -43,7 +43,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_NAME = 1;
+const int FIELD_ID_ID = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_COUNT_METRICS = 5;
@@ -61,10 +61,10 @@
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) {
+    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
     // TODO: evaluate initial conditions. and set mConditionMet.
-    if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
-        mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
+    if (metric.has_bucket()) {
+        mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000;
     } else {
         mBucketSizeNs = LLONG_MAX;
     }
@@ -78,7 +78,7 @@
         mConditionSliced = true;
     }
 
-    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
+    VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -87,12 +87,12 @@
 }
 
 void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
+    VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
 void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
     flushIfNeededLocked(dumpTimeNs);
-    report->set_metric_name(mName);
+    report->set_metric_id(mMetricId);
     report->set_start_report_nanos(mStartTimeNs);
 
     auto count_metrics = report->mutable_count_metrics();
@@ -112,11 +112,11 @@
                                              ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
 
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
 
-    VLOG("metric %s dump report now...", mName.c_str());
+    VLOG("metric %lld dump report now...",(long long)mMetricId);
 
     for (const auto& counter : mPastBuckets) {
         const HashableDimensionKey& hashableKey = counter.first;
@@ -158,7 +158,7 @@
 
 void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mName.c_str());
+    VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
     mCondition = conditionMet;
 }
 
@@ -170,11 +170,11 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("CountMetric %s dropping data for dimension key %s", mName.c_str(),
-                  newKey.c_str());
+            ALOGE("CountMetric %lld dropping data for dimension key %s",
+                (long long)mMetricId, newKey.c_str());
             return true;
         }
     }
@@ -215,7 +215,7 @@
                                          mCurrentSlicedCounter->find(eventKey)->second);
     }
 
-    VLOG("metric %s %s->%lld", mName.c_str(), eventKey.c_str(),
+    VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.c_str(),
          (long long)(*mCurrentSlicedCounter)[eventKey]);
 }
 
@@ -234,8 +234,8 @@
         info.mCount = counter.second;
         auto& bucketList = mPastBuckets[counter.first];
         bucketList.push_back(info);
-        VLOG("metric %s, dump key value: %s -> %lld", mName.c_str(), counter.first.c_str(),
-             (long long)counter.second);
+        VLOG("metric %lld, dump key value: %s -> %lld",
+            (long long)mMetricId, counter.first.c_str(), (long long)counter.second);
     }
 
     for (auto& tracker : mAnomalyTrackers) {
@@ -247,7 +247,7 @@
     uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
     mCurrentBucketNum += numBucketsForward;
-    VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
+    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 30b105c..3f8a8ff 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -42,7 +42,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_NAME = 1;
+const int FIELD_ID_ID = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_DURATION_METRICS = 6;
@@ -63,7 +63,7 @@
                                                const sp<ConditionWizard>& wizard,
                                                const FieldMatcher& internalDimensions,
                                                const uint64_t startTimeNs)
-    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
       mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
@@ -73,8 +73,8 @@
     // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
     // them in the base class, because the proto generated CountMetric, and DurationMetric are
     // not related. Maybe we should add a template in the future??
-    if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
-        mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000000;
+    if (metric.has_bucket()) {
+        mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000;
     } else {
         mBucketSizeNs = LLONG_MAX;
     }
@@ -88,7 +88,7 @@
         mConditionSliced = true;
     }
 
-    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
+    VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -98,9 +98,9 @@
 
 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(const Alert &alert) {
     std::lock_guard<std::mutex> lock(mMutex);
-    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(),
+    if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
+        ALOGW("invalid alert: threshold (%f) > possible recordable value (%d x %lld)",
+              alert.trigger_if_sum_gt(), alert.num_buckets(),
               (long long)mBucketSizeNs);
         return nullptr;
     }
@@ -116,17 +116,17 @@
     switch (mAggregationType) {
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
-                    mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
-                    mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
     }
 }
 
 void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
+    VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
     flushIfNeededLocked(eventTime);
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& pair : mCurrentSlicedDuration) {
@@ -136,7 +136,7 @@
 
 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                       const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mName.c_str());
+    VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
     mCondition = conditionMet;
     flushIfNeededLocked(eventTime);
     // TODO: need to populate the condition change time from the event which triggers the condition
@@ -148,7 +148,7 @@
 
 void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
     flushIfNeededLocked(dumpTimeNs);
-    report->set_metric_name(mName);
+    report->set_metric_id(mMetricId);
     report->set_start_report_nanos(mStartTimeNs);
 
     auto duration_metrics = report->mutable_duration_metrics();
@@ -168,11 +168,11 @@
                                                 ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
 
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
 
-    VLOG("metric %s dump report now...", mName.c_str());
+    VLOG("metric %lld dump report now...", (long long)mMetricId);
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
@@ -237,11 +237,11 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("DurationMetric %s dropping data for dimension key %s", mName.c_str(),
-                  newKey.c_str());
+            ALOGE("DurationMetric %lld dropping data for dimension key %s",
+                (long long)mMetricId, newKey.c_str());
             return true;
         }
     }
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index c8138d3..821d8ea4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -41,7 +41,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_NAME = 1;
+const int FIELD_ID_ID = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_EVENT_METRICS = 4;
@@ -55,7 +55,7 @@
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) {
+    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
@@ -64,7 +64,7 @@
 
     startNewProtoOutputStreamLocked();
 
-    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
+    VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -102,12 +102,13 @@
 
 void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
 
     size_t bufferSize = mProto->size();
-    VLOG("metric %s dump report now... proto size: %zu ", mName.c_str(), bufferSize);
+    VLOG("metric %lld dump report now... proto size: %zu ",
+        (long long)mMetricId, bufferSize);
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto);
 
     protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
@@ -119,7 +120,7 @@
 
 void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mName.c_str());
+    VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
     mCondition = conditionMet;
 }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 51fd9fc..e6f311b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -44,7 +44,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_NAME = 1;
+const int FIELD_ID_ID = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_GAUGE_METRICS = 8;
@@ -60,20 +60,21 @@
 
 GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
                                          const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard, const int atomTagId,
+                                         const sp<ConditionWizard>& wizard,
                                          const int pullTagId, const uint64_t startTimeNs,
                                          shared_ptr<StatsPullerManager> statsPullerManager)
-    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
       mStatsPullerManager(statsPullerManager),
-      mPullTagId(pullTagId),
-      mAtomTagId(atomTagId) {
+      mPullTagId(pullTagId) {
     mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>();
     mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
-    if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
-        mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
+    int64_t bucketSizeMills = 0;
+    if (metric.has_bucket()) {
+        bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket());
     } else {
-        mBucketSizeNs = kDefaultGaugemBucketSizeNs;
+        bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
     }
+    mBucketSizeNs = bucketSizeMills * 1000000;
 
     mFieldFilter = metric.gauge_fields_filter();
 
@@ -88,11 +89,10 @@
 
     // Kicks off the puller immediately.
     if (mPullTagId != -1) {
-        mStatsPullerManager->RegisterReceiver(mPullTagId, this,
-                                              metric.bucket().bucket_size_millis());
+        mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
     }
 
-    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
+    VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -100,8 +100,8 @@
 GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int atomTagId, const int64_t startTimeNs)
-    : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, atomTagId, startTimeNs,
+                                         const int64_t startTimeNs)
+    : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
                           make_shared<StatsPullerManager>()) {
 }
 
@@ -118,11 +118,11 @@
 
 void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
-    VLOG("gauge metric %s dump report now...", mName.c_str());
+    VLOG("gauge metric %lld report now...", (long long)mMetricId);
 
     flushIfNeededLocked(dumpTimeNs);
 
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
 
@@ -166,7 +166,7 @@
 
 void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mName.c_str());
+    VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
     flushIfNeededLocked(eventTime);
     mCondition = conditionMet;
 
@@ -194,7 +194,7 @@
 }
 
 void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
+    VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
 std::shared_ptr<FieldValueMap> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
@@ -223,11 +223,11 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("GaugeMetric %s dropping data for dimension key %s", mName.c_str(),
-                  newKey.c_str());
+            ALOGE("GaugeMetric %lld dropping data for dimension key %s",
+                (long long)mMetricId, newKey.c_str());
             return true;
         }
     }
@@ -314,7 +314,8 @@
         info.mGaugeFields = slice.second;
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
-        VLOG("gauge metric %s, dump key value: %s", mName.c_str(), slice.first.c_str());
+        VLOG("gauge metric %lld, dump key value: %s",
+            (long long)mMetricId, slice.first.c_str());
     }
 
     // Reset counters
@@ -331,7 +332,7 @@
     int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
     mCurrentBucketNum += numBucketsForward;
-    VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
+    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 2a6401d..f267e98 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -50,7 +50,7 @@
 public:
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& countMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int atomTagId, const int64_t startTimeNs);
+                        const int pullTagId, const int64_t startTimeNs);
 
     virtual ~GaugeMetricProducer();
 
@@ -71,7 +71,7 @@
     // for testing
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int atomTagId, const uint64_t startTimeNs,
+                        const int pullTagId, const uint64_t startTimeNs,
                         std::shared_ptr<StatsPullerManager> statsPullerManager);
 
     // Internal interface to handle condition change.
@@ -86,9 +86,6 @@
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& eventTime);
 
-    // The default bucket size for gauge metric is 1 hr.
-    static const uint64_t kDefaultGaugemBucketSizeNs = 60ULL * 60 * 1000 * 1000 * 1000;
-
     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
@@ -106,8 +103,6 @@
     // Translate Atom based bucket to single numeric value bucket for anomaly
     void updateCurrentSlicedBucketForAnomaly();
 
-    int mAtomTagId;
-
     // Whitelist of fields to report. Empty means all are reported.
     FieldFilter mFieldFilter;
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 4ed8289..d620a7e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -29,13 +29,13 @@
     }
 
     bool condition;
-    map<string, std::vector<HashableDimensionKey>> conditionKeys;
+    ConditionKey conditionKey;
     if (mConditionSliced) {
         for (const auto& link : mConditionLinks) {
-            conditionKeys.insert(std::make_pair(link.condition(),
-                                                getDimensionKeysForCondition(event, link)));
+            conditionKey.insert(std::make_pair(link.condition(),
+                                               getDimensionKeysForCondition(event, link)));
         }
-        if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
+        if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) {
             condition = false;
         } else {
             condition = true;
@@ -48,11 +48,11 @@
         vector<DimensionsValue> dimensionValues = getDimensionKeys(event, mDimensions);
         for (const DimensionsValue& dimensionValue : dimensionValues) {
             onMatchedLogEventInternalLocked(
-                matcherIndex, HashableDimensionKey(dimensionValue), conditionKeys, condition, event);
+                matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event);
         }
     } else {
         onMatchedLogEventInternalLocked(
-            matcherIndex, DEFAULT_DIMENSION_KEY, conditionKeys, condition, event);
+            matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index fe1a53b..3779c44 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -39,9 +39,9 @@
 // be a no-op.
 class MetricProducer : public virtual PackageInfoListener {
 public:
-    MetricProducer(const std::string& name, const ConfigKey& key, const int64_t startTimeNs,
+    MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t startTimeNs,
                    const int conditionIndex, const sp<ConditionWizard>& wizard)
-        : mName(name),
+        : mMetricId(metricId),
           mConfigKey(key),
           mStartTimeNs(startTimeNs),
           mCurrentBucketStartTimeNs(startTimeNs),
@@ -117,8 +117,8 @@
         return mBucketSizeNs;
     }
 
-    inline const string& getName() {
-        return mName;
+    inline const int64_t& getMetricId() {
+        return mMetricId;
     }
 
 protected:
@@ -129,7 +129,7 @@
     virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0;
     virtual size_t byteSizeLocked() const = 0;
 
-    const std::string mName;
+    const int64_t mMetricId;
 
     const ConfigKey mConfigKey;
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 9749e0f..f929517 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -51,9 +51,9 @@
     mConfigValid =
             initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
                              mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
-                             mTrackerToMetricMap, mTrackerToConditionMap);
+                             mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds);
 
-    if (!config.has_log_source()) {
+    if (config.allowed_log_source_size() == 0) {
         // TODO(b/70794411): uncomment the following line and remove the hard coded log source
         // after all configs have the log source added.
         // mConfigValid = false;
@@ -63,10 +63,14 @@
         mAllowedUid.push_back(0);
         mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
     } else {
-        mAllowedUid.insert(mAllowedUid.begin(), config.log_source().uid().begin(),
-                           config.log_source().uid().end());
-        mAllowedPkg.insert(mAllowedPkg.begin(), config.log_source().package().begin(),
-                           config.log_source().package().end());
+        for (const auto& source : config.allowed_log_source()) {
+            auto it = UidMap::sAidToUidMapping.find(source);
+            if (it != UidMap::sAidToUidMapping.end()) {
+                mAllowedUid.push_back(it->second);
+            } else {
+                mAllowedPkg.push_back(source);
+            }
+        }
 
         if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
             ALOGE("Too many log sources. This is likely to be an error in the config.");
@@ -143,8 +147,10 @@
 }
 
 void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) {
-    for (auto& producer : mAllMetricProducers) {
-        producer->onDumpReport(dumpTimeStampNs, report->add_metrics());
+    for (const auto& producer : mAllMetricProducers) {
+        if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
+            producer->onDumpReport(dumpTimeStampNs, report->add_metrics());
+        }
     }
 }
 
@@ -152,11 +158,13 @@
     VLOG("=========================Metric Reports Start==========================");
     uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC;
     // one StatsLogReport per MetricProduer
-    for (auto& metric : mAllMetricProducers) {
-        long long token =
-                protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
-        metric->onDumpReport(dumpTimeStampNs, protoOutput);
-        protoOutput->end(token);
+    for (const auto& producer : mAllMetricProducers) {
+        if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
+            long long token =
+                    protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
+            producer->onDumpReport(dumpTimeStampNs, protoOutput);
+            protoOutput->end(token);
+        }
     }
     VLOG("=========================Metric Reports End==========================");
 }
@@ -173,6 +181,22 @@
             VLOG("log source %d not on the whitelist", event.GetUid());
             return;
         }
+    } else { // Check that app hook fields are valid.
+        // TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
+
+        // Label is 2nd from last field and must be from [0, 15].
+        status_t err = NO_ERROR;
+        long label = event.GetLong(event.size()-1, &err);
+        if (err != NO_ERROR || label < 0 || label > 15) {
+            VLOG("App hook does not have valid label %ld", label);
+            return;
+        }
+        // The state must be from 0,3. This part of code must be manually updated.
+        long apphookState = event.GetLong(event.size(), &err);
+        if (err != NO_ERROR || apphookState < 0 || apphookState > 3) {
+            VLOG("App hook does not have valid state %ld", apphookState);
+            return;
+        }
     }
 
     int tagId = event.GetTagId();
@@ -240,7 +264,7 @@
     for (size_t i = 0; i < mAllAtomMatchers.size(); i++) {
         if (matcherCache[i] == MatchingState::kMatched) {
             StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
-                                                          mAllAtomMatchers[i]->getName());
+                                                          mAllAtomMatchers[i]->getId());
             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 51c4c90..a03047f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -57,6 +57,10 @@
 
     void onUidMapReceived() override;
 
+    bool shouldAddUidMapListener() const {
+        return !mAllowedPkg.empty();
+    }
+
     // Config source owner can call onDumpReport() to get all the metrics collected.
     virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
     virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report);
@@ -131,6 +135,9 @@
 
     void initLogSourceWhiteList();
 
+    // The metrics that don't need to be uploaded or even reported.
+    std::set<int64_t> mNoReportMetricIds;
+
     FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
 };
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index b58dc68..74bd6f9 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -17,6 +17,7 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
+#include "dimension.h"
 #include "ValueMetricProducer.h"
 #include "guardrail/StatsdStats.h"
 #include "stats_log_util.h"
@@ -46,7 +47,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_NAME = 1;
+const int FIELD_ID_ID = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_VALUE_METRICS = 7;
@@ -60,25 +61,25 @@
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_VALUE = 3;
 
-static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
-
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const uint64_t startTimeNs,
                                          shared_ptr<StatsPullerManager> statsPullerManager)
-    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
       mValueField(metric.value_field()),
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId) {
     // TODO: valuemetric for pushed events may need unlimited bucket length
-    if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
-        mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
+    int64_t bucketSizeMills = 0;
+    if (metric.has_bucket()) {
+        bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket());
     } else {
-        mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
+        bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
     }
 
+    mBucketSizeNs = bucketSizeMills * 1000000;
     mDimensions = metric.dimensions();
 
     if (metric.links().size() > 0) {
@@ -89,11 +90,10 @@
 
     if (!metric.has_condition() && mPullTagId != -1) {
         VLOG("Setting up periodic pulling for %d", mPullTagId);
-        mStatsPullerManager->RegisterReceiver(mPullTagId, this,
-                                              metric.bucket().bucket_size_millis());
+        mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
     }
-    VLOG("value metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
-         (long long)mBucketSizeNs, (long long)mStartTimeNs);
+    VLOG("value metric %lld created. bucket size %lld start_time: %lld",
+        (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
 // for testing
@@ -113,12 +113,12 @@
 }
 
 void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
+    VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
 void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
     flushIfNeededLocked(dumpTimeNs);
-    report->set_metric_name(mName);
+    report->set_metric_id(mMetricId);
     report->set_start_report_nanos(mStartTimeNs);
     auto value_metrics = report->mutable_value_metrics();
     for (const auto& pair : mPastBuckets) {
@@ -135,9 +135,9 @@
 
 void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
-    VLOG("metric %s dump report now...", mName.c_str());
+    VLOG("metric %lld dump report now...", (long long)mMetricId);
     flushIfNeededLocked(dumpTimeNs);
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
 
@@ -171,7 +171,7 @@
     protoOutput->end(protoToken);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
 
-    VLOG("metric %s dump report now...", mName.c_str());
+    VLOG("metric %lld dump report now...", (long long)mMetricId);
     mPastBuckets.clear();
     mStartTimeNs = mCurrentBucketStartTimeNs;
     // TODO: Clear mDimensionKeyMap once the report is dumped.
@@ -218,7 +218,8 @@
         // For scheduled pulled data, the effective event time is snap to the nearest
         // bucket boundary to make bucket finalize.
         uint64_t realEventTime = allData.at(0)->GetTimestampNs();
-        uint64_t eventTime = mStartTimeNs + ((realEventTime - mStartTimeNs)/mBucketSizeNs) * mBucketSizeNs;
+        uint64_t eventTime = mStartTimeNs +
+            ((realEventTime - mStartTimeNs)/mBucketSizeNs) * mBucketSizeNs;
 
         mCondition = false;
         for (const auto& data : allData) {
@@ -242,11 +243,11 @@
     }
     if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("ValueMetric %s dropping data for dimension key %s", mName.c_str(),
-                  newKey.c_str());
+            ALOGE("ValueMetric %lld dropping data for dimension key %s",
+                (long long)mMetricId, newKey.c_str());
             return true;
         }
     }
@@ -272,7 +273,11 @@
     }
     Interval& interval = mCurrentSlicedBucket[eventKey];
 
-    long value = get_value(event);
+    std::shared_ptr<FieldValueMap> valueFieldMap = getValueFields(event);
+    if (valueFieldMap->empty() || valueFieldMap->size() > 1) {
+        return;
+    }
+    const long value = getLongFromDimenValue(valueFieldMap->begin()->second);
 
     if (mPullTagId != -1) { // for pulled events
         if (mCondition == true) {
@@ -296,15 +301,11 @@
     }
 }
 
-long ValueMetricProducer::get_value(const LogEvent& event) {
-    status_t err = NO_ERROR;
-    long val = event.GetLong(mValueField, &err);
-    if (err == NO_ERROR) {
-        return val;
-    } else {
-        VLOG("Can't find value in message. %s", event.ToString().c_str());
-        return 0;
-    }
+std::shared_ptr<FieldValueMap> ValueMetricProducer::getValueFields(const LogEvent& event) {
+    std::shared_ptr<FieldValueMap> valueFields =
+        std::make_shared<FieldValueMap>(event.getFieldValueMap());
+    filterFields(mValueField, valueFields.get());
+    return valueFields;
 }
 
 void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
@@ -346,7 +347,7 @@
     if (numBucketsForward > 1) {
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
     }
-    VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
+    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 6f3b1d1..3e7032d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -70,7 +70,7 @@
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& eventTime);
 
-    const int32_t mValueField;
+    const FieldMatcher mValueField;
 
     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
 
@@ -103,7 +103,7 @@
     // TODO: Add a lock to mPastBuckets.
     std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
 
-    long get_value(const LogEvent& event);
+    std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event);
 
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 9192d12f..842581e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -60,12 +60,12 @@
 
 class DurationTracker {
 public:
-    DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey,
+    DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
                     sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                     uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
         : mConfigKey(key),
-          mName(name),
+          mTrackerId(id),
           mEventKey(eventKey),
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
@@ -145,7 +145,7 @@
     // A reference to the DurationMetricProducer's config key.
     const ConfigKey& mConfigKey;
 
-    const std::string mName;
+    const int64_t mTrackerId;
 
     HashableDimensionKey mEventKey;
 
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index d8a8e23..94f98ad 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -24,12 +24,12 @@
 namespace os {
 namespace statsd {
 
-MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name,
+MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
                                        const HashableDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                                        const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-    : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
                       bucketSizeNs, anomalyTrackers) {
 }
 
@@ -42,12 +42,13 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mInfos.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey.toString(),
-                                                           newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(
+            mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+            newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("MaxDurTracker %s dropping data for dimension key %s", mName.c_str(),
-                  newKey.c_str());
+            ALOGE("MaxDurTracker %lld dropping data for dimension key %s",
+                (long long)mTrackerId, newKey.c_str());
             return true;
         }
     }
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 76f486e..68c48cb1 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,7 +28,7 @@
 // they stop or bucket expires.
 class MaxDurationTracker : public DurationTracker {
 public:
-    MaxDurationTracker(const ConfigKey& key, const string& name,
+    MaxDurationTracker(const ConfigKey& key, const int64_t& id,
                        const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                        int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                        uint64_t bucketSizeNs,
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index c347d5c..c77d0b7 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -25,11 +25,11 @@
 using std::pair;
 
 OringDurationTracker::OringDurationTracker(
-        const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey,
+        const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
         sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
         uint64_t bucketSizeNs, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
 
-    : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
                       bucketSizeNs, anomalyTrackers),
       mStarted(),
       mPaused() {
@@ -44,12 +44,13 @@
     }
     if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mConditionKeyMap.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey.toString(),
-                                                           newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(
+            mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+            newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("OringDurTracker %s dropping data for dimension key %s", mName.c_str(),
-                  newKey.c_str());
+            ALOGE("OringDurTracker %lld dropping data for dimension key %s",
+                (long long)mTrackerId, newKey.c_str());
             return true;
         }
     }
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index dcf04db..7fe649c 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,7 +27,7 @@
 // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
 class OringDurationTracker : public DurationTracker {
 public:
-    OringDurationTracker(const ConfigKey& key, const string& name,
+    OringDurationTracker(const ConfigKey& key, const int64_t& id,
                          const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                          int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                          uint64_t bucketSizeNs,
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 97dbf7a..bc887ac 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -38,21 +38,21 @@
 namespace os {
 namespace statsd {
 
-bool handleMetricWithLogTrackers(const string what, const int metricIndex,
+bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex,
                                  const bool usedForDimension,
                                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
-                                 const unordered_map<string, int>& logTrackerMap,
+                                 const unordered_map<int64_t, int>& logTrackerMap,
                                  unordered_map<int, std::vector<int>>& trackerToMetricMap,
                                  int& logTrackerIndex) {
     auto logTrackerIt = logTrackerMap.find(what);
     if (logTrackerIt == logTrackerMap.end()) {
-        ALOGW("cannot find the AtomMatcher \"%s\" in config", what.c_str());
+        ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what);
         return false;
     }
     if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
-        ALOGE("AtomMatcher \"%s\" has more than one tag ids. When a metric has dimension, "
+        ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
               "the \"what\" can only about one atom type.",
-              what.c_str());
+              (long long)what);
         return false;
     }
     logTrackerIndex = logTrackerIt->second;
@@ -62,22 +62,22 @@
 }
 
 bool handleMetricWithConditions(
-        const string condition, const int metricIndex,
-        const unordered_map<string, int>& conditionTrackerMap,
+        const int64_t condition, const int metricIndex,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
         const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
                 links,
         vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
         unordered_map<int, std::vector<int>>& conditionToMetricMap) {
     auto condition_it = conditionTrackerMap.find(condition);
     if (condition_it == conditionTrackerMap.end()) {
-        ALOGW("cannot find Predicate \"%s\" in the config", condition.c_str());
+        ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
         return false;
     }
 
     for (const auto& link : links) {
         auto it = conditionTrackerMap.find(link.condition());
         if (it == conditionTrackerMap.end()) {
-            ALOGW("cannot find Predicate \"%s\" in the config", link.condition().c_str());
+            ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition());
             return false;
         }
         allConditionTrackers[condition_it->second]->setSliced(true);
@@ -93,7 +93,7 @@
 }
 
 bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
-                     unordered_map<string, int>& logTrackerMap,
+                     unordered_map<int64_t, int>& logTrackerMap,
                      vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
     vector<AtomMatcher> matcherConfigs;
     const int atomMatcherCount = config.atom_matcher_size();
@@ -107,22 +107,22 @@
         switch (logMatcher.contents_case()) {
             case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
                 allAtomMatchers.push_back(new SimpleLogMatchingTracker(
-                        logMatcher.name(), index, logMatcher.simple_atom_matcher(), uidMap));
+                        logMatcher.id(), index, logMatcher.simple_atom_matcher(), uidMap));
                 break;
             case AtomMatcher::ContentsCase::kCombination:
                 allAtomMatchers.push_back(
-                        new CombinationLogMatchingTracker(logMatcher.name(), index));
+                        new CombinationLogMatchingTracker(logMatcher.id(), index));
                 break;
             default:
-                ALOGE("Matcher \"%s\" malformed", logMatcher.name().c_str());
+                ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
                 return false;
                 // continue;
         }
-        if (logTrackerMap.find(logMatcher.name()) != logTrackerMap.end()) {
+        if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) {
             ALOGE("Duplicate AtomMatcher found!");
             return false;
         }
-        logTrackerMap[logMatcher.name()] = index;
+        logTrackerMap[logMatcher.id()] = index;
         matcherConfigs.push_back(logMatcher);
     }
 
@@ -139,8 +139,8 @@
 }
 
 bool initConditions(const ConfigKey& key, const StatsdConfig& config,
-                    const unordered_map<string, int>& logTrackerMap,
-                    unordered_map<string, int>& conditionTrackerMap,
+                    const unordered_map<int64_t, int>& logTrackerMap,
+                    unordered_map<int64_t, int>& conditionTrackerMap,
                     vector<sp<ConditionTracker>>& allConditionTrackers,
                     unordered_map<int, std::vector<int>>& trackerToConditionMap) {
     vector<Predicate> conditionConfigs;
@@ -154,23 +154,23 @@
         switch (condition.contents_case()) {
             case Predicate::ContentsCase::kSimplePredicate: {
                 allConditionTrackers.push_back(new SimpleConditionTracker(
-                        key, condition.name(), index, condition.simple_predicate(), logTrackerMap));
+                        key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
                 break;
             }
             case Predicate::ContentsCase::kCombination: {
                 allConditionTrackers.push_back(
-                        new CombinationConditionTracker(condition.name(), index));
+                        new CombinationConditionTracker(condition.id(), index));
                 break;
             }
             default:
-                ALOGE("Predicate \"%s\" malformed", condition.name().c_str());
+                ALOGE("Predicate \"%lld\" malformed", (long long)condition.id());
                 return false;
         }
-        if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) {
+        if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
             ALOGE("Duplicate Predicate found!");
             return false;
         }
-        conditionTrackerMap[condition.name()] = index;
+        conditionTrackerMap[condition.id()] = index;
         conditionConfigs.push_back(condition);
     }
 
@@ -190,14 +190,15 @@
 }
 
 bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
-                 const unordered_map<string, int>& logTrackerMap,
-                 const unordered_map<string, int>& conditionTrackerMap,
+                 const unordered_map<int64_t, int>& logTrackerMap,
+                 const unordered_map<int64_t, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
                  vector<sp<MetricProducer>>& allMetricProducers,
                  unordered_map<int, std::vector<int>>& conditionToMetricMap,
                  unordered_map<int, std::vector<int>>& trackerToMetricMap,
-                 unordered_map<string, int>& metricMap) {
+                 unordered_map<int64_t, int>& metricMap,
+                 std::set<int64_t> &noReportMetricIds) {
     sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
     const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
                                 config.event_metric_size() + config.value_metric_size();
@@ -219,12 +220,12 @@
     for (int i = 0; i < config.count_metric_size(); i++) {
         const CountMetric& metric = config.count_metric(i);
         if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in CountMetric \"%s\"", metric.name().c_str());
+            ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id());
             return false;
         }
 
         int metricIndex = allMetricProducers.size();
-        metricMap.insert({metric.name(), metricIndex});
+        metricMap.insert({metric.id(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
                                          allAtomMatchers, logTrackerMap, trackerToMetricMap,
@@ -256,7 +257,7 @@
     for (int i = 0; i < config.duration_metric_size(); i++) {
         int metricIndex = allMetricProducers.size();
         const DurationMetric& metric = config.duration_metric(i);
-        metricMap.insert({metric.name(), metricIndex});
+        metricMap.insert({metric.id(), metricIndex});
 
         auto what_it = conditionTrackerMap.find(metric.what());
         if (what_it == conditionTrackerMap.end()) {
@@ -327,8 +328,8 @@
     for (int i = 0; i < config.event_metric_size(); i++) {
         int metricIndex = allMetricProducers.size();
         const EventMetric& metric = config.event_metric(i);
-        metricMap.insert({metric.name(), metricIndex});
-        if (!metric.has_name() || !metric.has_what()) {
+        metricMap.insert({metric.id(), metricIndex});
+        if (!metric.has_id() || !metric.has_what()) {
             ALOGW("cannot find the metric name or what in config");
             return false;
         }
@@ -363,12 +364,12 @@
     for (int i = 0; i < config.value_metric_size(); i++) {
         const ValueMetric& metric = config.value_metric(i);
         if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in ValueMetric \"%s\"", metric.name().c_str());
+            ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
             return false;
         }
 
         int metricIndex = allMetricProducers.size();
-        metricMap.insert({metric.name(), metricIndex});
+        metricMap.insert({metric.id(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
                                          allAtomMatchers, logTrackerMap, trackerToMetricMap,
@@ -408,25 +409,25 @@
     for (int i = 0; i < config.gauge_metric_size(); i++) {
         const GaugeMetric& metric = config.gauge_metric(i);
         if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in GaugeMetric \"%s\"", metric.name().c_str());
+            ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
             return false;
         }
 
         if ((!metric.gauge_fields_filter().has_include_all() ||
              (metric.gauge_fields_filter().include_all() == false)) &&
             !hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
+            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
             return false;
         }
         if ((metric.gauge_fields_filter().has_include_all() &&
              metric.gauge_fields_filter().include_all() == true) &&
             hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
+            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
             return false;
         }
 
         int metricIndex = allMetricProducers.size();
-        metricMap.insert({metric.name(), metricIndex});
+        metricMap.insert({metric.id(), metricIndex});
         int trackerIndex;
         if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
                                          allAtomMatchers, logTrackerMap, trackerToMetricMap,
@@ -458,52 +459,80 @@
         }
 
         sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, wizard, pullTagId, atomTagId, startTimeNs);
+                key, metric, conditionIndex, wizard, pullTagId, startTimeNs);
         allMetricProducers.push_back(gaugeProducer);
     }
+    for (int i = 0; i < config.no_report_metric_size(); ++i) {
+        const auto no_report_metric = config.no_report_metric(i);
+        if (metricMap.find(no_report_metric) == metricMap.end()) {
+            ALOGW("no_report_metric %lld not exist", no_report_metric);
+            return false;
+        }
+        noReportMetricIds.insert(no_report_metric);
+    }
     return true;
 }
 
 bool initAlerts(const StatsdConfig& config,
-                const unordered_map<string, int>& metricProducerMap,
+                const unordered_map<int64_t, int>& metricProducerMap,
                 vector<sp<MetricProducer>>& allMetricProducers,
                 vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
+    unordered_map<int64_t, int> anomalyTrackerMap;
     for (int i = 0; i < config.alert_size(); i++) {
         const Alert& alert = config.alert(i);
-        const auto& itr = metricProducerMap.find(alert.metric_name());
+        const auto& itr = metricProducerMap.find(alert.metric_id());
         if (itr == metricProducerMap.end()) {
-            ALOGW("alert \"%s\" has unknown metric name: \"%s\"", alert.name().c_str(),
-                  alert.metric_name().c_str());
+            ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
+                  (long long)alert.metric_id());
             return false;
         }
-        if (alert.trigger_if_sum_gt() < 0 || alert.number_of_buckets() <= 0) {
-            ALOGW("invalid alert: threshold=%lld num_buckets= %d",
-                  alert.trigger_if_sum_gt(), alert.number_of_buckets());
+        if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
+            ALOGW("invalid alert: threshold=%f num_buckets= %d",
+                  alert.trigger_if_sum_gt(), alert.num_buckets());
             return false;
         }
         const int metricIndex = itr->second;
         sp<MetricProducer> metric = allMetricProducers[metricIndex];
         sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert);
         if (anomalyTracker != nullptr) {
+            anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
             allAnomalyTrackers.push_back(anomalyTracker);
         }
     }
+    for (int i = 0; i < config.subscription_size(); ++i) {
+        const Subscription& subscription = config.subscription(i);
+        if (subscription.subscriber_information_case() ==
+            Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
+            ALOGW("subscription \"%lld\" has no subscriber info.\"",
+                (long long)subscription.id());
+            return false;
+        }
+        const auto& itr = anomalyTrackerMap.find(subscription.rule_id());
+        if (itr == anomalyTrackerMap.end()) {
+            ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
+                (long long)subscription.id(), (long long)subscription.rule_id());
+            return false;
+        }
+        const int anomalyTrackerIndex = itr->second;
+        allAnomalyTrackers[anomalyTrackerIndex]->addSubscription(subscription);
+    }
     return true;
 }
 
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
-                const UidMap& uidMap,
-                        const long timeBaseSec, set<int>& allTagIds,
+                      const UidMap& uidMap,
+                      const long timeBaseSec, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
                       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) {
-    unordered_map<string, int> logTrackerMap;
-    unordered_map<string, int> conditionTrackerMap;
-    unordered_map<string, int> metricProducerMap;
+                      unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      std::set<int64_t> &noReportMetricIds) {
+    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    unordered_map<int64_t, int> metricProducerMap;
 
     if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
         ALOGE("initLogMatchingTrackers failed");
@@ -519,7 +548,7 @@
 
     if (!initMetrics(key, config, timeBaseSec, logTrackerMap, conditionTrackerMap, allAtomMatchers,
                      allConditionTrackers, allMetricProducers, conditionToMetricMap,
-                     trackerToMetricMap, metricProducerMap)) {
+                     trackerToMetricMap, metricProducerMap, noReportMetricIds)) {
         ALOGE("initMetricProducers failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 9ad5176..4f19ada 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -43,8 +43,8 @@
 // [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,
-                const UidMap& uidMap,
-                     std::unordered_map<std::string, int>& logTrackerMap,
+                     const UidMap& uidMap,
+                     std::unordered_map<int64_t, int>& logTrackerMap,
                      std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                      std::set<int>& allTagIds);
 
@@ -59,8 +59,8 @@
 // [trackerToConditionMap]: contain the mapping from index of
 //                        log tracker to condition trackers that use the log tracker
 bool initConditions(const ConfigKey& key, const StatsdConfig& config,
-                    const std::unordered_map<std::string, int>& logTrackerMap,
-                    std::unordered_map<std::string, int>& conditionTrackerMap,
+                    const std::unordered_map<int64_t, int>& logTrackerMap,
+                    std::unordered_map<int64_t, int>& conditionTrackerMap,
                     std::vector<sp<ConditionTracker>>& allConditionTrackers,
                     std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
                     std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
@@ -79,28 +79,29 @@
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 bool initMetrics(
         const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
-        const std::unordered_map<std::string, int>& logTrackerMap,
-        const std::unordered_map<std::string, int>& conditionTrackerMap,
+        const std::unordered_map<int64_t, int>& logTrackerMap,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
         const vector<sp<LogMatchingTracker>>& allAtomMatchers,
         vector<sp<ConditionTracker>>& allConditionTrackers,
         std::vector<sp<MetricProducer>>& allMetricProducers,
         std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
-        std::unordered_map<int, std::vector<int>>& trackerToMetricMap);
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::set<int64_t> &noReportMetricIds);
 
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
-
-                const UidMap& uidMap,
-                const long timeBaseSec, std::set<int>& allTagIds,
+                      const UidMap& uidMap,
+                      const long timeBaseSec, std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
                       std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
-                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      std::set<int64_t> &noReportMetricIds);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 416b87b..517d21d 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -402,6 +402,79 @@
     return results;
 }
 
+// Note not all the following AIDs are used as uids. Some are used only for gids.
+// It's ok to leave them in the map, but we won't ever see them in the log's uid field.
+// App's uid starts from 10000, and will not overlap with the following AIDs.
+const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0},
+                                                             {"AID_SYSTEM", 1000},
+                                                             {"AID_RADIO", 1001},
+                                                             {"AID_BLUETOOTH", 1002},
+                                                             {"AID_GRAPHICS", 1003},
+                                                             {"AID_INPUT", 1004},
+                                                             {"AID_AUDIO", 1005},
+                                                             {"AID_CAMERA", 1006},
+                                                             {"AID_LOG", 1007},
+                                                             {"AID_COMPASS", 1008},
+                                                             {"AID_MOUNT", 1009},
+                                                             {"AID_WIFI", 1010},
+                                                             {"AID_ADB", 1011},
+                                                             {"AID_INSTALL", 1012},
+                                                             {"AID_MEDIA", 1013},
+                                                             {"AID_DHCP", 1014},
+                                                             {"AID_SDCARD_RW", 1015},
+                                                             {"AID_VPN", 1016},
+                                                             {"AID_KEYSTORE", 1017},
+                                                             {"AID_USB", 1018},
+                                                             {"AID_DRM", 1019},
+                                                             {"AID_MDNSR", 1020},
+                                                             {"AID_GPS", 1021},
+                                                             // {"AID_UNUSED1", 1022},
+                                                             {"AID_MEDIA_RW", 1023},
+                                                             {"AID_MTP", 1024},
+                                                             // {"AID_UNUSED2", 1025},
+                                                             {"AID_DRMRPC", 1026},
+                                                             {"AID_NFC", 1027},
+                                                             {"AID_SDCARD_R", 1028},
+                                                             {"AID_CLAT", 1029},
+                                                             {"AID_LOOP_RADIO", 1030},
+                                                             {"AID_MEDIA_DRM", 1031},
+                                                             {"AID_PACKAGE_INFO", 1032},
+                                                             {"AID_SDCARD_PICS", 1033},
+                                                             {"AID_SDCARD_AV", 1034},
+                                                             {"AID_SDCARD_ALL", 1035},
+                                                             {"AID_LOGD", 1036},
+                                                             {"AID_SHARED_RELRO", 1037},
+                                                             {"AID_DBUS", 1038},
+                                                             {"AID_TLSDATE", 1039},
+                                                             {"AID_MEDIA_EX", 1040},
+                                                             {"AID_AUDIOSERVER", 1041},
+                                                             {"AID_METRICS_COLL", 1042},
+                                                             {"AID_METRICSD", 1043},
+                                                             {"AID_WEBSERV", 1044},
+                                                             {"AID_DEBUGGERD", 1045},
+                                                             {"AID_MEDIA_CODEC", 1046},
+                                                             {"AID_CAMERASERVER", 1047},
+                                                             {"AID_FIREWALL", 1048},
+                                                             {"AID_TRUNKS", 1049},
+                                                             {"AID_NVRAM", 1050},
+                                                             {"AID_DNS", 1051},
+                                                             {"AID_DNS_TETHER", 1052},
+                                                             {"AID_WEBVIEW_ZYGOTE", 1053},
+                                                             {"AID_VEHICLE_NETWORK", 1054},
+                                                             {"AID_MEDIA_AUDIO", 1055},
+                                                             {"AID_MEDIA_VIDEO", 1056},
+                                                             {"AID_MEDIA_IMAGE", 1057},
+                                                             {"AID_TOMBSTONED", 1058},
+                                                             {"AID_MEDIA_OBB", 1059},
+                                                             {"AID_ESE", 1060},
+                                                             {"AID_OTA_UPDATE", 1061},
+                                                             {"AID_AUTOMOTIVE_EVS", 1062},
+                                                             {"AID_LOWPAN", 1063},
+                                                             {"AID_HSM", 1064},
+                                                             {"AID_SHELL", 2000},
+                                                             {"AID_CACHE", 2001},
+                                                             {"AID_DIAG", 2002}};
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 02dea54..07e13e0 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -51,7 +51,7 @@
 public:
     UidMap();
     ~UidMap();
-
+    static const std::map<std::string, uint32_t> sAidToUidMapping;
     /*
      * 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].
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index f5282ea..ae69a50 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -24,12 +24,6 @@
 
 import "frameworks/base/cmds/statsd/src/atoms.proto";
 
-message Field {
-  optional int32 field = 1;
-  optional int32 position_index = 2 [default = -1];
-  repeated Field child = 3;
-}
-
 message DimensionsValue {
   optional int32 field = 1;
 
@@ -137,7 +131,7 @@
 }
 
 message StatsLogReport {
-  optional string metric_name = 1;
+  optional int64 metric_id = 1;
 
   optional int64 start_report_nanos = 2;
 
@@ -178,7 +172,7 @@
 message ConfigMetricsReportList {
   message ConfigKey {
     optional int32 uid = 1;
-    optional string name = 2;
+    optional int64 id = 2;
   }
   optional ConfigKey config_key = 1;
 
@@ -191,28 +185,28 @@
     optional int32 stats_end_time_sec = 2;
 
     message MatcherStats {
-        optional string name = 1;
+        optional int64 id = 1;
         optional int32 matched_times = 2;
     }
 
     message ConditionStats {
-        optional string name = 1;
+        optional int64 id = 1;
         optional int32 max_tuple_counts = 2;
     }
 
     message MetricStats {
-        optional string name = 1;
+        optional int64 id = 1;
         optional int32 max_tuple_counts = 2;
     }
 
     message AlertStats {
-        optional string name = 1;
+        optional int64 id = 1;
         optional int32 alerted_times = 2;
     }
 
     message ConfigStats {
         optional int32 uid = 1;
-        optional string name = 2;
+        optional int64 id = 2;
         optional int32 creation_time_sec = 3;
         optional int32 deletion_time_sec = 4;
         optional int32 metric_count = 5;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 476e117..b335b58 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -218,8 +218,34 @@
         protoOutput->end(tokenStack.top().first);
         tokenStack.pop();
     }
+}
 
-
+int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
+    switch (unit) {
+        case ONE_MINUTE:
+            return 60 * 1000LL;
+        case FIVE_MINUTES:
+            return 5 * 60 * 1000LL;
+        case TEN_MINUTES:
+            return 10 * 60 * 1000LL;
+        case THIRTY_MINUTES:
+            return 30 * 60 * 1000LL;
+        case ONE_HOUR:
+            return 60 * 60 * 1000LL;
+        case THREE_HOURS:
+            return 3 * 60 * 60 * 1000LL;
+        case SIX_HOURS:
+            return 6 * 60 * 60 * 1000LL;
+        case TWELVE_HOURS:
+            return 12 * 60 * 60 * 1000LL;
+        case ONE_DAY:
+            return 24 * 60 * 60 * 1000LL;
+        case CTS:
+            return 1000;
+        case TIME_UNIT_UNSPECIFIED:
+        default:
+            return -1;
+    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 1f81860..33303dc 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/util/ProtoOutputStream.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "field_util.h"
 
@@ -36,6 +37,9 @@
 void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap,
     util::ProtoOutputStream* protoOutput);
 
+// Convert the TimeUnit enum to the bucket size in millis.
+int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index e46b8eb..160b1f4 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -32,7 +32,7 @@
 // Minimum bucket size in seconds
 const long kMinBucketSizeSec = 5 * 60;
 
-typedef std::map<std::string, std::vector<HashableDimensionKey>> ConditionKey;
+typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey;
 
 typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
 
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 1ed1e05..d45a6b0 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -29,6 +29,20 @@
     ANY = 3;
 }
 
+enum TimeUnit {
+    TIME_UNIT_UNSPECIFIED = 0;
+    ONE_MINUTE = 1;
+    FIVE_MINUTES = 2;
+    TEN_MINUTES = 3;
+    THIRTY_MINUTES = 4;
+    ONE_HOUR = 5;
+    THREE_HOURS = 6;
+    SIX_HOURS = 7;
+    TWELVE_HOURS = 8;
+    ONE_DAY = 9;
+    CTS = 1000;
+}
+
 message FieldMatcher {
     optional int32 field = 1;
 
@@ -84,12 +98,12 @@
 }
 
 message AtomMatcher {
-    optional string name = 1;
+    optional int64 id = 1;
 
     message Combination {
         optional LogicalOperation operation = 1;
 
-        repeated string matcher = 2;
+        repeated int64 matcher = 2;
     }
     oneof contents {
         SimpleAtomMatcher simple_atom_matcher = 2;
@@ -98,13 +112,13 @@
 }
 
 message SimplePredicate {
-    optional string start = 1;
+    optional int64 start = 1;
 
-    optional string stop = 2;
+    optional int64 stop = 2;
 
     optional bool count_nesting = 3 [default = true];
 
-    optional string stop_all = 4;
+    optional int64 stop_all = 4;
 
     enum InitialValue {
         UNKNOWN = 0;
@@ -116,12 +130,12 @@
 }
 
 message Predicate {
-    optional string name = 1;
+    optional int64 id = 1;
 
     message Combination {
         optional LogicalOperation operation = 1;
 
-        repeated string predicate = 2;
+        repeated int64 predicate = 2;
     }
 
     oneof contents {
@@ -135,7 +149,7 @@
 }
 
 message MetricConditionLink {
-    optional string condition = 1;
+    optional int64 condition = 1;
 
     optional FieldMatcher dimensions_in_what = 2;
 
@@ -148,35 +162,35 @@
 }
 
 message EventMetric {
-    optional string name = 1;
+    optional int64 id = 1;
 
-    optional string what = 2;
+    optional int64 what = 2;
 
-    optional string condition = 3;
+    optional int64 condition = 3;
 
     repeated MetricConditionLink links = 4;
 }
 
 message CountMetric {
-    optional string name = 1;
+    optional int64 id = 1;
 
-    optional string what = 2;
+    optional int64 what = 2;
 
-    optional string condition = 3;
+    optional int64 condition = 3;
 
     optional FieldMatcher dimensions = 4;
 
-    optional Bucket bucket = 5;
+    optional TimeUnit bucket = 5;
 
     repeated MetricConditionLink links = 6;
 }
 
 message DurationMetric {
-    optional string name = 1;
+    optional int64 id = 1;
 
-    optional string what = 2;
+    optional int64 what = 2;
 
-    optional string condition = 3;
+    optional int64 condition = 3;
 
     repeated MetricConditionLink links = 4;
 
@@ -189,37 +203,37 @@
 
     optional FieldMatcher dimensions = 6;
 
-    optional Bucket bucket = 7;
+    optional TimeUnit bucket = 7;
 }
 
 message GaugeMetric {
-    optional string name = 1;
+    optional int64 id = 1;
 
-    optional string what = 2;
+    optional int64 what = 2;
 
     optional FieldFilter gauge_fields_filter = 3;
 
-    optional string condition = 4;
+    optional int64 condition = 4;
 
     optional FieldMatcher dimensions = 5;
 
-    optional Bucket bucket = 6;
+    optional TimeUnit bucket = 6;
 
     repeated MetricConditionLink links = 7;
 }
 
 message ValueMetric {
-    optional string name = 1;
+    optional int64 id = 1;
 
-    optional string what = 2;
+    optional int64 what = 2;
 
-    optional int32 value_field = 3;
+    optional FieldMatcher value_field = 3;
 
-    optional string condition = 4;
+    optional int64 condition = 4;
 
     optional FieldMatcher dimensions = 5;
 
-    optional Bucket bucket = 6;
+    optional TimeUnit bucket = 6;
 
     repeated MetricConditionLink links = 7;
 
@@ -228,29 +242,51 @@
 }
 
 message Alert {
-    optional string name = 1;
+    optional int64 id = 1;
 
-    optional string metric_name = 2;
+    optional int64 metric_id = 2;
 
-    message IncidentdDetails {
-        repeated int32 section = 1;
-    }
-    optional IncidentdDetails incidentd_details = 3;
+    optional int32 num_buckets = 3;
 
-    optional int32 number_of_buckets = 4;
+    optional int32 refractory_period_secs = 4;
 
-    optional int32 refractory_period_secs = 5;
-
-    optional int64 trigger_if_sum_gt = 6;
+    optional double trigger_if_sum_gt = 5;
 }
 
-message AllowedLogSource {
-    repeated int32 uid = 1;
-    repeated string package = 2;
+message Alarm {
+    optional int64 id = 1;
+    optional int64 offset_millis = 2;
+    optional int64 period_millis = 3;
+}
+
+message IncidentdDetails {
+    repeated int32 section = 1;
+}
+
+message PerfettoDetails {
+    optional int32 perfetto_stuff = 1;
+}
+
+message Subscription {
+    optional int64 id = 1;
+
+    enum RuleType {
+        RULE_TYPE_UNSPECIFIED = 0;
+        ALARM = 1;
+        ALERT = 2;
+    }
+    optional RuleType rule_type = 2;
+
+    optional int64 rule_id = 3;
+
+    oneof subscriber_information {
+        IncidentdDetails incidentd_details = 4;
+        PerfettoDetails perfetto_details = 5;
+    }
 }
 
 message StatsdConfig {
-    optional string name = 1;
+    optional int64 id = 1;
 
     repeated EventMetric event_metric = 2;
 
@@ -268,5 +304,11 @@
 
     repeated Alert alert = 9;
 
-    optional AllowedLogSource log_source = 10;
+    repeated Alarm alarm = 10;
+
+    repeated Subscription subscription = 11;
+
+    repeated string allowed_log_source = 12;
+
+    repeated int64 no_report_metric = 13;
 }
diff --git a/core/java/android/service/autofill/Scorer.java b/cmds/statsd/src/statsd_internal.proto
similarity index 61%
rename from core/java/android/service/autofill/Scorer.java
rename to cmds/statsd/src/statsd_internal.proto
index c401855..25aacee 100644
--- a/core/java/android/service/autofill/Scorer.java
+++ b/cmds/statsd/src/statsd_internal.proto
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.service.autofill;
 
-/**
- * Helper class used to calculate a score.
- *
- * <p>Typically used to calculate the
- * <a href="AutofillService.html#FieldClassification">field classification</a> score between an
- * actual {@link android.view.autofill.AutofillValue} filled by the user and the expected value
- * predicted by an autofill service.
- */
-public interface Scorer {
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
 
+package android.os.statsd;
+
+option java_package = "com.android.os";
+option java_outer_classname = "StatsdInternalProto";
+
+message Field {
+  optional int32 field = 1;
+  optional int32 position_index = 2 [default = -1];
+  repeated Field child = 3;
 }
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 9919abf..c542db2 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -124,7 +124,7 @@
         }
         if (index < 2) continue;
 
-        sendBroadcast(ConfigKey(uid, configName));
+        sendBroadcast(ConfigKey(uid, StrToInt64(configName)));
     }
 }
 
@@ -198,6 +198,7 @@
             index++;
         }
         if (index < 2) continue;
+
         string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name);
         VLOG("full file %s", file_name.c_str());
         int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
@@ -206,7 +207,7 @@
             if (android::base::ReadFdToString(fd, &content)) {
                 StatsdConfig config;
                 if (config.ParseFromString(content)) {
-                    configsMap[ConfigKey(uid, configName)] = config;
+                    configsMap[ConfigKey(uid, StrToInt64(configName))] = config;
                     VLOG("map key uid=%d|name=%s", uid, name);
                 }
             }
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 3d923e2..cc02f34 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -14,6 +14,7 @@
 
 #include "src/config/ConfigManager.h"
 #include "src/metrics/MetricsManager.h"
+#include "statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -31,7 +32,7 @@
 namespace statsd {
 
 static ostream& operator<<(ostream& os, const StatsdConfig& config) {
-    return os << "StatsdConfig{name=" << config.name().c_str() << "}";
+    return os << "StatsdConfig{id=" << config.id() << "}";
 }
 
 }  // namespace statsd
@@ -50,19 +51,21 @@
 /**
  * Validate that the ConfigKey is the one we wanted.
  */
-MATCHER_P2(ConfigKeyEq, uid, name, "") {
-    return arg.GetUid() == uid && arg.GetName() == name;
+MATCHER_P2(ConfigKeyEq, uid, id, "") {
+    return arg.GetUid() == uid && (long long)arg.GetId() == (long long)id;
 }
 
 /**
  * Validate that the StatsdConfig is the one we wanted.
  */
-MATCHER_P(StatsdConfigEq, name, "") {
-    return arg.name() == name;
+MATCHER_P(StatsdConfigEq, id, 0) {
+    return (long long)arg.id() == (long long)id;
 }
 
+const int64_t testConfigId = 12345;
+
 TEST(ConfigManagerTest, TestFakeConfig) {
-    auto metricsManager = std::make_unique<MetricsManager>(ConfigKey(0, "test"),
+    auto metricsManager = std::make_unique<MetricsManager>(ConfigKey(0, testConfigId),
                                                            build_fake_config(), 1000, new UidMap());
     EXPECT_TRUE(metricsManager->isConfigValid());
 }
@@ -77,13 +80,13 @@
     manager->AddListener(listener);
 
     StatsdConfig config91;
-    config91.set_name("91");
+    config91.set_id(91);
     StatsdConfig config92;
-    config92.set_name("92");
+    config92.set_id(92);
     StatsdConfig config93;
-    config93.set_name("93");
+    config93.set_id(93);
     StatsdConfig config94;
-    config94.set_name("94");
+    config94.set_id(94);
 
     {
         InSequence s;
@@ -91,42 +94,46 @@
         manager->Startup();
 
         // Add another one
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("91")))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")),
+            StatsdConfigEq(91)))
                 .RetiresOnSaturation();
-        manager->UpdateConfig(ConfigKey(1, "zzz"), config91);
+        manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91);
 
         // Update It
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("92")))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")),
+            StatsdConfigEq(92)))
                 .RetiresOnSaturation();
-        manager->UpdateConfig(ConfigKey(1, "zzz"), config92);
+        manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92);
 
         // Add one with the same uid but a different name
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq("93")))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("yyy")),
+            StatsdConfigEq(93)))
                 .RetiresOnSaturation();
-        manager->UpdateConfig(ConfigKey(1, "yyy"), config93);
+        manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93);
 
         // Add one with the same name but a different uid
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq("94")))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, StringToId("zzz")),
+            StatsdConfigEq(94)))
                 .RetiresOnSaturation();
-        manager->UpdateConfig(ConfigKey(2, "zzz"), config94);
+        manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94);
 
         // Remove (1,yyy)
-        EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, "yyy")))
+        EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("yyy"))))
                 .RetiresOnSaturation();
-        manager->RemoveConfig(ConfigKey(1, "yyy"));
+        manager->RemoveConfig(ConfigKey(1, StringToId("yyy")));
 
         // Remove (2,zzz)
-        EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "zzz")))
+        EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz"))))
                 .RetiresOnSaturation();
-        manager->RemoveConfig(ConfigKey(2, "zzz"));
+        manager->RemoveConfig(ConfigKey(2, StringToId("zzz")));
 
         // Remove (1,zzz)
-        EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, "zzz")))
+        EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("zzz"))))
                 .RetiresOnSaturation();
-        manager->RemoveConfig(ConfigKey(1, "zzz"));
+        manager->RemoveConfig(ConfigKey(1, StringToId("zzz")));
 
         // Remove (2,zzz) again and we shouldn't get the callback
-        manager->RemoveConfig(ConfigKey(2, "zzz"));
+        manager->RemoveConfig(ConfigKey(2, StringToId("zzz")));
     }
 }
 
@@ -142,16 +149,16 @@
     StatsdConfig config;
 
     EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _)).Times(5);
-    EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "xxx")));
-    EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "yyy")));
-    EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "zzz")));
+    EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx"))));
+    EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy"))));
+    EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz"))));
 
-    manager->Startup();
-    manager->UpdateConfig(ConfigKey(1, "aaa"), config);
-    manager->UpdateConfig(ConfigKey(2, "xxx"), config);
-    manager->UpdateConfig(ConfigKey(2, "yyy"), config);
-    manager->UpdateConfig(ConfigKey(2, "zzz"), config);
-    manager->UpdateConfig(ConfigKey(3, "bbb"), config);
+    manager->StartupForTest();
+    manager->UpdateConfig(ConfigKey(1, StringToId("aaa")), config);
+    manager->UpdateConfig(ConfigKey(2, StringToId("xxx")), config);
+    manager->UpdateConfig(ConfigKey(2, StringToId("yyy")), config);
+    manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config);
+    manager->UpdateConfig(ConfigKey(3, StringToId("bbb")), config);
 
     manager->RemoveConfigs(2);
 }
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 6cd31dd..fe0f59d 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -21,6 +21,7 @@
 #include "src/metrics/MetricProducer.h"
 #include "src/metrics/ValueMetricProducer.h"
 #include "src/metrics/metrics_manager_util.h"
+#include "statsd_test_util.h"
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
@@ -40,16 +41,16 @@
 
 // TODO: ADD MORE TEST CASES.
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 
 const long timeBaseSec = 1000;
 
 StatsdConfig buildGoodConfig() {
     StatsdConfig config;
-    config.set_name("12345");
+    config.set_id(12345);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_IS_ON");
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
@@ -59,7 +60,7 @@
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_IS_OFF");
+    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
 
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
@@ -69,24 +70,26 @@
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_ON_OR_OFF");
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
 
     AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher("SCREEN_IS_ON");
-    combination->add_matcher("SCREEN_IS_OFF");
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
+    combination->add_matcher(StringToId("SCREEN_IS_OFF"));
 
     CountMetric* metric = config.add_count_metric();
-    metric->set_name("3");
-    metric->set_what("SCREEN_IS_ON");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_IS_ON"));
+    metric->set_bucket(ONE_MINUTE);
     metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
     metric->mutable_dimensions()->add_child()->set_field(1);
 
+    config.add_no_report_metric(3);
+
     auto alert = config.add_alert();
-    alert->set_name("3");
-    alert->set_metric_name("3");
-    alert->set_number_of_buckets(10);
+    alert->set_id(3);
+    alert->set_metric_id(3);
+    alert->set_num_buckets(10);
     alert->set_refractory_period_secs(100);
     alert->set_trigger_if_sum_gt(100);
     return config;
@@ -94,10 +97,10 @@
 
 StatsdConfig buildCircleMatchers() {
     StatsdConfig config;
-    config.set_name("12345");
+    config.set_id(12345);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_IS_ON");
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
@@ -107,35 +110,35 @@
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_ON_OR_OFF");
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
 
     AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher("SCREEN_IS_ON");
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
     // Circle dependency
-    combination->add_matcher("SCREEN_ON_OR_OFF");
+    combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
 
     return config;
 }
 
 StatsdConfig buildAlertWithUnknownMetric() {
     StatsdConfig config;
-    config.set_name("12345");
+    config.set_id(12345);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_IS_ON");
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
 
     CountMetric* metric = config.add_count_metric();
-    metric->set_name("3");
-    metric->set_what("SCREEN_IS_ON");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_IS_ON"));
+    metric->set_bucket(ONE_MINUTE);
     metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
     metric->mutable_dimensions()->add_child()->set_field(1);
 
     auto alert = config.add_alert();
-    alert->set_name("3");
-    alert->set_metric_name("2");
-    alert->set_number_of_buckets(10);
+    alert->set_id(3);
+    alert->set_metric_id(2);
+    alert->set_num_buckets(10);
     alert->set_refractory_period_secs(100);
     alert->set_trigger_if_sum_gt(100);
     return config;
@@ -143,10 +146,10 @@
 
 StatsdConfig buildMissingMatchers() {
     StatsdConfig config;
-    config.set_name("12345");
+    config.set_id(12345);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_IS_ON");
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
@@ -156,29 +159,29 @@
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_ON_OR_OFF");
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
 
     AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher("SCREEN_IS_ON");
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
     // undefined matcher
-    combination->add_matcher("ABC");
+    combination->add_matcher(StringToId("ABC"));
 
     return config;
 }
 
 StatsdConfig buildMissingPredicate() {
     StatsdConfig config;
-    config.set_name("12345");
+    config.set_id(12345);
 
     CountMetric* metric = config.add_count_metric();
-    metric->set_name("3");
-    metric->set_what("SCREEN_EVENT");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    metric->set_condition("SOME_CONDITION");
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_EVENT"));
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_condition(StringToId("SOME_CONDITION"));
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_EVENT");
+    eventMatcher->set_id(StringToId("SCREEN_EVENT"));
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2);
@@ -188,38 +191,38 @@
 
 StatsdConfig buildDimensionMetricsWithMultiTags() {
     StatsdConfig config;
-    config.set_name("12345");
+    config.set_id(12345);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("BATTERY_VERY_LOW");
+    eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("BATTERY_VERY_VERY_LOW");
+    eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(3);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("BATTERY_LOW");
+    eventMatcher->set_id(StringToId("BATTERY_LOW"));
 
     AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher("BATTERY_VERY_LOW");
-    combination->add_matcher("BATTERY_VERY_VERY_LOW");
+    combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
+    combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
 
     // Count process state changes, slice by uid, while SCREEN_IS_OFF
     CountMetric* metric = config.add_count_metric();
-    metric->set_name("3");
-    metric->set_what("BATTERY_LOW");
-    metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+    metric->set_id(3);
+    metric->set_what(StringToId("BATTERY_LOW"));
+    metric->set_bucket(ONE_MINUTE);
     // This case is interesting. We want to dimension across two atoms.
     metric->mutable_dimensions()->add_child()->set_field(1);
 
     auto alert = config.add_alert();
-    alert->set_name("3");
-    alert->set_metric_name("3");
-    alert->set_number_of_buckets(10);
+    alert->set_id(103);
+    alert->set_metric_id(3);
+    alert->set_num_buckets(10);
     alert->set_refractory_period_secs(100);
     alert->set_trigger_if_sum_gt(100);
     return config;
@@ -227,10 +230,10 @@
 
 StatsdConfig buildCirclePredicates() {
     StatsdConfig config;
-    config.set_name("12345");
+    config.set_id(12345);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_IS_ON");
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
@@ -240,7 +243,7 @@
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_name("SCREEN_IS_OFF");
+    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
 
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
@@ -250,18 +253,18 @@
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
     auto condition = config.add_predicate();
-    condition->set_name("SCREEN_IS_ON");
+    condition->set_id(StringToId("SCREEN_IS_ON"));
     SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
-    simplePredicate->set_start("SCREEN_IS_ON");
-    simplePredicate->set_stop("SCREEN_IS_OFF");
+    simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
+    simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
 
     condition = config.add_predicate();
-    condition->set_name("SCREEN_IS_EITHER_ON_OFF");
+    condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
 
     Predicate_Combination* combination = condition->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
-    combination->add_predicate("SCREEN_IS_ON");
-    combination->add_predicate("SCREEN_IS_EITHER_ON_OFF");
+    combination->add_predicate(StringToId("SCREEN_IS_ON"));
+    combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
 
     return config;
 }
@@ -277,12 +280,15 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    std::set<int64_t> noReportMetricIds;
 
     EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                 conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
+                                 conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                                 noReportMetricIds));
     EXPECT_EQ(1u, allMetricProducers.size());
     EXPECT_EQ(1u, allAnomalyTrackers.size());
+    EXPECT_EQ(1u, noReportMetricIds.size());
 }
 
 TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
@@ -296,10 +302,12 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -313,10 +321,12 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -330,9 +340,11 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -346,9 +358,11 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -362,10 +376,12 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -379,10 +395,12 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
-                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+                                  noReportMetricIds));
 }
 
 #else
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index a27bed5..5d053e2 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -41,7 +41,7 @@
  */
 class MockMetricsManager : public MetricsManager {
 public:
-    MockMetricsManager() : MetricsManager(ConfigKey(1, "key"), StatsdConfig(), 1000, new UidMap()) {
+    MockMetricsManager() : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, new UidMap()) {
     }
 
     MOCK_METHOD0(byteSize, size_t());
@@ -56,7 +56,7 @@
 
     MockMetricsManager mockMetricsManager;
 
-    ConfigKey key(100, "key");
+    ConfigKey key(100, 12345);
     // Expect only the first flush to trigger a check for byte size since the last two are
     // rate-limited.
     EXPECT_CALL(mockMetricsManager, byteSize()).Times(1);
@@ -74,7 +74,7 @@
 
     MockMetricsManager mockMetricsManager;
 
-    ConfigKey key(100, "key");
+    ConfigKey key(100, 12345);
     EXPECT_CALL(mockMetricsManager, byteSize())
             .Times(2)
             .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95)));
@@ -98,7 +98,7 @@
 
     MockMetricsManager mockMetricsManager;
 
-    ConfigKey key(100, "key");
+    ConfigKey key(100, 12345);
     EXPECT_CALL(mockMetricsManager, byteSize())
             .Times(1)
             .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 8a394f7..945af27 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -18,6 +18,7 @@
 #include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 #include "statslog.h"
+#include "statsd_test_util.h"
 
 #include <gtest/gtest.h>
 
@@ -156,8 +157,8 @@
 TEST(UidMapTest, TestClearingOutput) {
     UidMap m;
 
-    ConfigKey config1(1, "config1");
-    ConfigKey config2(1, "config2");
+    ConfigKey config1(1, StringToId("config1"));
+    ConfigKey config2(1, StringToId("config2"));
 
     m.OnConfigUpdated(config1);
 
@@ -211,7 +212,7 @@
 TEST(UidMapTest, TestMemoryComputed) {
     UidMap m;
 
-    ConfigKey config1(1, "config1");
+    ConfigKey config1(1, StringToId("config1"));
     m.OnConfigUpdated(config1);
 
     size_t startBytes = m.mBytesUsed;
@@ -241,7 +242,7 @@
     UidMap m;
     string buf;
 
-    ConfigKey config1(1, "config1");
+    ConfigKey config1(1, StringToId("config1"));
     m.OnConfigUpdated(config1);
 
     size_t startBytes = m.mBytesUsed;
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index da872ad..5842bc8 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -31,7 +31,7 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 
 HashableDimensionKey getMockDimensionKey(int key, string value) {
     DimensionsValue dimensionsValue;
@@ -57,7 +57,7 @@
 TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
     const int64_t bucketSizeNs = 30 * NS_PER_SEC;
     Alert alert;
-    alert.set_number_of_buckets(3);
+    alert.set_num_buckets(3);
     alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
     alert.set_trigger_if_sum_gt(2);
 
@@ -177,7 +177,7 @@
 TEST(AnomalyTrackerTest, TestSparseBuckets) {
     const int64_t bucketSizeNs = 30 * NS_PER_SEC;
     Alert alert;
-    alert.set_number_of_buckets(3);
+    alert.set_num_buckets(3);
     alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
     alert.set_trigger_if_sum_gt(2);
 
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 705804f..819f2be 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -11,7 +11,9 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+
 #include "src/condition/SimpleConditionTracker.h"
+#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -29,7 +31,7 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 
 const int ATTRIBUTION_NODE_FIELD_ID = 1;
 const int ATTRIBUTION_UID_FIELD_ID = 1;
@@ -38,9 +40,9 @@
 SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
                                          bool outputSlicedUid, Position position) {
     SimplePredicate simplePredicate;
-    simplePredicate.set_start("WAKE_LOCK_ACQUIRE");
-    simplePredicate.set_stop("WAKE_LOCK_RELEASE");
-    simplePredicate.set_stop_all("RELEASE_ALL");
+    simplePredicate.set_start(StringToId("WAKE_LOCK_ACQUIRE"));
+    simplePredicate.set_stop(StringToId("WAKE_LOCK_RELEASE"));
+    simplePredicate.set_stop_all(StringToId("RELEASE_ALL"));
     if (outputSlicedUid) {
         simplePredicate.mutable_dimensions()->set_field(TAG_ID);
         simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID);
@@ -73,10 +75,10 @@
     event->init();
 }
 
-std::map<string, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
+std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
     const Position position,
     const std::vector<int> &uids, const string& conditionName) {
-    std::map<string, std::vector<HashableDimensionKey>>  outputKeyMap;
+    std::map<int64_t, std::vector<HashableDimensionKey>>  outputKeyMap;
     std::vector<int> uid_indexes;
     switch(position) {
         case Position::FIRST:
@@ -96,28 +98,29 @@
     for (const int idx : uid_indexes) {
         DimensionsValue dimensionsValue;
         dimensionsValue.set_field(TAG_ID);
-        dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID);
+        dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(
+            ATTRIBUTION_NODE_FIELD_ID);
         dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
             ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID);
         dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
             ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]);
-        outputKeyMap[conditionName].push_back(HashableDimensionKey(dimensionsValue));
+        outputKeyMap[StringToId(conditionName)].push_back(HashableDimensionKey(dimensionsValue));
     }
     return outputKeyMap;
 }
 
 TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
     SimplePredicate simplePredicate;
-    simplePredicate.set_start("SCREEN_TURNED_ON");
-    simplePredicate.set_stop("SCREEN_TURNED_OFF");
+    simplePredicate.set_start(StringToId("SCREEN_TURNED_ON"));
+    simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF"));
     simplePredicate.set_count_nesting(false);
     simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN);
 
-    unordered_map<string, int> trackerNameIndexMap;
-    trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
-    trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+    unordered_map<int64_t, int> trackerNameIndexMap;
+    trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
+    trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/,
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/,
                                             simplePredicate, trackerNameIndexMap);
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
@@ -191,15 +194,15 @@
 
 TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
     SimplePredicate simplePredicate;
-    simplePredicate.set_start("SCREEN_TURNED_ON");
-    simplePredicate.set_stop("SCREEN_TURNED_OFF");
+    simplePredicate.set_start(StringToId("SCREEN_TURNED_ON"));
+    simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF"));
     simplePredicate.set_count_nesting(true);
 
-    unordered_map<string, int> trackerNameIndexMap;
-    trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
-    trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+    unordered_map<int64_t, int> trackerNameIndexMap;
+    trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
+    trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON",
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -267,12 +270,12 @@
                 position);
         string conditionName = "WL_HELD_BY_UID2";
 
-        unordered_map<string, int> trackerNameIndexMap;
-        trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
-        trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
-        trackerNameIndexMap["RELEASE_ALL"] = 2;
+        unordered_map<int64_t, int> trackerNameIndexMap;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+        trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
         std::vector<int> uids = {111, 222, 333};
@@ -371,12 +374,12 @@
             Position::ANY /* position */);
     string conditionName = "WL_HELD";
 
-    unordered_map<string, int> trackerNameIndexMap;
-    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
-    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
-    trackerNameIndexMap["RELEASE_ALL"] = 2;
+    unordered_map<int64_t, int> trackerNameIndexMap;
+    trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+    trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+    trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -461,12 +464,12 @@
                 position);
         string conditionName = "WL_HELD_BY_UID3";
 
-        unordered_map<string, int> trackerNameIndexMap;
-        trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
-        trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
-        trackerNameIndexMap["RELEASE_ALL"] = 2;
+        unordered_map<int64_t, int> trackerNameIndexMap;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+        trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
 
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index c747016..cbcc36b 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -15,6 +15,7 @@
 #include <gtest/gtest.h>
 
 #include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
 
 #include <vector>
@@ -55,24 +56,24 @@
     *config.add_predicate() = isInBackgroundPredicate;
 
     auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_name("combinationPredicate");
+    combinationPredicate->set_id(StringToId("combinationPredicate"));
     combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
     addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
     addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
     addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate);
 
     auto countMetric = config.add_count_metric();
-    countMetric->set_name("AppCrashes");
-    countMetric->set_what(appCrashMatcher.name());
-    countMetric->set_condition(combinationPredicate->name());
+    countMetric->set_id(StringToId("AppCrashes"));
+    countMetric->set_what(appCrashMatcher.id());
+    countMetric->set_condition(combinationPredicate->id());
     // The metric is dimensioning by uid only.
     *countMetric->mutable_dimensions() =
         CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
-    countMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL);
+    countMetric->set_bucket(ONE_MINUTE);
 
     // Links between crash atom and condition of app is in syncing.
     auto links = countMetric->add_links();
-    links->set_condition(isSyncingPredicate.name());
+    links->set_condition(isSyncingPredicate.id());
     auto dimensionWhat = links->mutable_dimensions_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
@@ -82,7 +83,7 @@
 
     // Links between crash atom and condition of app is in background.
     links = countMetric->add_links();
-    links->set_condition(isInBackgroundPredicate.name());
+    links->set_condition(isInBackgroundPredicate.id());
     dimensionWhat = links->mutable_dimensions_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
@@ -95,7 +96,8 @@
 TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) {
     auto config = CreateStatsdConfig();
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketSizeNs = config.count_metric(0).bucket().bucket_size_millis() * 1000 * 1000;
+    uint64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 8d7b2d5..47e8a72 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -15,6 +15,7 @@
 #include <gtest/gtest.h>
 
 #include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
 
 #include <vector>
@@ -43,15 +44,15 @@
     *config.add_predicate() = holdingWakelockPredicate;
 
     auto durationMetric = config.add_duration_metric();
-    durationMetric->set_name("WakelockDuration");
-    durationMetric->set_what(holdingWakelockPredicate.name());
-    durationMetric->set_condition(screenIsOffPredicate.name());
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_condition(screenIsOffPredicate.id());
     durationMetric->set_aggregation_type(aggregationType);
     // The metric is dimensioning by first attribution node and only by uid.
     *durationMetric->mutable_dimensions() =
         CreateAttributionUidDimensions(
             android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-    durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL);
+    durationMetric->set_bucket(ONE_MINUTE);
     return config;
 }
 
@@ -61,7 +62,7 @@
         auto config = CreateStatsdConfig(aggregationType);
         uint64_t bucketStartTimeNs = 10000000000;
         uint64_t bucketSizeNs =
-            config.duration_metric(0).bucket().bucket_size_millis() * 1000 * 1000;
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
         auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 7658044..a134300 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -14,6 +14,7 @@
 
 #include "src/guardrail/StatsdStats.h"
 #include "statslog.h"
+#include "tests/statsd_test_util.h"
 
 #include <gtest/gtest.h>
 #include <vector>
@@ -28,8 +29,7 @@
 
 TEST(StatsdStatsTest, TestValidConfigAdd) {
     StatsdStats stats;
-    string name = "StatsdTest";
-    ConfigKey key(0, name);
+    ConfigKey key(0, 12345);
     const int metricsCount = 10;
     const int conditionsCount = 20;
     const int matchersCount = 30;
@@ -45,7 +45,7 @@
     EXPECT_EQ(1, report.config_stats_size());
     const auto& configReport = report.config_stats(0);
     EXPECT_EQ(0, configReport.uid());
-    EXPECT_EQ(name, configReport.name());
+    EXPECT_EQ(12345, configReport.id());
     EXPECT_EQ(metricsCount, configReport.metric_count());
     EXPECT_EQ(conditionsCount, configReport.condition_count());
     EXPECT_EQ(matchersCount, configReport.matcher_count());
@@ -56,8 +56,7 @@
 
 TEST(StatsdStatsTest, TestInvalidConfigAdd) {
     StatsdStats stats;
-    string name = "StatsdTest";
-    ConfigKey key(0, name);
+    ConfigKey key(0, 12345);
     const int metricsCount = 10;
     const int conditionsCount = 20;
     const int matchersCount = 30;
@@ -78,8 +77,7 @@
 
 TEST(StatsdStatsTest, TestConfigRemove) {
     StatsdStats stats;
-    string name = "StatsdTest";
-    ConfigKey key(0, name);
+    ConfigKey key(0, 12345);
     const int metricsCount = 10;
     const int conditionsCount = 20;
     const int matchersCount = 30;
@@ -105,22 +103,22 @@
 
 TEST(StatsdStatsTest, TestSubStats) {
     StatsdStats stats;
-    ConfigKey key(0, "test");
+    ConfigKey key(0, 12345);
     stats.noteConfigReceived(key, 2, 3, 4, 5, true);
 
-    stats.noteMatcherMatched(key, "matcher1");
-    stats.noteMatcherMatched(key, "matcher1");
-    stats.noteMatcherMatched(key, "matcher2");
+    stats.noteMatcherMatched(key, StringToId("matcher1"));
+    stats.noteMatcherMatched(key, StringToId("matcher1"));
+    stats.noteMatcherMatched(key, StringToId("matcher2"));
 
-    stats.noteConditionDimensionSize(key, "condition1", 250);
-    stats.noteConditionDimensionSize(key, "condition1", 240);
+    stats.noteConditionDimensionSize(key, StringToId("condition1"), 250);
+    stats.noteConditionDimensionSize(key, StringToId("condition1"), 240);
 
-    stats.noteMetricDimensionSize(key, "metric1", 201);
-    stats.noteMetricDimensionSize(key, "metric1", 202);
+    stats.noteMetricDimensionSize(key, StringToId("metric1"), 201);
+    stats.noteMetricDimensionSize(key, StringToId("metric1"), 202);
 
-    stats.noteAnomalyDeclared(key, "alert1");
-    stats.noteAnomalyDeclared(key, "alert1");
-    stats.noteAnomalyDeclared(key, "alert2");
+    stats.noteAnomalyDeclared(key, StringToId("alert1"));
+    stats.noteAnomalyDeclared(key, StringToId("alert1"));
+    stats.noteAnomalyDeclared(key, StringToId("alert2"));
 
     // broadcast-> 2
     stats.noteBroadcastSent(key);
@@ -147,39 +145,39 @@
 
     EXPECT_EQ(2, configReport.matcher_stats_size());
     // matcher1 is the first in the list
-    if (!configReport.matcher_stats(0).name().compare("matcher1")) {
+    if (configReport.matcher_stats(0).id() == StringToId("matcher1")) {
         EXPECT_EQ(2, configReport.matcher_stats(0).matched_times());
         EXPECT_EQ(1, configReport.matcher_stats(1).matched_times());
-        EXPECT_EQ("matcher2", configReport.matcher_stats(1).name());
+        EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(1).id());
     } else {
         // matcher1 is the second in the list.
         EXPECT_EQ(1, configReport.matcher_stats(0).matched_times());
-        EXPECT_EQ("matcher2", configReport.matcher_stats(0).name());
+        EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(0).id());
 
         EXPECT_EQ(2, configReport.matcher_stats(1).matched_times());
-        EXPECT_EQ("matcher1", configReport.matcher_stats(1).name());
+        EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id());
     }
 
     EXPECT_EQ(2, configReport.alert_stats_size());
-    bool alert1first = !configReport.alert_stats(0).name().compare("alert1");
-    EXPECT_EQ("alert1", configReport.alert_stats(alert1first ? 0 : 1).name());
+    bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1");
+    EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id());
     EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times());
-    EXPECT_EQ("alert2", configReport.alert_stats(alert1first ? 1 : 0).name());
+    EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id());
     EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times());
 
     EXPECT_EQ(1, configReport.condition_stats_size());
-    EXPECT_EQ("condition1", configReport.condition_stats(0).name());
+    EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id());
     EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts());
 
     EXPECT_EQ(1, configReport.metric_stats_size());
-    EXPECT_EQ("metric1", configReport.metric_stats(0).name());
+    EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id());
     EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts());
 
     // after resetting the stats, some new events come
-    stats.noteMatcherMatched(key, "matcher99");
-    stats.noteConditionDimensionSize(key, "condition99", 300);
-    stats.noteMetricDimensionSize(key, "metric99", 270);
-    stats.noteAnomalyDeclared(key, "alert99");
+    stats.noteMatcherMatched(key, StringToId("matcher99"));
+    stats.noteConditionDimensionSize(key, StringToId("condition99"), 300);
+    stats.noteMetricDimensionSize(key, StringToId("metric99tion99"), 270);
+    stats.noteAnomalyDeclared(key, StringToId("alert99"));
 
     // now the config stats should only contain the stats about the new event.
     stats.dumpStats(&output, false);
@@ -188,19 +186,19 @@
     EXPECT_EQ(1, report.config_stats_size());
     const auto& configReport2 = report.config_stats(0);
     EXPECT_EQ(1, configReport2.matcher_stats_size());
-    EXPECT_EQ("matcher99", configReport2.matcher_stats(0).name());
+    EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id());
     EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times());
 
     EXPECT_EQ(1, configReport2.condition_stats_size());
-    EXPECT_EQ("condition99", configReport2.condition_stats(0).name());
+    EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id());
     EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts());
 
     EXPECT_EQ(1, configReport2.metric_stats_size());
-    EXPECT_EQ("metric99", configReport2.metric_stats(0).name());
+    EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id());
     EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts());
 
     EXPECT_EQ(1, configReport2.alert_stats_size());
-    EXPECT_EQ("alert99", configReport2.alert_stats(0).name());
+    EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id());
     EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times());
 }
 
@@ -260,7 +258,7 @@
     for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
         timestamps.push_back(i);
     }
-    ConfigKey key(0, "test");
+    ConfigKey key(0, 12345);
     stats.noteConfigReceived(key, 2, 3, 4, 5, true);
 
     for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 6e114a6..768336b 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -14,7 +14,9 @@
 
 #include "src/metrics/CountMetricProducer.h"
 #include "src/dimension.h"
+#include "src/stats_log_util.h"
 #include "metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -33,18 +35,18 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 
 TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
     int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
     int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
     int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
     int tagId = 1;
 
     CountMetric metric;
-    metric.set_name("1");
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
 
     LogEvent event1(tagId, bucketStartTimeNs + 1);
     LogEvent event2(tagId, bucketStartTimeNs + 2);
@@ -97,12 +99,12 @@
 
 TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
     int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 
     CountMetric metric;
-    metric.set_name("1");
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.set_condition("SCREEN_ON");
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_condition(StringToId("SCREEN_ON"));
 
     LogEvent event1(1, bucketStartTimeNs + 1);
     LogEvent event2(1, bucketStartTimeNs + 10);
@@ -136,17 +138,17 @@
 
 TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
     int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 
     int tagId = 1;
     int conditionTagId = 2;
 
     CountMetric metric;
-    metric.set_name("1");
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
     MetricConditionLink* link = metric.add_links();
-    link->set_condition("APP_IN_BACKGROUND_PER_UID");
+    link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
     *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
     *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
 
@@ -154,13 +156,15 @@
     event1.write("111");  // uid
     event1.init();
     ConditionKey key1;
-    key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")};
+    key1[StringToId("APP_IN_BACKGROUND_PER_UID")] =
+        {getMockedDimensionKey(conditionTagId, 2, "111")};
 
     LogEvent event2(tagId, bucketStartTimeNs + 10);
     event2.write("222");  // uid
     event2.init();
     ConditionKey key2;
-    key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")};
+    key2[StringToId("APP_IN_BACKGROUND_PER_UID")] =
+        {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
@@ -189,20 +193,20 @@
 
 TEST(CountMetricProducerTest, TestAnomalyDetection) {
     Alert alert;
-    alert.set_name("alert");
-    alert.set_metric_name("1");
+    alert.set_id(11);
+    alert.set_metric_id(1);
     alert.set_trigger_if_sum_gt(2);
-    alert.set_number_of_buckets(2);
+    alert.set_num_buckets(2);
     alert.set_refractory_period_secs(1);
 
     int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs = 30 * NS_PER_SEC;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
     int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
     int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
 
     CountMetric metric;
-    metric.set_name("1");
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 8ee94c7..a59f1fe 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "src/metrics/DurationMetricProducer.h"
+#include "src/stats_log_util.h"
 #include "metrics_test_helper.h"
 #include "src/condition/ConditionWizard.h"
 
@@ -36,16 +37,16 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 
 TEST(DurationMetricTrackerTest, TestNoCondition) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 
     DurationMetric metric;
-    metric.set_name("1");
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
 
     int tagId = 1;
@@ -76,11 +77,11 @@
 TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     uint64_t bucketStartTimeNs = 10000000000;
-    uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 
     DurationMetric metric;
-    metric.set_name("1");
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
 
     int tagId = 1;
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 0ba1c2f..7171de9 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -15,6 +15,7 @@
 #include "src/metrics/EventMetricProducer.h"
 #include "src/dimension.h"
 #include "metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -33,7 +34,7 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 
 TEST(EventMetricProducerTest, TestNoCondition) {
     uint64_t bucketStartTimeNs = 10000000000;
@@ -41,7 +42,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     EventMetric metric;
-    metric.set_name("1");
+    metric.set_id(1);
 
     LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
     LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
@@ -64,8 +65,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     EventMetric metric;
-    metric.set_name("1");
-    metric.set_condition("SCREEN_ON");
+    metric.set_id(1);
+    metric.set_condition(StringToId("SCREEN_ON"));
 
     LogEvent event1(1, bucketStartTimeNs + 1);
     LogEvent event2(1, bucketStartTimeNs + 10);
@@ -93,10 +94,10 @@
     int conditionTagId = 2;
 
     EventMetric metric;
-    metric.set_name("1");
-    metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
+    metric.set_id(1);
+    metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
     MetricConditionLink* link = metric.add_links();
-    link->set_condition("APP_IN_BACKGROUND_PER_UID");
+    link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
     *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
     *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
 
@@ -104,13 +105,13 @@
     EXPECT_TRUE(event1.write("111"));
     event1.init();
     ConditionKey key1;
-    key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")};
+    key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "111")};
 
     LogEvent event2(tagId, bucketStartTimeNs + 10);
     EXPECT_TRUE(event2.write("222"));
     event2.init();
     ConditionKey key2;
-    key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")};
+    key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 359851f..ad9c5a3 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -13,8 +13,10 @@
 // limitations under the License.
 
 #include "src/metrics/GaugeMetricProducer.h"
+#include "src/stats_log_util.h"
 #include "logd/LogEvent.h"
 #include "metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -34,19 +36,19 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
-const string metricName = "test_metric";
+const int64_t metricId = 123;
 const int64_t bucketStartTimeNs = 10000000000;
-const int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
 
 TEST(GaugeMetricProducerTest, TestNoCondition) {
     GaugeMetric metric;
-    metric.set_name(metricName);
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
     metric.mutable_gauge_fields_filter()->set_include_all(false);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
@@ -63,7 +65,7 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      tagId, tagId, bucketStartTimeNs, pullerManager);
+                                      tagId, bucketStartTimeNs, pullerManager);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -119,12 +121,12 @@
 
 TEST(GaugeMetricProducerTest, TestWithCondition) {
     GaugeMetric metric;
-    metric.set_name(metricName);
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
-    metric.set_condition("SCREEN_ON");
+    metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -143,7 +145,7 @@
                 return true;
             }));
 
-    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, tagId,
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId,
                                       bucketStartTimeNs, pullerManager);
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -186,19 +188,19 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     GaugeMetric metric;
-    metric.set_name(metricName);
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      tagId, tagId, bucketStartTimeNs, pullerManager);
+                                      tagId, bucketStartTimeNs, pullerManager);
 
     Alert alert;
-    alert.set_name("alert");
-    alert.set_metric_name(metricName);
+    alert.set_id(101);
+    alert.set_metric_id(metricId);
     alert.set_trigger_if_sum_gt(25);
-    alert.set_number_of_buckets(2);
+    alert.set_num_buckets(2);
     sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
 
     int tagId = 1;
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 7843ca0..704a466 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -13,8 +13,9 @@
 // limitations under the License.
 
 #include "src/metrics/duration_helper/MaxDurationTracker.h"
-#include "metrics_test_helper.h"
 #include "src/condition/ConditionWizard.h"
+#include "metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -36,7 +37,7 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 
 const int TagId = 1;
 
@@ -50,12 +51,13 @@
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey conditionKey1;
-    conditionKey1["condition"] = conditionKey;
+    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+    int64_t metricId = 1;
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
                                bucketSizeNs, {});
 
     tracker.noteStart(key1, true, bucketStartTimeNs, conditionKey1);
@@ -79,12 +81,13 @@
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey conditionKey1;
-    conditionKey1["condition"] = conditionKey;
+    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+    int64_t metricId = 1;
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
                                bucketSizeNs, {});
 
     tracker.noteStart(key1, true, bucketStartTimeNs + 1, conditionKey1);
@@ -110,12 +113,13 @@
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey conditionKey1;
-    conditionKey1["condition"] = conditionKey;
+    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+    int64_t metricId = 1;
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
                                bucketSizeNs, {});
 
     // The event starts.
@@ -141,12 +145,13 @@
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey conditionKey1;
-    conditionKey1["condition"] = conditionKey;
+    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs,
+    int64_t metricId = 1;
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
                                bucketSizeNs, {});
 
     // 2 starts
@@ -177,7 +182,7 @@
 
     ConditionKey conditionKey1;
     HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
-    conditionKey1["APP_BACKGROUND"] = conditionKey;
+    conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
 
     EXPECT_CALL(*wizard, query(_, conditionKey1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
@@ -189,7 +194,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, bucketStartTimeNs,
+    int64_t metricId = 1;
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
                                bucketSizeNs, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
@@ -206,23 +212,24 @@
 }
 
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+    int64_t metricId = 1;
     Alert alert;
-    alert.set_name("alert");
-    alert.set_metric_name("metric");
+    alert.set_id(101);
+    alert.set_metric_id(metricId);
     alert.set_trigger_if_sum_gt(32 * NS_PER_SEC);
-    alert.set_number_of_buckets(2);
+    alert.set_num_buckets(2);
     alert.set_refractory_period_secs(1);
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey conditionKey1;
-    conditionKey1["APP_BACKGROUND"] = conditionKey;
+    conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
                                bucketSizeNs, {anomalyTracker});
 
     tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 550b059..36cdaae 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -13,8 +13,9 @@
 // limitations under the License.
 
 #include "src/metrics/duration_helper/OringDurationTracker.h"
-#include "metrics_test_helper.h"
 #include "src/condition/ConditionWizard.h"
+#include "metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -34,8 +35,9 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 const int TagId = 1;
+const int64_t metricId = 123;
 const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
 
 const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
@@ -46,7 +48,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -55,7 +57,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
                                  bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -75,7 +77,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -83,7 +85,7 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -102,7 +104,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -110,7 +112,7 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -128,7 +130,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -137,7 +139,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -163,7 +165,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
@@ -175,7 +177,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
                                  bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -194,7 +196,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
     EXPECT_CALL(*wizard, query(_, key1))
             .Times(2)
@@ -208,7 +210,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
                                  bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -229,7 +231,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
@@ -240,7 +242,7 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -260,22 +262,22 @@
 
 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
     Alert alert;
-    alert.set_name("alert");
-    alert.set_metric_name("1");
+    alert.set_id(101);
+    alert.set_metric_id(1);
     alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
-    alert.set_number_of_buckets(2);
+    alert.set_num_buckets(2);
     alert.set_refractory_period_secs(1);
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {anomalyTracker});
 
     // Nothing in the past bucket.
@@ -322,22 +324,22 @@
 
 TEST(OringDurationTrackerTest, TestAnomalyDetection) {
     Alert alert;
-    alert.set_name("alert");
-    alert.set_metric_name("1");
+    alert.set_id(101);
+    alert.set_metric_id(1);
     alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
-    alert.set_number_of_buckets(2);
+    alert.set_num_buckets(2);
     alert.set_refractory_period_secs(1);
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = kConditionKey1;
+    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
                                  bucketStartTimeNs, bucketSizeNs, {anomalyTracker});
 
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 12bc834..459da01 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -13,7 +13,9 @@
 // limitations under the License.
 
 #include "src/metrics/ValueMetricProducer.h"
+#include "src/stats_log_util.h"
 #include "metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -34,11 +36,11 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, "test");
+const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
-const string metricName = "test_metric";
+const int64_t metricId = 123;
 const int64_t bucketStartTimeNs = 10000000000;
-const int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
@@ -48,9 +50,10 @@
  */
 TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
     ValueMetric metric;
-    metric.set_name(metricName);
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.set_value_field(2);
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     // TODO: pending refactor of StatsPullerManager
@@ -124,10 +127,11 @@
  */
 TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
     ValueMetric metric;
-    metric.set_name(metricName);
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.set_value_field(2);
-    metric.set_condition("SCREEN_ON");
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     shared_ptr<MockStatsPullerManager> pullerManager =
@@ -200,9 +204,10 @@
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
     ValueMetric metric;
-    metric.set_name(metricName);
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.set_value_field(2);
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     shared_ptr<MockStatsPullerManager> pullerManager =
@@ -240,16 +245,17 @@
 
 TEST(ValueMetricProducerTest, TestAnomalyDetection) {
     Alert alert;
-    alert.set_name("alert");
-    alert.set_metric_name(metricName);
+    alert.set_id(101);
+    alert.set_metric_id(metricId);
     alert.set_trigger_if_sum_gt(130);
-    alert.set_number_of_buckets(2);
+    alert.set_num_buckets(2);
     alert.set_refractory_period_secs(3);
 
     ValueMetric metric;
-    metric.set_name(metricName);
-    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.set_value_field(2);
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 39e366f..939dc1f 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -22,7 +22,7 @@
 AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
                                                   WakelockStateChanged::State state) {
     AtomMatcher atom_matcher;
-    atom_matcher.set_name(name);
+    atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
@@ -42,7 +42,7 @@
 AtomMatcher CreateScreenStateChangedAtomMatcher(
     const string& name, ScreenStateChanged::State state) {
     AtomMatcher atom_matcher;
-    atom_matcher.set_name(name);
+    atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
@@ -62,7 +62,7 @@
 AtomMatcher CreateSyncStateChangedAtomMatcher(
     const string& name, SyncStateChanged::State state) {
     AtomMatcher atom_matcher;
-    atom_matcher.set_name(name);
+    atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
@@ -82,7 +82,7 @@
 AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
     const string& name, ActivityForegroundStateChanged::Activity activity) {
     AtomMatcher atom_matcher;
-    atom_matcher.set_name(name);
+    atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
@@ -104,7 +104,7 @@
 AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher(
     const string& name, ProcessLifeCycleStateChanged::Event event) {
     AtomMatcher atom_matcher;
-    atom_matcher.set_name(name);
+    atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
@@ -121,47 +121,47 @@
 
 Predicate CreateScreenIsOnPredicate() {
     Predicate predicate;
-    predicate.set_name("ScreenIsOn");
-    predicate.mutable_simple_predicate()->set_start("ScreenTurnedOn");
-    predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOff");
+    predicate.set_id(StringToId("ScreenIsOn"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff"));
     return predicate;
 }
 
 Predicate CreateScreenIsOffPredicate() {
     Predicate predicate;
-    predicate.set_name("ScreenIsOff");
-    predicate.mutable_simple_predicate()->set_start("ScreenTurnedOff");
-    predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOn");
+    predicate.set_id(StringToId("ScreenIsOff"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
     return predicate;
 }
 
 Predicate CreateHoldingWakelockPredicate() {
     Predicate predicate;
-    predicate.set_name("HoldingWakelock");
-    predicate.mutable_simple_predicate()->set_start("AcquireWakelock");
-    predicate.mutable_simple_predicate()->set_stop("ReleaseWakelock");
+    predicate.set_id(StringToId("HoldingWakelock"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock"));
     return predicate;
 }
 
 Predicate CreateIsSyncingPredicate() {
     Predicate predicate;
-    predicate.set_name("IsSyncing");
-    predicate.mutable_simple_predicate()->set_start("SyncStart");
-    predicate.mutable_simple_predicate()->set_stop("SyncEnd");
+    predicate.set_id(StringToId("IsSyncing"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
     return predicate;
 }
 
 Predicate CreateIsInBackgroundPredicate() {
     Predicate predicate;
-    predicate.set_name("IsInBackground");
-    predicate.mutable_simple_predicate()->set_start("MoveToBackground");
-    predicate.mutable_simple_predicate()->set_stop("MoveToForeground");
+    predicate.set_id(StringToId("IsInBackground"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground"));
     return predicate;
 }
 
 void addPredicateToPredicateCombination(const Predicate& predicate,
                                         Predicate* combinationPredicate) {
-    combinationPredicate->mutable_combination()->add_predicate(predicate.name());
+    combinationPredicate->mutable_combination()->add_predicate(predicate.id());
 }
 
 FieldMatcher CreateAttributionUidDimensions(const int atomId,
@@ -316,6 +316,9 @@
             });
 }
 
+int64_t StringToId(const string& str) {
+    return static_cast<int64_t>(std::hash<std::string>()(str));
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 282e1b2..5e19da0 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -121,6 +121,8 @@
 // Util function to sort the log events by timestamp.
 void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
 
+int64_t StringToId(const string& str);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index 7bd15d7..a4c0800 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -20,7 +20,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += ../../src/stats_log.proto \
-                   ../../src/atoms.proto
+                   ../../src/atoms.proto \
+                   ../../src/statsd_config.proto
 
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
index 0329992..c1c3914 100644
--- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
+++ b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
index 9294681..93dba71 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
@@ -26,7 +26,7 @@
         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())
+            sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getId())
                     .append("\n");
         }
 
@@ -34,7 +34,7 @@
             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("metric id: ").append(log.getMetricId()).append("\n");
                 sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
                 sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
 
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 137fd4d..4f9032f 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -34,6 +34,7 @@
 
 public class MainActivity extends Activity {
     private final static String TAG = "StatsdDogfood";
+    private final static long CONFIG_ID = 987654321;
 
     final int[] mUids = {11111111, 2222222};
     StatsManager mStatsManager;
@@ -163,7 +164,7 @@
                     return;
                 }
                 if (mStatsManager != null) {
-                    byte[] data = mStatsManager.getData("fake");
+                    byte[] data = mStatsManager.getData(CONFIG_ID);
                     if (data != null) {
                         displayData(data);
                     } else {
@@ -186,7 +187,7 @@
                     byte[] config = new byte[inputStream.available()];
                     inputStream.read(config);
                     if (mStatsManager != null) {
-                        if (mStatsManager.addConfiguration("fake",
+                        if (mStatsManager.addConfiguration(CONFIG_ID,
                                 config, getPackageName(), MainActivity.this.getClass().getName())) {
                             Toast.makeText(
                                     MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
index d2ff892..709b28b29 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
@@ -18,6 +18,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.util.Log;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import java.text.ParseException;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -26,9 +27,9 @@
     private static final String TAG = "loadtest.BatteryDataRecorder";
     private static final String DUMP_FILENAME = TAG + "_dump.tmp";
 
-    public BatteryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+    public BatteryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
         int burst) {
-        super(placebo, replication, bucketMins, periodSecs, burst);
+        super(placebo, replication, bucket, periodSecs, burst);
     }
 
     @Override
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
index f516477..b492ea9 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
@@ -31,6 +31,7 @@
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
 import com.android.internal.os.StatsdConfigProto.SimplePredicate;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 
 import java.io.InputStream;
 import java.io.IOException;
@@ -41,7 +42,7 @@
  * Creates StatsdConfig protos for loadtesting.
  */
 public class ConfigFactory {
-    public static final String CONFIG_NAME = "LOADTEST";
+    public static final long CONFIG_ID = 123456789;
 
     private static final String TAG = "loadtest.ConfigFactory";
 
@@ -82,11 +83,11 @@
      * @param placebo If true, only return an empty config
      * @return The serialized config
      */
-  public byte[] getConfig(int replication, long bucketMillis, boolean placebo, boolean includeCount,
+  public byte[] getConfig(int replication, TimeUnit bucket, boolean placebo, boolean includeCount,
                           boolean includeDuration, boolean includeEvent, boolean includeValue,
                           boolean includeGauge) {
         StatsdConfig.Builder config = StatsdConfig.newBuilder()
-            .setName(CONFIG_NAME);
+            .setId(CONFIG_ID);
         if (placebo) {
           replication = 0;  // Config will be empty, aside from a name.
         }
@@ -101,25 +102,25 @@
             }
             if (includeCount) {
                 for (CountMetric metric : mTemplate.getCountMetricList()) {
-                    addCountMetric(metric, i, bucketMillis, config);
+                    addCountMetric(metric, i, bucket, config);
                     numMetrics++;
                 }
             }
             if (includeDuration) {
                 for (DurationMetric metric : mTemplate.getDurationMetricList()) {
-                    addDurationMetric(metric, i, bucketMillis, config);
+                    addDurationMetric(metric, i, bucket, config);
                     numMetrics++;
                 }
             }
             if (includeGauge) {
                 for (GaugeMetric metric : mTemplate.getGaugeMetricList()) {
-                    addGaugeMetric(metric, i, bucketMillis, config);
+                    addGaugeMetric(metric, i, bucket, config);
                     numMetrics++;
                 }
             }
             if (includeValue) {
                 for (ValueMetric metric : mTemplate.getValueMetricList()) {
-                    addValueMetric(metric, i, bucketMillis, config);
+                    addValueMetric(metric, i, bucket, config);
                     numMetrics++;
                 }
             }
@@ -160,7 +161,7 @@
      */
     private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) {
         EventMetric.Builder metric = template.toBuilder()
-            .setName(template.getName() + suffix)
+            .setId(template.getId() + suffix)
             .setWhat(template.getWhat() + suffix);
         if (template.hasCondition()) {
             metric.setCondition(template.getCondition() + suffix);
@@ -173,20 +174,14 @@
         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,
+    private void addCountMetric(CountMetric template, int suffix, TimeUnit bucket,
         StatsdConfig.Builder config) {
         CountMetric.Builder metric = template.toBuilder()
-            .setName(template.getName() + suffix)
+            .setId(template.getId() + suffix)
             .setWhat(template.getWhat() + suffix);
         if (template.hasCondition()) {
             metric.setCondition(template.getCondition() + suffix);
@@ -196,7 +191,7 @@
             metric.clearLinks();
             metric.addAllLinks(links);
         }
-        metric.setBucket(getBucket(bucketMillis));
+        metric.setBucket(bucket);
         config.addCountMetric(metric);
     }
 
@@ -204,10 +199,10 @@
      * 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,
+    private void addDurationMetric(DurationMetric template, int suffix, TimeUnit bucket,
         StatsdConfig.Builder config) {
         DurationMetric.Builder metric = template.toBuilder()
-            .setName(template.getName() + suffix)
+            .setId(template.getId() + suffix)
             .setWhat(template.getWhat() + suffix);
         if (template.hasCondition()) {
             metric.setCondition(template.getCondition() + suffix);
@@ -217,7 +212,7 @@
             metric.clearLinks();
             metric.addAllLinks(links);
         }
-        metric.setBucket(getBucket(bucketMillis));
+        metric.setBucket(bucket);
         config.addDurationMetric(metric);
     }
 
@@ -225,10 +220,10 @@
      * 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,
+    private void addGaugeMetric(GaugeMetric template, int suffix, TimeUnit bucket,
         StatsdConfig.Builder config) {
         GaugeMetric.Builder metric = template.toBuilder()
-            .setName(template.getName() + suffix)
+            .setId(template.getId() + suffix)
             .setWhat(template.getWhat() + suffix);
         if (template.hasCondition()) {
             metric.setCondition(template.getCondition() + suffix);
@@ -238,7 +233,7 @@
             metric.clearLinks();
             metric.addAllLinks(links);
         }
-        metric.setBucket(getBucket(bucketMillis));
+        metric.setBucket(bucket);
         config.addGaugeMetric(metric);
     }
 
@@ -246,10 +241,10 @@
      * 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,
+    private void addValueMetric(ValueMetric template, int suffix, TimeUnit bucket,
         StatsdConfig.Builder config) {
         ValueMetric.Builder metric = template.toBuilder()
-            .setName(template.getName() + suffix)
+            .setId(template.getId() + suffix)
             .setWhat(template.getWhat() + suffix);
         if (template.hasCondition()) {
             metric.setCondition(template.getCondition() + suffix);
@@ -259,7 +254,7 @@
             metric.clearLinks();
             metric.addAllLinks(links);
         }
-        metric.setBucket(getBucket(bucketMillis));
+        metric.setBucket(bucket);
         config.addValueMetric(metric);
     }
 
@@ -269,11 +264,11 @@
      */
     private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) {
         Predicate.Builder predicate = template.toBuilder()
-            .setName(template.getName() + suffix);
+            .setId(template.getId() + suffix);
         if (template.hasCombination()) {
             Predicate.Combination.Builder cb = template.getCombination().toBuilder()
                 .clearPredicate();
-            for (String child : template.getCombination().getPredicateList()) {
+            for (long child : template.getCombination().getPredicateList()) {
                 cb.addPredicate(child + suffix);
             }
             predicate.setCombination(cb.build());
@@ -296,11 +291,11 @@
      */
     private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) {
         AtomMatcher.Builder matcher = template.toBuilder()
-            .setName(template.getName() + suffix);
+            .setId(template.getId() + suffix);
         if (template.hasCombination()) {
             AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder()
                 .clearMatcher();
-            for (String child : template.getCombination().getMatcherList()) {
+            for (long child : template.getCombination().getMatcherList()) {
                 cb.addMatcher(child + suffix);
             }
             matcher.setCombination(cb);
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
index ba43d57..19087d8 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
@@ -26,7 +26,7 @@
         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())
+            sb.append("\tuid: ").append(key.getUid()).append(" id: ").append(key.getId())
                     .append("\n");
         }
 
@@ -34,7 +34,7 @@
             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("metric id: ").append(log.getMetricId()).append("\n");
                 sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
                 sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
 
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
index 83f4b7b..056ac0c 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -47,6 +47,7 @@
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
 import com.android.os.StatsLog.StatsdStatsReport;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import java.util.List;
 
 /**
@@ -169,7 +170,7 @@
     private long mPeriodSecs;
 
     /** The bucket size, in minutes, for aggregate metrics. */
-    private long mBucketMins;
+    private TimeUnit mBucket;
 
     /** The duration, in minutes, of the loadtest. */
     private long mDurationMins;
@@ -294,7 +295,7 @@
             return null;
         }
         if (mStatsManager != null) {
-            byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_NAME);
+            byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_ID);
             if (data != null) {
                 ConfigMetricsReportList reports = null;
                 try {
@@ -360,7 +361,7 @@
         getData();
 
         // Create a config and push it to statsd.
-        if (!setConfig(mFactory.getConfig(mReplication, mBucketMins * 60 * 1000, mPlacebo,
+        if (!setConfig(mFactory.getConfig(mReplication, mBucket, mPlacebo,
                 mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric,
                 mIncludeValueMetric, mIncludeGaugeMetric))) {
             return;
@@ -377,7 +378,7 @@
         scheduleNext();
 
         // Start tracking performance.
-        mPerfData = new PerfData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
+        mPerfData = new PerfData(this, mPlacebo, mReplication, mBucket, mPeriodSecs, mBurst);
         mPerfData.startRecording(this);
 
         mReportText.setText("Loadtest in progress.");
@@ -453,7 +454,7 @@
         // TODO: Clear all configs instead of specific ones.
         if (mStatsManager != null) {
             if (mStarted) {
-                if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_NAME)) {
+                if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_ID)) {
                     Log.d(TAG, "Removed loadtest statsd configs.");
                 } else {
                     Log.d(TAG, "Failed to remove loadtest configs.");
@@ -464,7 +465,7 @@
 
     private boolean setConfig(byte[] config) {
       if (mStatsManager != null) {
-            if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_NAME,
+            if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID,
                 config, getPackageName(), LoadtestActivity.this.getClass().getName())) {
                 Log.d(TAG, "Config pushed to statsd");
                 return true;
@@ -483,8 +484,8 @@
         mPeriodSecs = periodSecs;
     }
 
-    private synchronized void setBucketMins(long bucketMins) {
-        mBucketMins = bucketMins;
+    private synchronized void setBucket(TimeUnit bucket) {
+        mBucket = bucket;
     }
 
     private synchronized void setBurst(int burst) {
@@ -536,12 +537,12 @@
     }
 
     private void initBucket() {
-        mBucketMins = getResources().getInteger(R.integer.bucket_default);
+        mBucket = TimeUnit.valueOf(getResources().getInteger(R.integer.bucket_default));
         mBucketText = (EditText) findViewById(R.id.bucket);
-        mBucketText.addTextChangedListener(new NumericalWatcher(mBucketText, 1, 24 * 60) {
+        mBucketText.addTextChangedListener(new NumericalWatcher(mBucketText, 1, 9) {
             @Override
             public void onNewValue(int newValue) {
-                setBucketMins(newValue);
+                setBucket(TimeUnit.valueOf(newValue));
             }
         });
         handleFocus(mBucketText);
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
index d9513a1..66bcbff 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.os.SystemClock;
 import android.util.Log;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 
 public class MemoryDataRecorder extends PerfDataRecorder {
     private static final String TAG = "loadtest.MemoryDataDataRecorder";
@@ -26,9 +27,9 @@
     private long mStartTimeMillis;
     private StringBuilder mSb;
 
-    public MemoryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+    public MemoryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
         int burst) {
-        super(placebo, replication, bucketMins, periodSecs, burst);
+        super(placebo, replication, bucket, periodSecs, burst);
     }
 
     @Override
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
index 22ba9c5..4b4e368 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
@@ -15,6 +15,8 @@
  */
 package com.android.statsd.loadtest;
 
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
+
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -52,14 +54,14 @@
     private final Set<PerfDataRecorder> mRecorders;
 
     public PerfData(LoadtestActivity loadtestActivity, boolean placebo, int replication,
-        long bucketMins, long periodSecs,  int burst) {
-        super(placebo, replication, bucketMins, periodSecs, burst);
+        TimeUnit bucket, long periodSecs,  int burst) {
+        super(placebo, replication, bucket, periodSecs, burst);
         mRecorders = new HashSet();
-        mRecorders.add(new BatteryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
-        mRecorders.add(new MemoryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
-        mRecorders.add(new StatsdStatsRecorder(loadtestActivity, placebo, replication, bucketMins,
+        mRecorders.add(new BatteryDataRecorder(placebo, replication, bucket, periodSecs, burst));
+        mRecorders.add(new MemoryDataRecorder(placebo, replication, bucket, periodSecs, burst));
+        mRecorders.add(new StatsdStatsRecorder(loadtestActivity, placebo, replication, bucket,
                 periodSecs, burst));
-        mRecorders.add(new ValidationRecorder(loadtestActivity, placebo, replication, bucketMins,
+        mRecorders.add(new ValidationRecorder(loadtestActivity, placebo, replication, bucket,
                 periodSecs, burst));
         mAlarmMgr = (AlarmManager) loadtestActivity.getSystemService(Context.ALARM_SERVICE);
     }
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
index 5b5ba37..fd182ad 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
@@ -21,6 +21,7 @@
 import android.util.Log;
 import android.os.Debug;
 
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import java.io.BufferedReader;
 import java.io.Closeable;
 import java.io.File;
@@ -38,10 +39,10 @@
     protected final String mTimeAsString;
     protected final String mColumnSuffix;
 
-    protected PerfDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+    protected PerfDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
         int burst) {
         mTimeAsString = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
-        mColumnSuffix = getColumnSuffix(placebo, replication, bucketMins, periodSecs, burst);
+        mColumnSuffix = getColumnSuffix(placebo, replication, bucket, periodSecs, burst);
     }
 
     /** Starts recording performance data. */
@@ -120,12 +121,12 @@
     }
 
     /** Gets the suffix to use in the column name for perf data. */
-    private String getColumnSuffix(boolean placebo, int replication, long bucketMins,
+    private String getColumnSuffix(boolean placebo, int replication, TimeUnit bucket,
         long periodSecs, int burst) {
         if (placebo) {
             return "_placebo_p=" + periodSecs;
         }
-        return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
+        return "_r=" + replication + "_bkt=" + bucket + "_p=" + periodSecs + "_bst=" + burst;
     }
 
 
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
index 4ef5dc2..1e30fdf 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.util.Log;
 import com.android.os.StatsLog.StatsdStatsReport;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -27,8 +28,8 @@
     private final LoadtestActivity mLoadtestActivity;
 
     public StatsdStatsRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
-        long bucketMins, long periodSecs, int burst) {
-        super(placebo, replication, bucketMins, periodSecs, burst);
+            TimeUnit bucket, long periodSecs, int burst) {
+        super(placebo, replication, bucket, periodSecs, burst);
         mLoadtestActivity = loadtestActivity;
     }
 
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
index 4b614aa..5d26be3 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
@@ -20,6 +20,7 @@
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.EventMetricData;
 import com.android.os.StatsLog.StatsLogReport;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -33,8 +34,8 @@
     private final LoadtestActivity mLoadtestActivity;
 
     public ValidationRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
-        long bucketMins, long periodSecs, int burst) {
-        super(placebo, replication, bucketMins, periodSecs, burst);
+            TimeUnit bucket, long periodSecs, int burst) {
+        super(placebo, replication, bucket, periodSecs, burst);
         mLoadtestActivity = loadtestActivity;
     }
 
@@ -59,18 +60,8 @@
             Log.d(TAG, "GOT DATA");
             for (ConfigMetricsReport report : reports) {
                 for (StatsLogReport logReport : report.getMetricsList()) {
-                    if (!logReport.hasMetricName()) {
+                    if (!logReport.hasMetricId()) {
                         Log.e(TAG, "Metric missing name.");
-                        continue;
-                    }
-                    String metricName = logReport.getMetricName();
-                    if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_WHILE_SCREEN_IS_ON_")) {
-                        validateEventBatteryLevelChangesWhileScreenIsOn(logReport);
-                        continue;
-                    }
-                    if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_")) {
-                        validateEventBatteryLevelChanges(logReport);
-                        continue;
                     }
                 }
             }
@@ -78,7 +69,7 @@
     }
 
     private void validateEventBatteryLevelChanges(StatsLogReport logReport) {
-        Log.d(TAG, "Validating " + logReport.getMetricName());
+        Log.d(TAG, "Validating " + logReport.getMetricId());
         if (logReport.hasEventMetrics()) {
             Log.d(TAG, "Num events captured: " + logReport.getEventMetrics().getDataCount());
             for (EventMetricData data : logReport.getEventMetrics().getDataList()) {
@@ -90,6 +81,6 @@
     }
 
     private void validateEventBatteryLevelChangesWhileScreenIsOn(StatsLogReport logReport) {
-        Log.d(TAG, "Validating " + logReport.getMetricName());
+        Log.d(TAG, "Validating " + logReport.getMetricId());
     }
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index aaa6bf0..de346f3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1768,9 +1768,7 @@
                             (String[]) ((SomeArgs) msg.obj).arg2);
                     break;
                 case EXECUTE_TRANSACTION:
-                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
-                    mTransactionExecutor.execute(transaction);
-                    transaction.recycle();
+                    mTransactionExecutor.execute(((ClientTransaction) msg.obj));
                     break;
             }
             Object obj = msg.obj;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8641a21..82f2bac 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -64,7 +64,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
-import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -1683,22 +1682,6 @@
     }
 
     @Override
-    public void installPackage(Uri packageURI,
-            PackageInstallObserver observer, int flags, String installerPackageName) {
-        if (!"file".equals(packageURI.getScheme())) {
-            throw new UnsupportedOperationException("Only file:// URIs are supported");
-        }
-
-        final String originPath = packageURI.getPath();
-        try {
-            mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
-                    mContext.getUserId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @Override
     public int installExistingPackage(String packageName) throws NameNotFoundException {
         return installExistingPackage(packageName, PackageManager.INSTALL_REASON_UNKNOWN);
     }
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 80782e3..5ef4be1 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -159,5 +159,5 @@
     /**
      * Called from SystemUI when it shows the AoD UI.
      */
-    void setInAmbientMode(boolean inAmbienMode);
+    oneway void setInAmbientMode(boolean inAmbientMode, boolean animated);
 }
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 6dca400..8c47598 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -50,11 +50,6 @@
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 final class SharedPreferencesImpl implements SharedPreferences {
     private static final String TAG = "SharedPreferencesImpl";
@@ -74,12 +69,16 @@
     private final Object mLock = new Object();
     private final Object mWritingToDiskLock = new Object();
 
-    private Future<Map<String, Object>> mMap;
+    @GuardedBy("mLock")
+    private Map<String, Object> mMap;
 
     @GuardedBy("mLock")
     private int mDiskWritesInFlight = 0;
 
     @GuardedBy("mLock")
+    private boolean mLoaded = false;
+
+    @GuardedBy("mLock")
     private StructTimespec mStatTimestamp;
 
     @GuardedBy("mLock")
@@ -106,18 +105,27 @@
         mFile = file;
         mBackupFile = makeBackupFile(file);
         mMode = mode;
+        mLoaded = false;
         mMap = null;
         startLoadFromDisk();
     }
 
     private void startLoadFromDisk() {
-        FutureTask<Map<String, Object>> futureTask = new FutureTask<>(() -> loadFromDisk());
-        mMap = futureTask;
-        new Thread(futureTask, "SharedPreferencesImpl-load").start();
+        synchronized (mLock) {
+            mLoaded = false;
+        }
+        new Thread("SharedPreferencesImpl-load") {
+            public void run() {
+                loadFromDisk();
+            }
+        }.start();
     }
 
-    private Map<String, Object> loadFromDisk() {
+    private void loadFromDisk() {
         synchronized (mLock) {
+            if (mLoaded) {
+                return;
+            }
             if (mBackupFile.exists()) {
                 mFile.delete();
                 mBackupFile.renameTo(mFile);
@@ -150,14 +158,16 @@
         }
 
         synchronized (mLock) {
+            mLoaded = true;
             if (map != null) {
+                mMap = map;
                 mStatTimestamp = stat.st_mtim;
                 mStatSize = stat.st_size;
             } else {
-                map = new HashMap<>();
+                mMap = new HashMap<>();
             }
+            mLock.notifyAll();
         }
-        return map;
     }
 
     static File makeBackupFile(File prefsFile) {
@@ -216,42 +226,36 @@
         }
     }
 
-    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()) {
+    private void awaitLoadedLocked() {
+        if (!mLoaded) {
             // Raise an explicit StrictMode onReadFromDisk for this
             // thread, since the real read will be in a different
             // thread and otherwise ignored by StrictMode.
             BlockGuard.getThreadPolicy().onReadFromDisk();
         }
-        return getLoaded();
+        while (!mLoaded) {
+            try {
+                mLock.wait();
+            } catch (InterruptedException unused) {
+            }
+        }
     }
 
     @Override
     public Map<String, ?> getAll() {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            return new HashMap<String, Object>(map);
+            awaitLoadedLocked();
+            //noinspection unchecked
+            return new HashMap<String, Object>(mMap);
         }
     }
 
     @Override
     @Nullable
     public String getString(String key, @Nullable String defValue) {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            String v = (String) map.get(key);
+            awaitLoadedLocked();
+            String v = (String)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
@@ -259,65 +263,66 @@
     @Override
     @Nullable
     public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            @SuppressWarnings("unchecked")
-            Set<String> v = (Set<String>) map.get(key);
+            awaitLoadedLocked();
+            Set<String> v = (Set<String>) mMap.get(key);
             return v != null ? v : defValues;
         }
     }
 
     @Override
     public int getInt(String key, int defValue) {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            Integer v = (Integer) map.get(key);
+            awaitLoadedLocked();
+            Integer v = (Integer)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public long getLong(String key, long defValue) {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            Long v = (Long) map.get(key);
+            awaitLoadedLocked();
+            Long v = (Long)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public float getFloat(String key, float defValue) {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            Float v = (Float) map.get(key);
+            awaitLoadedLocked();
+            Float v = (Float)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     @Override
     public boolean getBoolean(String key, boolean defValue) {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            Boolean v = (Boolean) map.get(key);
+            awaitLoadedLocked();
+            Boolean v = (Boolean)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
 
     @Override
     public boolean contains(String key) {
-        Map<String, Object> map = getLoadedWithBlockGuard();
         synchronized (mLock) {
-            return map.containsKey(key);
+            awaitLoadedLocked();
+            return mMap.containsKey(key);
         }
     }
 
     @Override
     public Editor edit() {
-        // TODO: remove the need to call getLoaded() when
+        // TODO: remove the need to call awaitLoadedLocked() when
         // requesting an editor.  will require some work on the
         // Editor, but then we should be able to do:
         //
         //      context.getSharedPreferences(..).edit().putString(..).apply()
         //
         // ... all without blocking.
-        getLoadedWithBlockGuard();
+        synchronized (mLock) {
+            awaitLoadedLocked();
+        }
 
         return new EditorImpl();
     }
@@ -471,43 +476,13 @@
                 // a memory commit comes in when we're already
                 // writing to disk.
                 if (mDiskWritesInFlight > 0) {
-                    // We can't modify our map as a currently
+                    // We can't modify our mMap as a currently
                     // in-flight write owns it.  Clone it before
                     // modifying it.
                     // noinspection unchecked
-                    mMap = new Future<Map<String, Object>>() {
-                        private Map<String, Object> mCopiedMap =
-                                new HashMap<String, Object>(getLoaded());
-
-                        @Override
-                        public boolean cancel(boolean mayInterruptIfRunning) {
-                            return false;
-                        }
-
-                        @Override
-                        public boolean isCancelled() {
-                            return false;
-                        }
-
-                        @Override
-                        public boolean isDone() {
-                            return true;
-                        }
-
-                        @Override
-                        public Map<String, Object> get()
-                                throws InterruptedException, ExecutionException {
-                            return mCopiedMap;
-                        }
-
-                        @Override
-                        public Map<String, Object> get(long timeout, TimeUnit unit)
-                                throws InterruptedException, ExecutionException, TimeoutException {
-                            return mCopiedMap;
-                        }
-                    };
+                    mMap = new HashMap<String, Object>(mMap);
                 }
-                mapToWriteToDisk = getLoaded();
+                mapToWriteToDisk = mMap;
                 mDiskWritesInFlight++;
 
                 boolean hasListeners = mListeners.size() > 0;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 66cf991..6eb4783 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -494,7 +494,7 @@
         registerService(Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class,
                 new CachedServiceFetcher<SubscriptionManager>() {
             @Override
-            public SubscriptionManager createService(ContextImpl ctx) {
+            public SubscriptionManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 return new SubscriptionManager(ctx.getOuterContext());
             }});
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7e80ac7..0b74741 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1668,6 +1668,46 @@
     public static final String ACTION_DEVICE_ADMIN_SERVICE
             = "android.app.action.DEVICE_ADMIN_SERVICE";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"ID_TYPE_"}, value = {
+        ID_TYPE_BASE_INFO,
+        ID_TYPE_SERIAL,
+        ID_TYPE_IMEI,
+        ID_TYPE_MEID
+    })
+    public @interface AttestationIdType {}
+
+    /**
+     * Specifies that the device should attest its manufacturer details. For use with
+     * {@link #generateKeyPair}.
+     *
+     * @see #generateKeyPair
+     */
+    public static final int ID_TYPE_BASE_INFO = 1;
+
+    /**
+     * Specifies that the device should attest its serial number. For use with
+     * {@link #generateKeyPair}.
+     *
+     * @see #generateKeyPair
+     */
+    public static final int ID_TYPE_SERIAL = 2;
+
+    /**
+     * Specifies that the device should attest its IMEI. For use with {@link #generateKeyPair}.
+     *
+     * @see #generateKeyPair
+     */
+    public static final int ID_TYPE_IMEI = 4;
+
+    /**
+     * Specifies that the device should attest its MEID. For use with {@link #generateKeyPair}.
+     *
+     * @see #generateKeyPair
+     */
+    public static final int ID_TYPE_MEID = 8;
+
     /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
@@ -4106,22 +4146,46 @@
      * @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}.
      * @param keySpec Specification of the key to generate, see
      * {@link java.security.KeyPairGenerator}.
+     * @param idAttestationFlags A bitmask of all the identifiers that should be included in the
+     *        attestation record ({@code ID_TYPE_BASE_INFO}, {@code ID_TYPE_SERIAL},
+     *        {@code ID_TYPE_IMEI} and {@code ID_TYPE_MEID}), or {@code 0} if no device
+     *        identification is required in the attestation record.
+     *        Device owner, profile owner and their delegated certificate installer can use
+     *        {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device information
+     *        including manufacturer, model, brand, device and product in the attestation record.
+     *        Only device owner and their delegated certificate installer can use
+     *        {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID} to request
+     *        unique device identifiers to be attested.
+     *        <p>
+     *        If any of {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID}
+     *        is set, it is implicitly assumed that {@link #ID_TYPE_BASE_INFO} is also set.
+     *        <p>
+     *        If any flag is specified, then an attestation challenge must be included in the
+     *        {@code keySpec}.
      * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
-     *         owner.
-     * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, or if the
+     *         owner. If Device ID attestation is requested (using {@link #ID_TYPE_SERIAL},
+     *         {@link #ID_TYPE_IMEI} or {@link #ID_TYPE_MEID}), the caller must be the Device Owner
+     *         or the Certificate Installer delegate.
+     * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, if the
      *         algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
-     *         or {@code ECGenParameterSpec}.
+     *         or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the
+     *         {@code keySpec} does not contain an attestation challenge.
+     * @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
      */
     public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
-            @NonNull String algorithm, @NonNull KeyGenParameterSpec keySpec) {
+            @NonNull String algorithm, @NonNull KeyGenParameterSpec keySpec,
+            @AttestationIdType int idAttestationFlags) {
         throwIfParentInstance("generateKeyPair");
         try {
             final ParcelableKeyGenParameterSpec parcelableSpec =
                     new ParcelableKeyGenParameterSpec(keySpec);
             KeymasterCertificateChain attestationChain = new KeymasterCertificateChain();
+
+            // Translate ID attestation flags to values used by AttestationUtils
             final boolean success = mService.generateKeyPair(
-                    admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain);
+                    admin, mContext.getPackageName(), algorithm, parcelableSpec,
+                    idAttestationFlags, attestationChain);
             if (!success) {
                 Log.e(TAG, "Error generating key via DevicePolicyManagerService.");
                 return null;
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7cf19ee..5916a62 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -173,7 +173,7 @@
     boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
     boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm,
             in ParcelableKeyGenParameterSpec keySpec,
-            out KeymasterCertificateChain attestationChain);
+            in int idAttestationFlags, out KeymasterCertificateChain attestationChain);
     boolean setKeyPairCertificate(in ComponentName who, in String callerPackage, in String alias,
             in byte[] certBuffer, in byte[] certChainBuffer, boolean isUserSelectable);
     void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7b549cd5..87f2271 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -32,6 +32,8 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -624,6 +626,7 @@
         int mMinEms = -1;
         int mMaxEms = -1;
         int mMaxLength = -1;
+        @Nullable String mTextIdEntry;
 
         // POJO used to override some autofill-related values when the node is parcelized.
         // Not written to parcel.
@@ -701,7 +704,7 @@
             final int flags = mFlags;
             if ((flags&FLAGS_HAS_ID) != 0) {
                 mId = in.readInt();
-                if (mId != 0) {
+                if (mId != View.NO_ID) {
                     mIdEntry = preader.readString();
                     if (mIdEntry != null) {
                         mIdType = preader.readString();
@@ -724,6 +727,7 @@
                 mMinEms = in.readInt();
                 mMaxEms = in.readInt();
                 mMaxLength = in.readInt();
+                mTextIdEntry = preader.readString();
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 mX = in.readInt();
@@ -857,7 +861,7 @@
             out.writeInt(writtenFlags);
             if ((flags&FLAGS_HAS_ID) != 0) {
                 out.writeInt(mId);
-                if (mId != 0) {
+                if (mId != View.NO_ID) {
                     pwriter.writeString(mIdEntry);
                     if (mIdEntry != null) {
                         pwriter.writeString(mIdType);
@@ -890,6 +894,7 @@
                 out.writeInt(mMinEms);
                 out.writeInt(mMaxEms);
                 out.writeInt(mMaxLength);
+                pwriter.writeString(mTextIdEntry);
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 out.writeInt(mX);
@@ -1430,6 +1435,17 @@
         }
 
         /**
+         * Gets the identifier used to set the text associated with this view.
+         *
+         * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
+         * not for assist purposes.
+         */
+        @Nullable
+        public String getTextIdEntry() {
+            return mTextIdEntry;
+        }
+
+        /**
          * Return additional hint text associated with the node; this is typically used with
          * a node that takes user input, describing to the user what the input means.
          */
@@ -1684,6 +1700,11 @@
         }
 
         @Override
+        public void setTextIdEntry(@NonNull String entryName) {
+            mNode.mTextIdEntry = Preconditions.checkNotNull(entryName);
+        }
+
+        @Override
         public void setHint(CharSequence hint) {
             getNodeText().mHint = hint != null ? hint.toString() : null;
         }
@@ -2082,6 +2103,7 @@
             Log.i(TAG, prefix + "  Text color fg: #" + Integer.toHexString(node.getTextColor())
                     + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
             Log.i(TAG, prefix + "  Input type: " + node.getInputType());
+            Log.i(TAG, prefix + "  Resource id: " + node.getTextIdEntry());
         }
         String webDomain = node.getWebDomain();
         if (webDomain != null) {
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 3c96f06..764ceed 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -54,11 +54,6 @@
     /** Target client activity. Might be null if the entire transaction is targeting an app. */
     private IBinder mActivityToken;
 
-    /** Get the target client of the transaction. */
-    public IApplicationThread getClient() {
-        return mClient;
-    }
-
     /**
      * Add a message to the end of the sequence of callbacks.
      * @param activityCallback A single message that can contain a lifecycle request/callback.
diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java
index 2fec30a..9812125 100644
--- a/core/java/android/app/servertransaction/ObjectPool.java
+++ b/core/java/android/app/servertransaction/ObjectPool.java
@@ -16,8 +16,8 @@
 
 package android.app.servertransaction;
 
-import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.Map;
 
 /**
@@ -27,7 +27,7 @@
 class ObjectPool {
 
     private static final Object sPoolSync = new Object();
-    private static final Map<Class, ArrayList<? extends ObjectPoolItem>> sPoolMap =
+    private static final Map<Class, LinkedList<? extends ObjectPoolItem>> sPoolMap =
             new HashMap<>();
 
     private static final int MAX_POOL_SIZE = 50;
@@ -40,9 +40,9 @@
     public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) {
         synchronized (sPoolSync) {
             @SuppressWarnings("unchecked")
-            final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass);
+            LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(itemClass);
             if (itemPool != null && !itemPool.isEmpty()) {
-                return itemPool.remove(itemPool.size() - 1);
+                return itemPool.poll();
             }
             return null;
         }
@@ -56,20 +56,16 @@
     public static <T extends ObjectPoolItem> void recycle(T item) {
         synchronized (sPoolSync) {
             @SuppressWarnings("unchecked")
-            ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass());
+            LinkedList<T> itemPool = (LinkedList<T>) sPoolMap.get(item.getClass());
             if (itemPool == null) {
-                itemPool = new ArrayList<>();
+                itemPool = new LinkedList<>();
                 sPoolMap.put(item.getClass(), itemPool);
             }
-            // Check if the item is already in the pool
-            final int size = itemPool.size();
-            for (int i = 0; i < size; i++) {
-                if (itemPool.get(i) == item) {
-                    throw new IllegalStateException("Trying to recycle already recycled item");
-                }
+            if (itemPool.contains(item)) {
+                throw new IllegalStateException("Trying to recycle already recycled item");
             }
 
-            if (size < MAX_POOL_SIZE) {
+            if (itemPool.size() < MAX_POOL_SIZE) {
                 itemPool.add(item);
             }
         }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 5c7f674..b8fb2e3 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -184,6 +184,10 @@
      * Subtype to tag an item representing priority.
      */
     public static final String SUBTYPE_PRIORITY = "priority";
+    /**
+     * Subtype to tag an item to use as a content description.
+     */
+    public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description";
 
     private final SliceItem[] mItems;
     private final @SliceHint String[] mHints;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 56a0def..e319750 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -26,7 +26,6 @@
 import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
-import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
@@ -222,13 +221,6 @@
     ParceledListSlice queryInstrumentation(
             String targetPackage, int flags);
 
-    /** @deprecated Use PackageInstaller instead */
-    void installPackageAsUser(in String originPath,
-            in IPackageInstallObserver2 observer,
-            int flags,
-            in String installerPackageName,
-            int userId);
-
     void finishPackageInstall(int token, boolean didLaunch);
 
     void setInstallerPackageName(in String targetPackage, in String installerPackageName);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 77c5743..df677d2 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -324,7 +324,14 @@
      */
     public int createSession(@NonNull SessionParams params) throws IOException {
         try {
-            return mInstaller.createSession(params, mInstallerPackageName, mUserId);
+            final String installerPackage;
+            if (params.installerPackageName == null) {
+                installerPackage = mInstallerPackageName;
+            } else {
+                installerPackage = params.installerPackageName;
+            }
+
+            return mInstaller.createSession(params, installerPackage, mUserId);
         } catch (RuntimeException e) {
             ExceptionUtils.maybeUnwrapIOException(e);
             throw e;
@@ -1081,6 +1088,8 @@
         public String volumeUuid;
         /** {@hide} */
         public String[] grantedRuntimePermissions;
+        /** {@hide} */
+        public String installerPackageName;
 
         /**
          * Construct parameters for a new package install session.
@@ -1109,6 +1118,7 @@
             abiOverride = source.readString();
             volumeUuid = source.readString();
             grantedRuntimePermissions = source.readStringArray();
+            installerPackageName = source.readString();
         }
 
         /**
@@ -1304,6 +1314,18 @@
             }
         }
 
+        /**
+         * Set the installer package for the app.
+         *
+         * By default this is the app that created the {@link PackageInstaller} object.
+         *
+         * @param installerPackageName name of the installer package
+         * {@hide}
+         */
+        public void setInstallerPackageName(String installerPackageName) {
+            this.installerPackageName = installerPackageName;
+        }
+
         /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
@@ -1319,6 +1341,7 @@
             pw.printPair("abiOverride", abiOverride);
             pw.printPair("volumeUuid", volumeUuid);
             pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
+            pw.printPair("installerPackageName", installerPackageName);
             pw.println();
         }
 
@@ -1343,6 +1366,7 @@
             dest.writeString(abiOverride);
             dest.writeString(volumeUuid);
             dest.writeStringArray(grantedRuntimePermissions);
+            dest.writeString(installerPackageName);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2d72632..5f82c2a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,7 +47,6 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -2480,10 +2479,17 @@
             = "android.software.securely_removes_users";
 
     /** {@hide} */
+    @TestApi
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_FILE_BASED_ENCRYPTION
             = "android.software.file_based_encryption";
 
+    /** {@hide} */
+    @TestApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_ADOPTABLE_STORAGE
+            = "android.software.adoptable_storage";
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device has a full implementation of the android.webkit.* APIs. Devices
@@ -4718,17 +4724,6 @@
     }
 
     /**
-     * @deprecated replaced by {@link PackageInstaller}
-     * @hide
-     */
-    @Deprecated
-    public abstract void installPackage(
-            Uri packageURI,
-            PackageInstallObserver observer,
-            @InstallFlags int flags,
-            String installerPackageName);
-
-    /**
      * If there is already an application with the given package name installed
      * on the system for other users, also install it for the calling user.
      * @hide
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 8ee8e10..2c45b8d 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -119,6 +119,12 @@
     public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider);
 
     /**
+     * Sets the Use Open Wifi packages provider.
+     * @param provider The packages provider.
+     */
+    public abstract void setUseOpenWifiAppPackagesProvider(PackagesProvider provider);
+
+    /**
      * Sets the sync adapter packages provider.
      * @param provider The provider.
      */
@@ -147,6 +153,14 @@
             int userId);
 
     /**
+     * Requests granting of the default permissions to the current default Use Open Wifi app.
+     * @param packageName The default use open wifi package name.
+     * @param userId The user for which to grant the permissions.
+     */
+    public abstract void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName,
+            int userId);
+
+    /**
      * Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has
      * currently installed it. The apps are not preloaded.
      * @param packageList List of package names to keep cached.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 57ab18e..5a63896 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1642,8 +1642,9 @@
      * lifetime. Typical examples include parameters that require a
      * time-consuming hardware re-configuration or internal camera pipeline
      * change. For performance reasons we advise clients to pass their initial
-     * values as part of {@link SessionConfiguration#setSessionParameters }. Once
-     * the camera capture session is enabled it is also recommended to avoid
+     * values as part of
+     * {@link SessionConfiguration#setSessionParameters }.i
+     * Once the camera capture session is enabled it is also recommended to avoid
      * changing them from their initial values set in
      * {@link SessionConfiguration#setSessionParameters }.
      * Control over session parameters can still be exerted in capture requests
@@ -1653,15 +1654,18 @@
      * <li>The camera client starts by quering the session parameter key list via
      *   {@link android.hardware.camera2.CameraCharacteristics#getAvailableSessionKeys }.</li>
      * <li>Before triggering the capture session create sequence, a capture request
-     *   must be built via {@link CameraDevice#createCaptureRequest } using an
-     *   appropriate template matching the particular use case.</li>
+     *   must be built via
+     *   {@link CameraDevice#createCaptureRequest }
+     *   using an appropriate template matching the particular use case.</li>
      * <li>The client should go over the list of session parameters and check
      *   whether some of the keys listed matches with the parameters that
      *   they intend to modify as part of the first capture request.</li>
      * <li>If there is no such match, the capture request can be  passed
-     *   unmodified to {@link SessionConfiguration#setSessionParameters }.</li>
+     *   unmodified to
+     *   {@link SessionConfiguration#setSessionParameters }.</li>
      * <li>If matches do exist, the client should update the respective values
-     *   and pass the request to {@link SessionConfiguration#setSessionParameters }.</li>
+     *   and pass the request to
+     *   {@link SessionConfiguration#setSessionParameters }.</li>
      * <li>After the capture session initialization completes the session parameter
      *   key list can continue to serve as reference when posting or updating
      *   further requests. As mentioned above further changes to session
@@ -2972,6 +2976,18 @@
             new Key<Integer>("android.info.supportedHardwareLevel", int.class);
 
     /**
+     * <p>A short string for manufacturer version information about the camera device, such as
+     * ISP hardware, sensors, etc.</p>
+     * <p>This can be used in {@link android.media.ExifInterface#TAG_IMAGE_DESCRIPTION TAG_IMAGE_DESCRIPTION}
+     * in jpeg EXIF. This key may be absent if no version information is available on the
+     * device.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    public static final Key<String> INFO_VERSION =
+            new Key<String>("android.info.version", String.class);
+
+    /**
      * <p>The maximum number of frames that can occur after a request
      * (different than the previous) has been submitted, and before the
      * result's state becomes synchronized.</p>
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 3003607..0a08353 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -28,7 +28,7 @@
  */
 public final class BrightnessChangeEvent implements Parcelable {
     /** Brightness in nits */
-    public int brightness;
+    public float brightness;
 
     /** Timestamp of the change {@see System.currentTimeMillis()} */
     public long timeStamp;
@@ -58,7 +58,7 @@
     public int colorTemperature;
 
     /** Brightness level before slider adjustment */
-    public int lastBrightness;
+    public float lastBrightness;
 
     public BrightnessChangeEvent() {
     }
@@ -78,7 +78,7 @@
     }
 
     private BrightnessChangeEvent(Parcel source) {
-        brightness = source.readInt();
+        brightness = source.readFloat();
         timeStamp = source.readLong();
         packageName = source.readString();
         userId = source.readInt();
@@ -87,7 +87,7 @@
         batteryLevel = source.readFloat();
         nightMode = source.readBoolean();
         colorTemperature = source.readInt();
-        lastBrightness = source.readInt();
+        lastBrightness = source.readFloat();
     }
 
     public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -107,7 +107,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(brightness);
+        dest.writeFloat(brightness);
         dest.writeLong(timeStamp);
         dest.writeString(packageName);
         dest.writeInt(userId);
@@ -116,6 +116,6 @@
         dest.writeFloat(batteryLevel);
         dest.writeBoolean(nightMode);
         dest.writeInt(colorTemperature);
-        dest.writeInt(lastBrightness);
+        dest.writeFloat(lastBrightness);
     }
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 7de667d..97e9b9c 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -628,13 +628,6 @@
     }
 
     /**
-     * @hide STOPSHIP - remove when adaptive brightness accepts curves.
-     */
-    public void setBrightness(int brightness) {
-        mGlobal.setBrightness(brightness);
-    }
-
-    /**
      * Sets the global display brightness configuration.
      *
      * @hide
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index bf4cc1d..cbb5a7d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -476,18 +476,6 @@
     }
 
     /**
-     * Set brightness but don't add a BrightnessChangeEvent
-     * STOPSHIP remove when adaptive brightness accepts curves.
-     */
-    public void setBrightness(int brightness) {
-        try {
-            mDm.setBrightness(brightness);
-        } catch (RemoteException ex) {
-            throw ex.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Sets the global brightness configuration for a given user.
      *
      * @hide
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index cd551bd..3f6dd2e 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -222,6 +222,11 @@
         // set by the user as opposed to being programmatically controlled by apps.
         public boolean brightnessSetByUser;
 
+        // Set to true if screenBrightness or screenAutoBrightnessAdjustment are being set
+        // temporarily. This is typically set while the user has their finger on the brightness
+        // control, before they've selected the final brightness value.
+        public boolean brightnessIsTemporary;
+
         // If true, enables automatic brightness control.
         public boolean useAutoBrightness;
 
@@ -280,6 +285,7 @@
             screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
             screenLowPowerBrightnessFactor = other.screenLowPowerBrightnessFactor;
             brightnessSetByUser = other.brightnessSetByUser;
+            brightnessIsTemporary = other.brightnessIsTemporary;
             useAutoBrightness = other.useAutoBrightness;
             blockScreenOn = other.blockScreenOn;
             lowPowerMode = other.lowPowerMode;
@@ -303,6 +309,7 @@
                     && screenLowPowerBrightnessFactor
                     == other.screenLowPowerBrightnessFactor
                     && brightnessSetByUser == other.brightnessSetByUser
+                    && brightnessIsTemporary == other.brightnessIsTemporary
                     && useAutoBrightness == other.useAutoBrightness
                     && blockScreenOn == other.blockScreenOn
                     && lowPowerMode == other.lowPowerMode
@@ -324,6 +331,7 @@
                     + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
                     + ", screenLowPowerBrightnessFactor=" + screenLowPowerBrightnessFactor
                     + ", brightnessSetByUser=" + brightnessSetByUser
+                    + ", brightnessIsTemporary=" + brightnessIsTemporary
                     + ", useAutoBrightness=" + useAutoBrightness
                     + ", blockScreenOn=" + blockScreenOn
                     + ", lowPowerMode=" + lowPowerMode
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 8afae6e..61c42e1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -87,10 +87,6 @@
     // Requires BRIGHTNESS_SLIDER_USAGE permission.
     ParceledListSlice getBrightnessEvents(String callingPackage);
 
-    // STOPSHIP remove when adaptive brightness code is updated to accept curves.
-    // Requires BRIGHTNESS_SLIDER_USAGE permission.
-    void setBrightness(int brightness);
-
     // Sets the global brightness configuration for a given user. Requires
     // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not
     // the same as the calling user.
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 52527ed..0a21083 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -15,9 +15,13 @@
  */
 package android.hardware.location;
 
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.os.RemoteException;
 
+import com.android.internal.util.Preconditions;
+
 import dalvik.system.CloseGuard;
 
 import java.io.Closeable;
@@ -31,16 +35,12 @@
  *
  * @hide
  */
+@SystemApi
 public class ContextHubClient implements Closeable {
     /*
      * The proxy to the client interface at the service.
      */
-    private final IContextHubClient mClientProxy;
-
-    /*
-     * The callback interface associated with this client.
-     */
-    private final IContextHubClientCallback mCallbackInterface;
+    private IContextHubClient mClientProxy = null;
 
     /*
      * The Context Hub that this client is attached to.
@@ -51,20 +51,33 @@
 
     private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
 
-    /* package */ ContextHubClient(
-            IContextHubClient clientProxy, IContextHubClientCallback callback,
-            ContextHubInfo hubInfo) {
-        mClientProxy = clientProxy;
-        mCallbackInterface = callback;
+    /* package */ ContextHubClient(ContextHubInfo hubInfo) {
         mAttachedHub = hubInfo;
         mCloseGuard.open("close");
     }
 
     /**
+     * Sets the proxy interface of the client at the service. This method should always be called
+     * by the ContextHubManager after the client is registered at the service, and should only be
+     * called once.
+     *
+     * @param clientProxy the proxy of the client at the service
+     */
+    /* package */ void setClientProxy(IContextHubClient clientProxy) {
+        Preconditions.checkNotNull(clientProxy, "IContextHubClient cannot be null");
+        if (mClientProxy != null) {
+            throw new IllegalStateException("Cannot change client proxy multiple times");
+        }
+
+        mClientProxy = clientProxy;
+    }
+
+    /**
      * Returns the hub that this client is attached to.
      *
      * @return the ContextHubInfo of the attached hub
      */
+    @NonNull
     public ContextHubInfo getAttachedHub() {
         return mAttachedHub;
     }
@@ -96,12 +109,16 @@
      *
      * @return the result of sending the message defined as in ContextHubTransaction.Result
      *
+     * @throws NullPointerException if NanoAppMessage is null
+     *
      * @see NanoAppMessage
      * @see ContextHubTransaction.Result
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @ContextHubTransaction.Result
-    public int sendMessageToNanoApp(NanoAppMessage message) {
+    public int sendMessageToNanoApp(@NonNull NanoAppMessage message) {
+        Preconditions.checkNotNull(message, "NanoAppMessage cannot be null");
+
         try {
             return mClientProxy.sendMessageToNanoApp(message);
         } catch (RemoteException e) {
diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java
index ab19d54..cc2fe65 100644
--- a/core/java/android/hardware/location/ContextHubClientCallback.java
+++ b/core/java/android/hardware/location/ContextHubClientCallback.java
@@ -15,15 +15,20 @@
  */
 package android.hardware.location;
 
+import android.annotation.SystemApi;
+
+import java.util.concurrent.Executor;
+
 /**
  * A class for {@link android.hardware.location.ContextHubClient ContextHubClient} to
  * receive messages and life-cycle events from nanoapps in the Context Hub at which the client is
  * attached to.
  *
- * This callback is registered through the
- * {@link android.hardware.location.ContextHubManager#createClient() creation} of
- * {@link android.hardware.location.ContextHubClient ContextHubClient}. Callbacks are
- * invoked in the following ways:
+ * This callback is registered through the {@link
+ * android.hardware.location.ContextHubManager#createClient(
+ * ContextHubInfo, ContextHubClientCallback, Executor) creation} of
+ * {@link android.hardware.location.ContextHubClient ContextHubClient}. Callbacks are invoked in
+ * the following ways:
  * 1) Messages from nanoapps delivered through onMessageFromNanoApp may either be broadcasted
  *    or targeted to a specific client.
  * 2) Nanoapp or Context Hub events (the remaining callbacks) are broadcasted to all clients, and
@@ -31,6 +36,7 @@
  *
  * @hide
  */
+@SystemApi
 public class ContextHubClientCallback {
     /**
      * Callback invoked when receiving a message from a nanoapp.
@@ -38,48 +44,56 @@
      * The message contents of this callback may either be broadcasted or targeted to the
      * client receiving the invocation.
      *
+     * @param client the client that is associated with this callback
      * @param message the message sent by the nanoapp
      */
-    public void onMessageFromNanoApp(NanoAppMessage message) {}
+    public void onMessageFromNanoApp(ContextHubClient client, NanoAppMessage message) {}
 
     /**
      * Callback invoked when the attached Context Hub has reset.
+     *
+     * @param client the client that is associated with this callback
      */
-    public void onHubReset() {}
+    public void onHubReset(ContextHubClient client) {}
 
     /**
      * Callback invoked when a nanoapp aborts at the attached Context Hub.
      *
+     * @param client the client that is associated with this callback
      * @param nanoAppId the ID of the nanoapp that had aborted
      * @param abortCode the reason for nanoapp's abort, specific to each nanoapp
      */
-    public void onNanoAppAborted(long nanoAppId, int abortCode) {}
+    public void onNanoAppAborted(ContextHubClient client, long nanoAppId, int abortCode) {}
 
     /**
      * Callback invoked when a nanoapp is loaded at the attached Context Hub.
      *
+     * @param client the client that is associated with this callback
      * @param nanoAppId the ID of the nanoapp that had been loaded
      */
-    public void onNanoAppLoaded(long nanoAppId) {}
+    public void onNanoAppLoaded(ContextHubClient client, long nanoAppId) {}
 
     /**
      * Callback invoked when a nanoapp is unloaded from the attached Context Hub.
      *
+     * @param client the client that is associated with this callback
      * @param nanoAppId the ID of the nanoapp that had been unloaded
      */
-    public void onNanoAppUnloaded(long nanoAppId) {}
+    public void onNanoAppUnloaded(ContextHubClient client, long nanoAppId) {}
 
     /**
      * Callback invoked when a nanoapp is enabled at the attached Context Hub.
      *
+     * @param client the client that is associated with this callback
      * @param nanoAppId the ID of the nanoapp that had been enabled
      */
-    public void onNanoAppEnabled(long nanoAppId) {}
+    public void onNanoAppEnabled(ContextHubClient client, long nanoAppId) {}
 
     /**
      * Callback invoked when a nanoapp is disabled at the attached Context Hub.
      *
+     * @param client the client that is associated with this callback
      * @param nanoAppId the ID of the nanoapp that had been disabled
      */
-    public void onNanoAppDisabled(long nanoAppId) {}
+    public void onNanoAppDisabled(ContextHubClient client, long nanoAppId) {}
 }
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index c2b2800..36123e3 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -221,9 +221,6 @@
 
     /**
      * @return the CHRE platform ID as defined in chre/version.h
-     *
-     * TODO(b/67734082): Expose as public API
-     * @hide
      */
     public long getChrePlatformId() {
         return mChrePlatformId;
@@ -231,9 +228,6 @@
 
     /**
      * @return the CHRE API's major version as defined in chre/version.h
-     *
-     * TODO(b/67734082): Expose as public API
-     * @hide
      */
     public byte getChreApiMajorVersion() {
         return mChreApiMajorVersion;
@@ -241,9 +235,6 @@
 
     /**
      * @return the CHRE API's minor version as defined in chre/version.h
-     *
-     * TODO(b/67734082): Expose as public API
-     * @hide
      */
     public byte getChreApiMinorVersion() {
         return mChreApiMinorVersion;
@@ -251,9 +242,6 @@
 
     /**
      * @return the CHRE patch version as defined in chre/version.h
-     *
-     * TODO(b/67734082): Expose as public API
-     * @hide
      */
     public short getChrePatchVersion() {
         return mChrePatchVersion;
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 4cea0ac..de13c81 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -17,6 +17,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -30,6 +31,8 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -59,7 +62,11 @@
 
     /**
      * An interface to receive asynchronous communication from the context hub.
+     *
+     * @deprecated Use the more refined {@link android.hardware.location.ContextHubClientCallback}
+     *             instead for notification callbacks.
      */
+    @Deprecated
     public abstract static class Callback {
         protected Callback() {}
 
@@ -75,7 +82,7 @@
         public abstract void onMessageReceipt(
                 int hubHandle,
                 int nanoAppHandle,
-                ContextHubMessage message);
+                @NonNull ContextHubMessage message);
     }
 
     /**
@@ -98,8 +105,13 @@
 
     /**
      * Get a handle to all the context hubs in the system
+     *
      * @return array of context hub handles
+     *
+     * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the
+     *             new APIs.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public int[] getContextHubHandles() {
         try {
@@ -116,7 +128,11 @@
      * @return ContextHubInfo Information about the requested context hub.
      *
      * @see ContextHubInfo
+     *
+     * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the
+     *             new APIs.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public ContextHubInfo getContextHubInfo(int hubHandle) {
         try {
@@ -144,9 +160,12 @@
      *         -1 otherwise
      *
      * @see NanoApp
+     *
+     * @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public int loadNanoApp(int hubHandle, NanoApp app) {
+    public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
         try {
             return mService.loadNanoApp(hubHandle, app);
         } catch (RemoteException e) {
@@ -168,7 +187,10 @@
      *
      * @return 0 if the command for unloading was sent to the context hub;
      *         -1 otherwise
+     *
+     * @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public int unloadNanoApp(int nanoAppHandle) {
         try {
@@ -199,13 +221,18 @@
      * TODO(b/30943489): Have the returned NanoAppInstanceInfo contain the
      *     correct information.
      *
-     * @param nanoAppHandle handle of the nanoAppInstance
-     * @return NanoAppInstanceInfo Information about the nano app instance.
+     * @param nanoAppHandle handle of the nanoapp instance
+     * @return NanoAppInstanceInfo the NanoAppInstanceInfo of the nanoapp, or null if the nanoapp
+     *                             does not exist
      *
      * @see NanoAppInstanceInfo
+     *
+     * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub
+     *             for loaded nanoapps.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
+    @Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
         try {
             return mService.getNanoAppInstanceInfo(nanoAppHandle);
         } catch (RemoteException e) {
@@ -222,9 +249,13 @@
      * @see NanoAppFilter
      *
      * @return int[] Array of handles to any found nano apps
+     *
+     * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub
+     *             for loaded nanoapps.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) {
+    @NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
         try {
             return mService.findNanoAppOnHub(hubHandle, filter);
         } catch (RemoteException e) {
@@ -250,9 +281,16 @@
      * @see ContextHubMessage
      *
      * @return int 0 on success, -1 otherwise
+     *
+     * @deprecated Use {@link android.hardware.location.ContextHubClient#sendMessageToNanoApp(
+     *             NanoAppMessage)} instead, after creating a
+     *             {@link android.hardware.location.ContextHubClient} with
+     *             {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
+     *             or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) {
+    public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
         try {
             return mService.sendMessage(hubHandle, nanoAppHandle, message);
         } catch (RemoteException e) {
@@ -266,11 +304,9 @@
      * @return the list of ContextHubInfo objects
      *
      * @see ContextHubInfo
-     *
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public List<ContextHubInfo> getContextHubs() {
+    @NonNull public List<ContextHubInfo> getContextHubs() {
         try {
             return mService.getContextHubs();
         } catch (RemoteException e) {
@@ -342,13 +378,16 @@
      *
      * @return the ContextHubTransaction of the request
      *
-     * @see NanoAppBinary
+     * @throws NullPointerException if hubInfo or NanoAppBinary is null
      *
-     * @hide
+     * @see NanoAppBinary
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public ContextHubTransaction<Void> loadNanoApp(
-            ContextHubInfo hubInfo, NanoAppBinary appBinary) {
+    @NonNull public ContextHubTransaction<Void> loadNanoApp(
+            @NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) {
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+        Preconditions.checkNotNull(appBinary, "NanoAppBinary cannot be null");
+
         ContextHubTransaction<Void> transaction =
                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_LOAD_NANOAPP);
         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
@@ -370,10 +409,13 @@
      *
      * @return the ContextHubTransaction of the request
      *
-     * @hide
+     * @throws NullPointerException if hubInfo is null
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public ContextHubTransaction<Void> unloadNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
+    @NonNull public ContextHubTransaction<Void> unloadNanoApp(
+            @NonNull ContextHubInfo hubInfo, long nanoAppId) {
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+
         ContextHubTransaction<Void> transaction =
                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_UNLOAD_NANOAPP);
         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
@@ -395,10 +437,13 @@
      *
      * @return the ContextHubTransaction of the request
      *
-     * @hide
+     * @throws NullPointerException if hubInfo is null
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public ContextHubTransaction<Void> enableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
+    @NonNull public ContextHubTransaction<Void> enableNanoApp(
+            @NonNull ContextHubInfo hubInfo, long nanoAppId) {
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+
         ContextHubTransaction<Void> transaction =
                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP);
         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
@@ -420,10 +465,13 @@
      *
      * @return the ContextHubTransaction of the request
      *
-     * @hide
+     * @throws NullPointerException if hubInfo is null
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public ContextHubTransaction<Void> disableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
+    @NonNull public ContextHubTransaction<Void> disableNanoApp(
+            @NonNull ContextHubInfo hubInfo, long nanoAppId) {
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+
         ContextHubTransaction<Void> transaction =
                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP);
         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
@@ -444,10 +492,13 @@
      *
      * @return the ContextHubTransaction of the request
      *
-     * @hide
+     * @throws NullPointerException if hubInfo is null
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public ContextHubTransaction<List<NanoAppState>> queryNanoApps(ContextHubInfo hubInfo) {
+    @NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps(
+            @NonNull ContextHubInfo hubInfo) {
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+
         ContextHubTransaction<List<NanoAppState>> transaction =
                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_QUERY_NANOAPPS);
         IContextHubTransactionCallback callback = createQueryCallback(transaction);
@@ -469,9 +520,14 @@
      * @see Callback
      *
      * @return int 0 on success, -1 otherwise
+     *
+     * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
+     *             or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to
+     *             register a {@link android.hardware.location.ContextHubClientCallback}.
      */
+    @Deprecated
     @SuppressLint("Doclava125")
-    public int registerCallback(Callback callback) {
+    public int registerCallback(@NonNull Callback callback) {
         return registerCallback(callback, null);
     }
 
@@ -498,7 +554,12 @@
      * @see Callback
      *
      * @return int 0 on success, -1 otherwise
+     *
+     * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
+     *             or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to
+     *             register a {@link android.hardware.location.ContextHubClientCallback}.
      */
+    @Deprecated
     @SuppressLint("Doclava125")
     public int registerCallback(Callback callback, Handler handler) {
         synchronized(this) {
@@ -515,47 +576,48 @@
     /**
      * Creates an interface to the ContextHubClient to send down to the service.
      *
+     * @param client the ContextHubClient object associated with this callback
      * @param callback the callback to invoke at the client process
      * @param executor the executor to invoke callbacks for this client
      *
      * @return the callback interface
      */
     private IContextHubClientCallback createClientCallback(
-            ContextHubClientCallback callback, Executor executor) {
+            ContextHubClient client, ContextHubClientCallback callback, Executor executor) {
         return new IContextHubClientCallback.Stub() {
             @Override
             public void onMessageFromNanoApp(NanoAppMessage message) {
-                executor.execute(() -> callback.onMessageFromNanoApp(message));
+                executor.execute(() -> callback.onMessageFromNanoApp(client, message));
             }
 
             @Override
             public void onHubReset() {
-                executor.execute(() -> callback.onHubReset());
+                executor.execute(() -> callback.onHubReset(client));
             }
 
             @Override
             public void onNanoAppAborted(long nanoAppId, int abortCode) {
-                executor.execute(() -> callback.onNanoAppAborted(nanoAppId, abortCode));
+                executor.execute(() -> callback.onNanoAppAborted(client, nanoAppId, abortCode));
             }
 
             @Override
             public void onNanoAppLoaded(long nanoAppId) {
-                executor.execute(() -> callback.onNanoAppLoaded(nanoAppId));
+                executor.execute(() -> callback.onNanoAppLoaded(client, nanoAppId));
             }
 
             @Override
             public void onNanoAppUnloaded(long nanoAppId) {
-                executor.execute(() -> callback.onNanoAppUnloaded(nanoAppId));
+                executor.execute(() -> callback.onNanoAppUnloaded(client, nanoAppId));
             }
 
             @Override
             public void onNanoAppEnabled(long nanoAppId) {
-                executor.execute(() -> callback.onNanoAppEnabled(nanoAppId));
+                executor.execute(() -> callback.onNanoAppEnabled(client, nanoAppId));
             }
 
             @Override
             public void onNanoAppDisabled(long nanoAppId) {
-                executor.execute(() -> callback.onNanoAppDisabled(nanoAppId));
+                executor.execute(() -> callback.onNanoAppDisabled(client, nanoAppId));
             }
         };
     }
@@ -574,31 +636,30 @@
      *
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
      * @throws IllegalStateException    if there were too many registered clients at the service
-     * @throws NullPointerException     if callback or hubInfo is null
+     * @throws NullPointerException     if callback, hubInfo, or executor is null
      *
-     * @hide
      * @see ContextHubClientCallback
      */
     @NonNull public ContextHubClient createClient(
             @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
             @NonNull @CallbackExecutor Executor executor) {
-        if (callback == null) {
-            throw new NullPointerException("Callback cannot be null");
-        }
-        if (hubInfo == null) {
-            throw new NullPointerException("Hub info cannot be null");
-        }
+        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+        Preconditions.checkNotNull(executor, "Executor cannot be null");
 
-        IContextHubClientCallback clientInterface = createClientCallback(callback, executor);
+        ContextHubClient client = new ContextHubClient(hubInfo);
+        IContextHubClientCallback clientInterface = createClientCallback(
+                client, callback, executor);
 
-        IContextHubClient client;
+        IContextHubClient clientProxy;
         try {
-            client = mService.createClient(clientInterface, hubInfo.getId());
+            clientProxy = mService.createClient(clientInterface, hubInfo.getId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
 
-        return new ContextHubClient(client, clientInterface, hubInfo);
+        client.setClientProxy(clientProxy);
+        return client;
     }
 
     /**
@@ -612,7 +673,7 @@
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
      * @throws IllegalStateException    if there were too many registered clients at the service
      * @throws NullPointerException     if callback or hubInfo is null
-     * @hide
+     *
      * @see ContextHubClientCallback
      */
     @NonNull public ContextHubClient createClient(
@@ -628,9 +689,13 @@
      * @param callback method to deregister
      *
      * @return int 0 on success, -1 otherwise
+     *
+     * @deprecated Use {@link android.hardware.location.ContextHubClient#close()} to unregister
+     *             a {@link android.hardware.location.ContextHubClientCallback}.
      */
     @SuppressLint("Doclava125")
-    public int unregisterCallback(Callback callback) {
+    @Deprecated
+    public int unregisterCallback(@NonNull Callback callback) {
       synchronized(this) {
           if (callback != mCallback) {
               Log.w(TAG, "Cannot recognize callback!");
@@ -679,8 +744,6 @@
                 synchronized (this) {
                     mLocalCallback.onMessageReceipt(hubId, nanoAppId, message);
                 }
-            } else {
-                Log.d(TAG, "Context hub manager client callback is NULL");
             }
         }
     };
@@ -694,7 +757,7 @@
         try {
             mService.registerCallback(mClientCallback);
         } catch (RemoteException e) {
-            Log.w(TAG, "Could not register callback:" + e);
+            throw e.rethrowFromSystemServer();
         }
     }
 }
diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java
index a1b743d..bc7efef 100644
--- a/core/java/android/hardware/location/ContextHubTransaction.java
+++ b/core/java/android/hardware/location/ContextHubTransaction.java
@@ -18,9 +18,12 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 
+import com.android.internal.util.Preconditions;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.CountDownLatch;
@@ -35,17 +38,19 @@
  * through the ContextHubManager APIs. The caller can either retrieve the result
  * synchronously through a blocking call ({@link #waitForResponse(long, TimeUnit)}) or
  * asynchronously through a user-defined listener
- * ({@link #setOnCompleteListener(Listener, Executor)} )}).
+ * ({@link #setOnCompleteListener(OnCompleteListener, Executor)} )}).
  *
  * @param <T> the type of the contents in the transaction response
  *
  * @hide
  */
+@SystemApi
 public class ContextHubTransaction<T> {
     private static final String TAG = "ContextHubTransaction";
 
     /**
      * Constants describing the type of a transaction through the Context Hub Service.
+     * {@hide}
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "TYPE_" }, value = {
@@ -65,6 +70,7 @@
 
     /**
      * Constants describing the result of a transaction or request through the Context Hub Service.
+     * {@hide}
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "RESULT_" }, value = {
@@ -72,7 +78,7 @@
             RESULT_FAILED_UNKNOWN,
             RESULT_FAILED_BAD_PARAMS,
             RESULT_FAILED_UNINITIALIZED,
-            RESULT_FAILED_PENDING,
+            RESULT_FAILED_BUSY,
             RESULT_FAILED_AT_HUB,
             RESULT_FAILED_TIMEOUT,
             RESULT_FAILED_SERVICE_INTERNAL_FAILURE,
@@ -95,7 +101,7 @@
     /**
      * Failure mode when there are too many transactions pending.
      */
-    public static final int RESULT_FAILED_PENDING = 4;
+    public static final int RESULT_FAILED_BUSY = 4;
     /**
      * Failure mode when the request went through, but failed asynchronously at the hub.
      */
@@ -151,7 +157,7 @@
      * @param <L> the type of the contents in the transaction response
      */
     @FunctionalInterface
-    public interface Listener<L> {
+    public interface OnCompleteListener<L> {
         /**
          * The listener function to invoke when the transaction completes.
          *
@@ -181,7 +187,7 @@
     /*
      * The listener to be invoked when the transaction completes.
      */
-    private ContextHubTransaction.Listener<T> mListener = null;
+    private ContextHubTransaction.OnCompleteListener<T> mListener = null;
 
     /*
      * Synchronization latch used to block on response.
@@ -272,8 +278,8 @@
      * A transaction can be invalidated if the process owning the transaction is no longer active
      * and the reference to this object is lost.
      *
-     * This method or {@link #setOnCompleteListener(ContextHubTransaction.Listener)} can only be
-     * invoked once, or an IllegalStateException will be thrown.
+     * This method or {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener)} can
+     * only be invoked once, or an IllegalStateException will be thrown.
      *
      * @param listener the listener to be invoked upon completion
      * @param executor the executor to invoke the callback
@@ -282,15 +288,11 @@
      * @throws NullPointerException if the callback or handler is null
      */
     public void setOnCompleteListener(
-            @NonNull ContextHubTransaction.Listener<T> listener,
+            @NonNull ContextHubTransaction.OnCompleteListener<T> listener,
             @NonNull @CallbackExecutor Executor executor) {
         synchronized (this) {
-            if (listener == null) {
-                throw new NullPointerException("Listener cannot be null");
-            }
-            if (executor == null) {
-                throw new NullPointerException("Executor cannot be null");
-            }
+            Preconditions.checkNotNull(listener, "OnCompleteListener cannot be null");
+            Preconditions.checkNotNull(executor, "Executor cannot be null");
             if (mListener != null) {
                 throw new IllegalStateException(
                         "Cannot set ContextHubTransaction listener multiple times");
@@ -308,18 +310,19 @@
     /**
      * Sets the listener to be invoked invoked when the transaction completes.
      *
-     * Equivalent to {@link #setOnCompleteListener(ContextHubTransaction.Listener, Executor)}
-     * with the executor using the main thread's Looper.
+     * Equivalent to {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener,
+     * Executor)} with the executor using the main thread's Looper.
      *
-     * This method or {@link #setOnCompleteListener(ContextHubTransaction.Listener, Executor)}
-     * can only be invoked once, or an IllegalStateException will be thrown.
+     * This method or {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener,
+     * Executor)} can only be invoked once, or an IllegalStateException will be thrown.
      *
      * @param listener the listener to be invoked upon completion
      *
      * @throws IllegalStateException if this method is called multiple times
      * @throws NullPointerException if the callback is null
      */
-    public void setOnCompleteListener(@NonNull ContextHubTransaction.Listener<T> listener) {
+    public void setOnCompleteListener(
+            @NonNull ContextHubTransaction.OnCompleteListener<T> listener) {
         setOnCompleteListener(listener, new HandlerExecutor(Handler.getMain()));
     }
 
@@ -337,9 +340,7 @@
      */
     /* package */ void setResponse(ContextHubTransaction.Response<T> response) {
         synchronized (this) {
-            if (response == null) {
-                throw new NullPointerException("Response cannot be null");
-            }
+            Preconditions.checkNotNull(response, "Response cannot be null");
             if (mIsResponseSet) {
                 throw new IllegalStateException(
                         "Cannot set response of ContextHubTransaction multiple times");
diff --git a/core/java/android/hardware/location/NanoAppBinary.java b/core/java/android/hardware/location/NanoAppBinary.java
index 934e9e4..ba01ca2 100644
--- a/core/java/android/hardware/location/NanoAppBinary.java
+++ b/core/java/android/hardware/location/NanoAppBinary.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.location;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -27,6 +28,7 @@
 /**
  * @hide
  */
+@SystemApi
 public final class NanoAppBinary implements Parcelable {
     private static final String TAG = "NanoAppBinary";
 
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index f73fd87..c00819b 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -90,11 +90,6 @@
     /**
      * Get the application version
      *
-     * NOTE: There is a race condition where shortly after loading, this
-     * may return -1 instead of the correct version.
-     *
-     * TODO(b/30970527): Fix this race condition.
-     *
      * @return int - version of the app
      */
     public int getAppVersion() {
diff --git a/core/java/android/hardware/location/NanoAppMessage.java b/core/java/android/hardware/location/NanoAppMessage.java
index 2028674..716a194 100644
--- a/core/java/android/hardware/location/NanoAppMessage.java
+++ b/core/java/android/hardware/location/NanoAppMessage.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.location;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -25,6 +26,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class NanoAppMessage implements Parcelable {
     private long mNanoAppId;
     private int mMessageType;
diff --git a/core/java/android/hardware/location/NanoAppState.java b/core/java/android/hardware/location/NanoAppState.java
index 644031b..d05277d 100644
--- a/core/java/android/hardware/location/NanoAppState.java
+++ b/core/java/android/hardware/location/NanoAppState.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.location;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -23,6 +24,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class NanoAppState implements Parcelable {
     private long mNanoAppId;
     private int mNanoAppVersion;
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 4d54e31..9b6515c 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -38,6 +38,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -645,7 +646,8 @@
         private final boolean mAf;
         private final boolean mEa;
 
-        FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
+        /** @hide */
+        public FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
                 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
             super(region, type, lowerLimit, upperLimit, spacing);
             mStereo = stereo;
@@ -771,7 +773,8 @@
 
         private final boolean mStereo;
 
-        AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
+        /** @hide */
+        public AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
                 boolean stereo) {
             super(region, type, lowerLimit, upperLimit, spacing);
             mStereo = stereo;
@@ -843,10 +846,10 @@
     /** Radio band configuration. */
     public static class BandConfig implements Parcelable {
 
-        final BandDescriptor mDescriptor;
+        @NonNull final BandDescriptor mDescriptor;
 
         BandConfig(BandDescriptor descriptor) {
-            mDescriptor = descriptor;
+            mDescriptor = Objects.requireNonNull(descriptor);
         }
 
         BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) {
@@ -968,7 +971,8 @@
         private final boolean mAf;
         private final boolean mEa;
 
-        FmBandConfig(FmBandDescriptor descriptor) {
+        /** @hide */
+        public FmBandConfig(FmBandDescriptor descriptor) {
             super((BandDescriptor)descriptor);
             mStereo = descriptor.isStereoSupported();
             mRds = descriptor.isRdsSupported();
@@ -1204,7 +1208,8 @@
     public static class AmBandConfig extends BandConfig {
         private final boolean mStereo;
 
-        AmBandConfig(AmBandDescriptor descriptor) {
+        /** @hide */
+        public AmBandConfig(AmBandDescriptor descriptor) {
             super((BandDescriptor)descriptor);
             mStereo = descriptor.isStereoSupported();
         }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index f75789f..7e37432 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -70,6 +70,7 @@
 
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
+    String getSubscriptionPlansOwner(int subId);
 
     void factoryReset(String subscriber);
 
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 42e43c8..5425bf5 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -59,8 +59,8 @@
     /**
      * Report network watchlist records if necessary.
      *
-     * Watchlist report process will run summarize records into a single report, then the
-     * report will be processed by differential privacy framework and store it on disk.
+     * Watchlist report process will summarize records into a single report, then the
+     * report will be processed by differential privacy framework and stored on disk.
      *
      * @hide
      */
@@ -72,4 +72,18 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Reload network watchlist.
+     *
+     * @hide
+     */
+    public void reloadWatchlist() {
+        try {
+            mNetworkWatchlistManager.reloadWatchlist();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to reload watchlist");
+            e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1e847c5..d4d74f4 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -180,6 +180,11 @@
     public static final int FOREGROUND_SERVICE = 22;
 
     /**
+     * A constant indicating an aggregate wifi multicast timer
+     */
+     public static final int WIFI_AGGREGATE_MULTICAST_ENABLED = 23;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -2334,6 +2339,22 @@
     };
 
     /**
+     * Returns total time for WiFi Multicast Wakelock timer.
+     * Note that this may be different from the sum of per uid timer values.
+     *
+     *  {@hide}
+     */
+    public abstract long getWifiMulticastWakelockTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns total time for WiFi Multicast Wakelock timer
+     * Note that this may be different from the sum of per uid timer values.
+     *
+     * {@hide}
+     */
+    public abstract int getWifiMulticastWakelockCount(int which);
+
+    /**
      * Returns the time in microseconds that wifi has been on while the device was
      * running on battery.
      *
@@ -3442,16 +3463,13 @@
                 screenDozeTime / 1000);
 
 
-        // Calculate both wakelock and wifi multicast wakelock times across all uids.
+        // Calculate wakelock times across all uids.
         long fullWakeLockTimeTotal = 0;
         long partialWakeLockTimeTotal = 0;
-        long multicastWakeLockTimeTotalMicros = 0;
-        int multicastWakeLockCountTotal = 0;
 
         for (int iu = 0; iu < NU; iu++) {
             final Uid u = uidStats.valueAt(iu);
 
-            // First calculating the wakelock stats
             final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
                     = u.getWakelockStats();
             for (int iw=wakelocks.size()-1; iw>=0; iw--) {
@@ -3469,13 +3487,6 @@
                         rawRealtime, which);
                 }
             }
-
-            // Now calculating the wifi multicast wakelock stats
-            final Timer mcTimer = u.getMulticastWakelockStats();
-            if (mcTimer != null) {
-                multicastWakeLockTimeTotalMicros += mcTimer.getTotalTimeLocked(rawRealtime, which);
-                multicastWakeLockCountTotal += mcTimer.getCountLocked(which);
-            }
         }
 
         // Dump network stats
@@ -3592,6 +3603,9 @@
         dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_COUNT_DATA, args);
 
         // Dump Multicast total stats
+        final long multicastWakeLockTimeTotalMicros =
+                getWifiMulticastWakelockTime(rawRealtime, which);
+        final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which);
         dumpLine(pw, 0 /* uid */, category, WIFI_MULTICAST_TOTAL_DATA,
                 multicastWakeLockTimeTotalMicros / 1000,
                 multicastWakeLockCountTotal);
@@ -4456,18 +4470,15 @@
             pw.print("  Connectivity changes: "); pw.println(connChanges);
         }
 
-        // Calculate both wakelock and wifi multicast wakelock times across all uids.
+        // Calculate wakelock times across all uids.
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
-        long multicastWakeLockTimeTotalMicros = 0;
-        int multicastWakeLockCountTotal = 0;
 
         final ArrayList<TimerEntry> timers = new ArrayList<>();
 
         for (int iu = 0; iu < NU; iu++) {
             final Uid u = uidStats.valueAt(iu);
 
-            // First calculate wakelock statistics
             final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
                     = u.getWakelockStats();
             for (int iw=wakelocks.size()-1; iw>=0; iw--) {
@@ -4495,13 +4506,6 @@
                     }
                 }
             }
-
-            // Next calculate wifi multicast wakelock statistics
-            final Timer mcTimer = u.getMulticastWakelockStats();
-            if (mcTimer != null) {
-                multicastWakeLockTimeTotalMicros += mcTimer.getTotalTimeLocked(rawRealtime, which);
-                multicastWakeLockCountTotal += mcTimer.getCountLocked(which);
-            }
         }
 
         final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
@@ -4531,6 +4535,9 @@
             pw.println(sb.toString());
         }
 
+        final long multicastWakeLockTimeTotalMicros =
+                getWifiMulticastWakelockTime(rawRealtime, which);
+        final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which);
         if (multicastWakeLockTimeTotalMicros != 0) {
             sb.setLength(0);
             sb.append(prefix);
@@ -7051,6 +7058,28 @@
                     }
                 }
             }
+
+            for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
+                final long[] timesMs = u.getCpuFreqTimes(which, procState);
+                if (timesMs != null && timesMs.length == cpuFreqs.length) {
+                    long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes(which, procState);
+                    if (screenOffTimesMs == null) {
+                        screenOffTimesMs = new long[timesMs.length];
+                    }
+                    final long procToken = proto.start(UidProto.Cpu.BY_PROCESS_STATE);
+                    proto.write(UidProto.Cpu.ByProcessState.PROCESS_STATE, procState);
+                    for (int ic = 0; ic < timesMs.length; ++ic) {
+                        long cToken = proto.start(UidProto.Cpu.ByProcessState.BY_FREQUENCY);
+                        proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1);
+                        proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS,
+                                timesMs[ic]);
+                        proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS,
+                                screenOffTimesMs[ic]);
+                        proto.end(cToken);
+                    }
+                    proto.end(procToken);
+                }
+            }
             proto.end(cpuToken);
 
             // Flashlight (FLASHLIGHT_DATA)
@@ -7535,22 +7564,9 @@
         proto.end(mToken);
 
         // Wifi multicast wakelock total stats (WIFI_MULTICAST_WAKELOCK_TOTAL_DATA)
-        // Calculate multicast wakelock stats across all uids.
-        long multicastWakeLockTimeTotalUs = 0;
-        int multicastWakeLockCountTotal = 0;
-
-        for (int iu = 0; iu < uidStats.size(); iu++) {
-            final Uid u = uidStats.valueAt(iu);
-
-            final Timer mcTimer = u.getMulticastWakelockStats();
-
-            if (mcTimer != null) {
-                multicastWakeLockTimeTotalUs +=
-                        mcTimer.getTotalTimeLocked(rawRealtimeUs, which);
-                multicastWakeLockCountTotal += mcTimer.getCountLocked(which);
-            }
-        }
-
+        final long multicastWakeLockTimeTotalUs =
+                getWifiMulticastWakelockTime(rawRealtimeUs, which);
+        final int multicastWakeLockCountTotal = getWifiMulticastWakelockCount(which);
         final long wmctToken = proto.start(SystemProto.WIFI_MULTICAST_WAKELOCK_TOTAL);
         proto.write(SystemProto.WifiMulticastWakelockTotal.DURATION_MS,
                 multicastWakeLockTimeTotalUs / 1000);
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 3ca1005..5c5e351 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -388,6 +388,8 @@
      * The runnable will be run on the thread to which this handler is attached.
      *
      * @param r The Runnable that will be executed.
+     * @param token An instance which can be used to cancel {@code r} via
+     *         {@link #removeCallbacksAndMessages}.
      * @param uptimeMillis The absolute time at which the callback should run,
      *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
      * 
@@ -430,6 +432,32 @@
     }
     
     /**
+     * Causes the Runnable r to be added to the message queue, to be run
+     * after the specified amount of time elapses.
+     * The runnable will be run on the thread to which this handler
+     * is attached.
+     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
+     * Time spent in deep sleep will add an additional delay to execution.
+     *
+     * @param r The Runnable that will be executed.
+     * @param token An instance which can be used to cancel {@code r} via
+     *         {@link #removeCallbacksAndMessages}.
+     * @param delayMillis The delay (in milliseconds) until the Runnable
+     *        will be executed.
+     *
+     * @return Returns true if the Runnable was successfully placed in to the
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the Runnable will be processed --
+     *         if the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     */
+    public final boolean postDelayed(Runnable r, Object token, long delayMillis)
+    {
+        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
+    }
+
+    /**
      * Posts a message to an object that implements Runnable.
      * Causes the Runnable r to executed on the next iteration through the
      * message queue. The runnable will be run on the thread to which this
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 3db12ed..29812e8 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -71,7 +71,7 @@
      * Fetches data for the specified configuration key. Returns a byte array representing proto
      * wire-encoded of ConfigMetricsReportList.
      */
-    byte[] getData(in String key);
+    byte[] getData(in long key);
 
     /**
      * Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
@@ -86,7 +86,7 @@
      *
      * Returns if this configuration was correctly registered.
      */
-    boolean addConfiguration(in String configKey, in byte[] config, in String pkg, in String cls);
+    boolean addConfiguration(in long configKey, in byte[] config, in String pkg, in String cls);
 
     /**
      * Removes the configuration with the matching config key. No-op if this config key does not
@@ -94,5 +94,5 @@
      *
      * Returns if this configuration key was removed.
      */
-    boolean removeConfiguration(in String configKey);
+    boolean removeConfiguration(in long configKey);
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dd9fd93..38993b7 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2197,7 +2197,8 @@
 
     /**
      * Similar to {@link #trySetQuietModeEnabled(boolean, UserHandle)}, except you can specify
-     * a target to start when user is unlocked.
+     * a target to start when user is unlocked. If {@code target} is specified, caller must have
+     * the {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @see {@link #trySetQuietModeEnabled(boolean, UserHandle)}
      * @hide
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4c587a8..68c242d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -123,8 +123,6 @@
     public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
     /** {@hide} */
     public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
-    /** {@hide} */
-    public static final String PROP_ADOPTABLE_FBE = "persist.sys.adoptable_fbe";
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
@@ -1476,6 +1474,11 @@
     }
 
     /** {@hide} */
+    public static boolean hasAdoptable() {
+        return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
+    }
+
+    /** {@hide} */
     public static File maybeTranslateEmulatedPathToInternal(File path) {
         // Disabled now that FUSE has been replaced by sdcardfs
         return path;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2f86514..009fc39 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8790,6 +8790,7 @@
          * Type: string package name or null if the feature is either not provided or disabled.
          * @hide
          */
+        @TestApi
         public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
 
         /**
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
index 72a138a..0412326 100644
--- a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
+++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
@@ -23,7 +23,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
-import android.os.UserHandle;
 import android.security.KeyStore;
 import android.util.AndroidException;
 
@@ -44,8 +43,10 @@
 
     public static final int NO_ERROR = KeyStore.NO_ERROR;
     public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR;
-    public static final int UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;
-    public static final int NO_SNAPSHOT_PENDING_ERROR = 21;
+    public static final int ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;
+    public static final int ERROR_NO_SNAPSHOT_PENDING = 21;
+    public static final int ERROR_KEYSTORE_INTERNAL_ERROR = 22;
+    public static final int ERROR_INSECURE_USER = 24;
 
     /**
      * Rate limit is enforced to prevent using too many trusted remote devices, since each device
@@ -124,7 +125,7 @@
                     return "OK";
                 case SYSTEM_ERROR:
                     return "System error";
-                case UNINITIALIZED_RECOVERY_PUBLIC_KEY:
+                case ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY:
                     return "Recovery service is not initialized";
                 case RATE_LIMIT_EXCEEDED:
                     return "Rate limit exceeded";
@@ -156,8 +157,7 @@
             @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
             throws RecoverableKeyStoreLoaderException {
         try {
-            mBinder.initRecoveryService(
-                    rootCertificateAlias, signedPublicKeyList, UserHandle.getCallingUserId());
+            mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -176,8 +176,7 @@
     public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
             throws RecoverableKeyStoreLoaderException {
         try {
-            KeyStoreRecoveryData recoveryData =
-                    mBinder.getRecoveryData(account, UserHandle.getCallingUserId());
+            KeyStoreRecoveryData recoveryData = mBinder.getRecoveryData(account);
             return recoveryData;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -198,7 +197,7 @@
     public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
             throws RecoverableKeyStoreLoaderException {
         try {
-            mBinder.setSnapshotCreatedPendingIntent(intent, UserHandle.getCallingUserId());
+            mBinder.setSnapshotCreatedPendingIntent(intent);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -220,8 +219,7 @@
             // IPC doesn't support generic Maps.
             @SuppressWarnings("unchecked")
             Map<byte[], Integer> result =
-                    (Map<byte[], Integer>)
-                            mBinder.getRecoverySnapshotVersions(UserHandle.getCallingUserId());
+                    (Map<byte[], Integer>) mBinder.getRecoverySnapshotVersions();
             return result;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -243,7 +241,7 @@
     public void setServerParameters(long serverParameters)
             throws RecoverableKeyStoreLoaderException {
         try {
-            mBinder.setServerParameters(serverParameters, UserHandle.getCallingUserId());
+            mBinder.setServerParameters(serverParameters);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -265,7 +263,7 @@
             @NonNull String packageName, @Nullable String[] aliases, int status)
             throws NameNotFoundException, RecoverableKeyStoreLoaderException {
         try {
-            mBinder.setRecoveryStatus(packageName, aliases, status, UserHandle.getCallingUserId());
+            mBinder.setRecoveryStatus(packageName, aliases, status);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -297,7 +295,7 @@
             @SuppressWarnings("unchecked")
             Map<String, Integer> result =
                     (Map<String, Integer>)
-                            mBinder.getRecoveryStatus(packageName, UserHandle.getCallingUserId());
+                            mBinder.getRecoveryStatus(packageName);
             return result;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -317,7 +315,7 @@
             @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
             throws RecoverableKeyStoreLoaderException {
         try {
-            mBinder.setRecoverySecretTypes(secretTypes, UserHandle.getCallingUserId());
+            mBinder.setRecoverySecretTypes(secretTypes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -335,7 +333,7 @@
     public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
             throws RecoverableKeyStoreLoaderException {
         try {
-            return mBinder.getRecoverySecretTypes(UserHandle.getCallingUserId());
+            return mBinder.getRecoverySecretTypes();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -353,7 +351,7 @@
     public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
             throws RecoverableKeyStoreLoaderException {
         try {
-            return mBinder.getPendingRecoverySecretTypes(UserHandle.getCallingUserId());
+            return mBinder.getPendingRecoverySecretTypes();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -373,7 +371,7 @@
     public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
             throws RecoverableKeyStoreLoaderException {
         try {
-            mBinder.recoverySecretAvailable(recoverySecret, UserHandle.getCallingUserId());
+            mBinder.recoverySecretAvailable(recoverySecret);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -412,8 +410,7 @@
                             verifierPublicKey,
                             vaultParams,
                             vaultChallenge,
-                            secrets,
-                            UserHandle.getCallingUserId());
+                            secrets);
             return recoveryClaim;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -426,7 +423,7 @@
      * Imports keys.
      *
      * @param sessionId Id for recovery session, same as in
-     *     {@link #startRecoverySession(String, byte[], byte[], byte[], List)} on}.
+     *     {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
      * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
      * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
      *     and session. KeyStore only uses package names from the application info in {@link
@@ -440,7 +437,7 @@
             throws RecoverableKeyStoreLoaderException {
         try {
             return (Map<String, byte[]>) mBinder.recoverKeys(
-                    sessionId, recoveryKeyBlob, applicationKeys, UserHandle.getCallingUserId());
+                    sessionId, recoveryKeyBlob, applicationKeys);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
index 0706b37..97a3868 100644
--- a/core/java/android/service/autofill/EditDistanceScorer.java
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -16,8 +16,7 @@
 package android.service.autofill;
 
 import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.annotation.TestApi;
 import android.view.autofill.AutofillValue;
 
 /**
@@ -25,13 +24,20 @@
  * by the user and the expected value predicted by an autofill service.
  */
 // TODO(b/70291841): explain algorithm once it's fully implemented
-public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
+/** @hide */
+@TestApi
+public final class EditDistanceScorer {
 
     private static final EditDistanceScorer sInstance = new EditDistanceScorer();
 
+    /** @hide */
+    public static final String NAME = "EDIT_DISTANCE";
+
     /**
      * Gets the singleton instance.
      */
+    @TestApi
+    /** @hide */
     public static EditDistanceScorer getInstance() {
         return sInstance;
     }
@@ -39,59 +45,32 @@
     private EditDistanceScorer() {
     }
 
-    /** @hide */
-    @Override
-    public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
-        if (actualValue == null || !actualValue.isText() || userData == null) return 0;
+    /**
+     * Returns the classification score between an actual {@link AutofillValue} filled
+     * by the user and the expected value predicted by an autofill service.
+     *
+     * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and
+     * partial mathces are something in between, typically using edit-distance algorithms.
+     *
+     * @hide
+     */
+    @TestApi
+    public float getScore(@NonNull AutofillValue actualValue, @NonNull String userDataValue) {
+        if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
         // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or
         // partial match when number of chars match
         final String textValue = actualValue.getTextValue().toString();
         final int total = textValue.length();
-        if (total != userData.length()) return 0F;
+        if (total != userDataValue.length()) return 0F;
 
         int matches = 0;
         for (int i = 0; i < total; i++) {
             if (Character.toLowerCase(textValue.charAt(i)) == Character
-                    .toLowerCase(userData.charAt(i))) {
+                    .toLowerCase(userDataValue.charAt(i))) {
                 matches++;
             }
         }
 
         return ((float) matches) / total;
     }
-
-    /////////////////////////////////////
-    // Object "contract" methods. //
-    /////////////////////////////////////
-    @Override
-    public String toString() {
-        return "EditDistanceScorer";
-    }
-
-    /////////////////////////////////////
-    // Parcelable "contract" methods. //
-    /////////////////////////////////////
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        // Do nothing
-    }
-
-    public static final Parcelable.Creator<EditDistanceScorer> CREATOR =
-            new Parcelable.Creator<EditDistanceScorer>() {
-        @Override
-        public EditDistanceScorer createFromParcel(Parcel parcel) {
-            return EditDistanceScorer.getInstance();
-        }
-
-        @Override
-        public EditDistanceScorer[] newArray(int size) {
-            return new EditDistanceScorer[size];
-        }
-    };
 }
diff --git a/core/java/android/service/autofill/FieldClassification.aidl b/core/java/android/service/autofill/FieldClassification.aidl
new file mode 100644
index 0000000..42f7f02
--- /dev/null
+++ b/core/java/android/service/autofill/FieldClassification.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+parcelable FieldClassification.AlgorithmInfo;
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 001b291..5361803 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -106,18 +106,20 @@
     /**
      * Represents the score of a {@link UserData} entry for the field.
      *
-     * <p>The score is defined by {@link #getScore()} and the entry is identified by
-     * {@link #getRemoteId()}.
+     * <p>The score is calculated by the given {@link #getAlgorithm() algorithm} and
+     * the entry is identified by {@link #getRemoteId()}.
      */
     public static final class Match {
 
         private final String mRemoteId;
         private final float mScore;
+        private final String mAlgorithm;
 
         /** @hide */
-        public Match(String remoteId, float score) {
+        public Match(String remoteId, float score, String algorithm) {
             mRemoteId = Preconditions.checkNotNull(remoteId);
             mScore = score;
+            mAlgorithm = algorithm;
         }
 
         /**
@@ -140,29 +142,46 @@
          *   <li>Any other value is a partial match.
          * </ul>
          *
-         * <p>How the score is calculated depends on the algorithm used by the {@link Scorer}
-         * implementation.
+         * <p>How the score is calculated depends on the
+         * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)
+         * algorithm} used.
          */
         public float getScore() {
             return mScore;
         }
 
+        /**
+         * Gets the algorithm used to calculate this score.
+         *
+         * <p>Typically, this is either the algorithm set by
+         * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)},
+         * or the
+         * {@link android.view.autofill.AutofillManager#getDefaultFieldClassificationAlgorithm()}.
+         */
+        @NonNull
+        public String getAlgorithm() {
+            return mAlgorithm;
+        }
+
         @Override
         public String toString() {
             if (!sDebug) return super.toString();
 
             final StringBuilder string = new StringBuilder("Match: remoteId=");
             Helper.appendRedacted(string, mRemoteId);
-            return string.append(", score=").append(mScore).toString();
+            return string.append(", score=").append(mScore)
+                    .append(", algorithm=").append(mAlgorithm)
+                    .toString();
         }
 
         private void writeToParcel(@NonNull Parcel parcel) {
             parcel.writeString(mRemoteId);
             parcel.writeFloat(mScore);
+            parcel.writeString(mAlgorithm);
         }
 
         private static Match readFromParcel(@NonNull Parcel parcel) {
-            return new Match(parcel.readString(), parcel.readFloat());
+            return new Match(parcel.readString(), parcel.readFloat(), parcel.readString());
         }
     }
 }
diff --git a/core/java/android/service/autofill/InternalScorer.java b/core/java/android/service/autofill/InternalScorer.java
deleted file mode 100644
index 0da5afc..0000000
--- a/core/java/android/service/autofill/InternalScorer.java
+++ /dev/null
@@ -1,40 +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.service.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.TestApi;
-import android.os.Parcelable;
-import android.view.autofill.AutofillValue;
-
-/**
- * Superclass of all scorer the system understands. As this is not public all
- * subclasses have to implement {@link Scorer} again.
- *
- * @hide
- */
-@TestApi
-public abstract class InternalScorer implements Scorer, Parcelable {
-
-    /**
-     * Returns the classification score between an actual {@link AutofillValue} filled
-     * by the user and the expected value predicted by an autofill service.
-     *
-     * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and
-     * partial mathces are something in between, typically using edit-distance algorithms.
-     */
-    public abstract float getScore(@NonNull AutofillValue actualValue, @NonNull String userData);
-}
diff --git a/core/java/android/service/autofill/UserData.aidl b/core/java/android/service/autofill/UserData.aidl
index 76016de..19282e0 100644
--- a/core/java/android/service/autofill/UserData.aidl
+++ b/core/java/android/service/autofill/UserData.aidl
@@ -17,4 +17,3 @@
 package android.service.autofill;
 
 parcelable UserData;
-parcelable UserData.Constraints;
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index f0cc360..2f9225a 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -25,10 +25,13 @@
 import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.content.ContentResolver;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Settings;
+import android.service.autofill.FieldClassification.Match;
 import android.util.Log;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.Helper;
 
 import com.android.internal.util.Preconditions;
@@ -49,21 +52,32 @@
     private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
     private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
 
-    private final InternalScorer mScorer;
+    private final String mAlgorithm;
+    private final Bundle mAlgorithmArgs;
     private final String[] mRemoteIds;
     private final String[] mValues;
 
     private UserData(Builder builder) {
-        mScorer = builder.mScorer;
+        mAlgorithm = builder.mAlgorithm;
+        mAlgorithmArgs = builder.mAlgorithmArgs;
         mRemoteIds = new String[builder.mRemoteIds.size()];
         builder.mRemoteIds.toArray(mRemoteIds);
         mValues = new String[builder.mValues.size()];
         builder.mValues.toArray(mValues);
     }
 
+    /**
+     * Gets the name of the algorithm that is used to calculate
+     * {@link Match#getScore() match scores}.
+     */
+    @Nullable
+    public String getFieldClassificationAlgorithm() {
+        return mAlgorithm;
+    }
+
     /** @hide */
-    public InternalScorer getScorer() {
-        return mScorer;
+    public Bundle getAlgorithmArgs() {
+        return mAlgorithmArgs;
     }
 
     /** @hide */
@@ -78,7 +92,9 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("Scorer: "); pw.println(mScorer);
+        pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
+        pw.print(" Args: "); pw.println(mAlgorithmArgs);
+
         // Cannot disclose remote ids or values because they could contain PII
         pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
         for (int i = 0; i < mRemoteIds.length; i++) {
@@ -105,9 +121,10 @@
      * A builder for {@link UserData} objects.
      */
     public static final class Builder {
-        private final InternalScorer mScorer;
         private final ArrayList<String> mRemoteIds;
         private final ArrayList<String> mValues;
+        private String mAlgorithm;
+        private Bundle mAlgorithmArgs;
         private boolean mDestroyed;
 
         /**
@@ -120,13 +137,9 @@
          *   <li>{@code value} is empty
          *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
          *   <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
-         *   <li>{@code scorer} is not instance of a class provided by the Android System.
          * </ol>
          */
-        public Builder(@NonNull Scorer scorer, @NonNull String remoteId, @NonNull String value) {
-            Preconditions.checkArgument((scorer instanceof InternalScorer),
-                    "not provided by Android System: " + scorer);
-            mScorer = (InternalScorer) scorer;
+        public Builder(@NonNull String remoteId, @NonNull String value) {
             checkValidRemoteId(remoteId);
             checkValidValue(value);
             final int capacity = getMaxUserDataSize();
@@ -137,6 +150,31 @@
         }
 
         /**
+         * Sets the algorithm used for <a href="#FieldClassification">field classification</a>.
+         *
+         * <p>The currently available algorithms can be retrieve through
+         * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
+         *
+         * <p><b>Note: </b>The available algorithms in the Android System can change dinamically,
+         * so it's not guaranteed that the algorithm set here is the one that will be effectually
+         * used. If the algorithm set here is not available at runtime, the
+         * {@link AutofillManager#getDefaultFieldClassificationAlgorithm()} is used instead.
+         * You can verify which algorithm was used by calling
+         * {@link FieldClassification.Match#getAlgorithm()}.
+         *
+         * @param name name of the algorithm or {@code null} to used default.
+         * @param args optional arguments to the algorithm.
+         *
+         * @return this builder
+         */
+        public Builder setFieldClassificationAlgorithm(@Nullable String name,
+                @Nullable Bundle args) {
+            mAlgorithm = name;
+            mAlgorithmArgs = args;
+            return this;
+        }
+
+        /**
          * Adds a new value for user data.
          *
          * @param remoteId unique string used to identify the user data.
@@ -211,7 +249,7 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("UserData: [scorer=").append(mScorer);
+        final StringBuilder builder = new StringBuilder("UserData: [algorithm=").append(mAlgorithm);
         // Cannot disclose remote ids or values because they could contain PII
         builder.append(", remoteIds=");
         Helper.appendRedacted(builder, mRemoteIds);
@@ -231,9 +269,10 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mScorer, flags);
         parcel.writeStringArray(mRemoteIds);
         parcel.writeStringArray(mValues);
+        parcel.writeString(mAlgorithm);
+        parcel.writeBundle(mAlgorithmArgs);
     }
 
     public static final Parcelable.Creator<UserData> CREATOR =
@@ -243,10 +282,10 @@
             // 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 InternalScorer scorer = parcel.readParcelable(null);
             final String[] remoteIds = parcel.readStringArray();
             final String[] values = parcel.readStringArray();
-            final Builder builder = new Builder(scorer, remoteIds[0], values[0]);
+            final Builder builder = new Builder(remoteIds[0], values[0])
+                    .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
             for (int i = 1; i < remoteIds.length; i++) {
                 builder.add(remoteIds[i], values[i]);
             }
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index b8f2191..dccce40 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -27,7 +27,7 @@
     void setDesiredSize(int width, int height);
     void setDisplayPadding(in Rect padding);
     void setVisibility(boolean visible);
-    void setInAmbientMode(boolean inAmbientDisplay);
+    void setInAmbientMode(boolean inAmbientDisplay, boolean animated);
     void dispatchPointer(in MotionEvent event);
     void dispatchWallpaperCommand(String action, int x, int y,
             int z, in Bundle extras);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 595bfb7..8588df7 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -563,9 +563,12 @@
          * Called when the device enters or exits ambient mode.
          *
          * @param inAmbientMode {@code true} if in ambient mode.
+         * @param animated {@code true} if you'll have te opportunity of animating your transition
+         *                 {@code false} when the screen will blank and the wallpaper should be
+         *                 set to ambient mode immediately.
          * @hide
          */
-        public void onAmbientModeChanged(boolean inAmbientMode) {
+        public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
         }
 
         /**
@@ -1021,18 +1024,20 @@
          * Executes life cycle event and updates internal ambient mode state based on
          * message sent from handler.
          *
-         * @param inAmbientMode True if in ambient mode.
+         * @param inAmbientMode {@code true} if in ambient mode.
+         * @param animated {@code true} if the transition will be animated.
          * @hide
          */
         @VisibleForTesting
-        public void doAmbientModeChanged(boolean inAmbientMode) {
+        public void doAmbientModeChanged(boolean inAmbientMode, boolean animated) {
             if (!mDestroyed) {
                 if (DEBUG) {
-                    Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + "): " + this);
+                    Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", "
+                            + animated + "): " + this);
                 }
                 mIsInAmbientMode = inAmbientMode;
                 if (mCreated) {
-                    onAmbientModeChanged(inAmbientMode);
+                    onAmbientModeChanged(inAmbientMode, animated);
                 }
             }
         }
@@ -1278,8 +1283,10 @@
         }
 
         @Override
-        public void setInAmbientMode(boolean inAmbientDisplay) throws RemoteException {
-            Message msg = mCaller.obtainMessageI(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0);
+        public void setInAmbientMode(boolean inAmbientDisplay, boolean animated)
+                throws RemoteException {
+            Message msg = mCaller.obtainMessageII(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
+                    animated ? 1 : 0);
             mCaller.sendMessage(msg);
         }
 
@@ -1350,7 +1357,7 @@
                     return;
                 }
                 case DO_IN_AMBIENT_MODE: {
-                    mEngine.doAmbientModeChanged(message.arg1 != 0);
+                    mEngine.doAmbientModeChanged(message.arg1 != 0, message.arg2 != 0);
                     return;
                 }
                 case MSG_UPDATE_SURFACE:
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 8c90156..ad3b4b6 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -20,11 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
-import android.icu.text.DecimalFormat;
 import android.icu.text.MeasureFormat;
-import android.icu.text.NumberFormat;
-import android.icu.text.UnicodeSet;
-import android.icu.text.UnicodeSetSpanner;
 import android.icu.util.Measure;
 import android.icu.util.MeasureUnit;
 import android.net.NetworkUtils;
@@ -32,8 +28,6 @@
 import android.text.TextUtils;
 import android.view.View;
 
-import java.lang.reflect.Constructor;
-import java.math.BigDecimal;
 import java.util.Locale;
 
 /**
@@ -43,8 +37,6 @@
 public final class Formatter {
 
     /** {@hide} */
-    public static final int FLAG_DEFAULT = 0;
-    /** {@hide} */
     public static final int FLAG_SHORTER = 1 << 0;
     /** {@hide} */
     public static final int FLAG_CALCULATE_ROUNDED = 1 << 1;
@@ -66,9 +58,7 @@
         return context.getResources().getConfiguration().getLocales().get(0);
     }
 
-    /**
-     * Wraps the source string in bidi formatting characters in RTL locales.
-     */
+    /* Wraps the source string in bidi formatting characters in RTL locales */
     private static String bidiWrap(@NonNull Context context, String source) {
         final Locale locale = localeFromContext(context);
         if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) {
@@ -97,7 +87,12 @@
      * @return formatted string with the number
      */
     public static String formatFileSize(@Nullable Context context, long sizeBytes) {
-        return formatFileSize(context, sizeBytes, FLAG_DEFAULT);
+        if (context == null) {
+            return "";
+        }
+        final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0);
+        return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
+                res.value, res.units));
     }
 
     /**
@@ -105,207 +100,88 @@
      * (showing fewer digits of precision).
      */
     public static String formatShortFileSize(@Nullable Context context, long sizeBytes) {
-        return formatFileSize(context, sizeBytes, FLAG_SHORTER);
-    }
-
-    private static String formatFileSize(@Nullable Context context, long sizeBytes, int flags) {
         if (context == null) {
             return "";
         }
-        final RoundedBytesResult res = RoundedBytesResult.roundBytes(sizeBytes, flags);
-        return bidiWrap(context, formatRoundedBytesResult(context, res));
-    }
-
-    private static String getSuffixOverride(@NonNull Resources res, MeasureUnit unit) {
-        if (unit == MeasureUnit.BYTE) {
-            return res.getString(com.android.internal.R.string.byteShort);
-        } else { // unit == PETABYTE
-            return res.getString(com.android.internal.R.string.petabyteShort);
-        }
-    }
-
-    private static NumberFormat getNumberFormatter(Locale locale, int fractionDigits) {
-        final NumberFormat numberFormatter = NumberFormat.getInstance(locale);
-        numberFormatter.setMinimumFractionDigits(fractionDigits);
-        numberFormatter.setMaximumFractionDigits(fractionDigits);
-        numberFormatter.setGroupingUsed(false);
-        if (numberFormatter instanceof DecimalFormat) {
-            // We do this only for DecimalFormat, since in the general NumberFormat case, calling
-            // setRoundingMode may throw an exception.
-            numberFormatter.setRoundingMode(BigDecimal.ROUND_HALF_UP);
-        }
-        return numberFormatter;
-    }
-
-    private static String deleteFirstFromString(String source, String toDelete) {
-        final int location = source.indexOf(toDelete);
-        if (location == -1) {
-            return source;
-        } else {
-            return source.substring(0, location)
-                    + source.substring(location + toDelete.length(), source.length());
-        }
-    }
-
-    private static String formatMeasureShort(Locale locale, NumberFormat numberFormatter,
-            float value, MeasureUnit units) {
-        final MeasureFormat measureFormatter = MeasureFormat.getInstance(
-                locale, MeasureFormat.FormatWidth.SHORT, numberFormatter);
-        return measureFormatter.format(new Measure(value, units));
-    }
-
-    private static final UnicodeSetSpanner SPACES_AND_CONTROLS =
-            new UnicodeSetSpanner(new UnicodeSet("[[:Zs:][:Cf:]]").freeze());
-
-    private static String formatRoundedBytesResult(
-            @NonNull Context context, @NonNull RoundedBytesResult input) {
-        final Locale locale = localeFromContext(context);
-        final NumberFormat numberFormatter = getNumberFormatter(locale, input.fractionDigits);
-        if (input.units == MeasureUnit.BYTE || input.units == PETABYTE) {
-            // ICU spells out "byte" instead of "B", and can't format petabytes yet.
-            final String formattedNumber = numberFormatter.format(input.value);
-            return context.getString(com.android.internal.R.string.fileSizeSuffix,
-                    formattedNumber, getSuffixOverride(context.getResources(), input.units));
-        } else {
-            return formatMeasureShort(locale, numberFormatter, input.value, input.units);
-        }
+        final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER);
+        return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
+                res.value, res.units));
     }
 
     /** {@hide} */
     public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) {
-        final RoundedBytesResult rounded = RoundedBytesResult.roundBytes(sizeBytes, flags);
-        final Locale locale = res.getConfiguration().getLocales().get(0);
-        final NumberFormat numberFormatter = getNumberFormatter(locale, rounded.fractionDigits);
-        final String formattedNumber = numberFormatter.format(rounded.value);
-        final String units;
-        if (rounded.units == MeasureUnit.BYTE || rounded.units == PETABYTE) {
-            // ICU spells out "byte" instead of "B", and can't format petabytes yet.
-            units = getSuffixOverride(res, rounded.units);
-        } else {
-            // Since ICU does not give us access to the pattern, we need to extract the unit string
-            // from ICU, which we do by taking out the formatted number out of the formatted string
-            // and trimming the result of spaces and controls.
-            final String formattedMeasure = formatMeasureShort(
-                    locale, numberFormatter, rounded.value, rounded.units);
-            final String numberRemoved = deleteFirstFromString(formattedMeasure, formattedNumber);
-            units = SPACES_AND_CONTROLS.trim(numberRemoved).toString();
+        final boolean isNegative = (sizeBytes < 0);
+        float result = isNegative ? -sizeBytes : sizeBytes;
+        int suffix = com.android.internal.R.string.byteShort;
+        long mult = 1;
+        if (result > 900) {
+            suffix = com.android.internal.R.string.kilobyteShort;
+            mult = 1000;
+            result = result / 1000;
         }
-        return new BytesResult(formattedNumber, units, rounded.roundedBytes);
-    }
-
-    /**
-     * ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way.
-     */
-    private static final MeasureUnit PETABYTE = createPetaByte();
-
-    /**
-     * Create a petabyte MeasureUnit without registering it with ICU.
-     * ICU doesn't support user-create MeasureUnit and the only public (but hidden) method to do so
-     * is {@link MeasureUnit#internalGetInstance(String, String)} which also registers the unit as
-     * an available type and thus leaks it to code that doesn't expect or support it.
-     * <p>This method uses reflection to create an instance of MeasureUnit to avoid leaking it. This
-     * instance is <b>only</b> to be used in this class.
-     */
-    private static MeasureUnit createPetaByte() {
-        try {
-            Constructor<MeasureUnit> constructor = MeasureUnit.class
-                    .getDeclaredConstructor(String.class, String.class);
-            constructor.setAccessible(true);
-            return constructor.newInstance("digital", "petabyte");
-        } catch (ReflectiveOperationException e) {
-            throw new RuntimeException("Failed to create petabyte MeasureUnit", e);
+        if (result > 900) {
+            suffix = com.android.internal.R.string.megabyteShort;
+            mult *= 1000;
+            result = result / 1000;
         }
-    }
-
-    private static class RoundedBytesResult {
-        public final float value;
-        public final MeasureUnit units;
-        public final int fractionDigits;
-        public final long roundedBytes;
-
-        private RoundedBytesResult(
-                float value, MeasureUnit units, int fractionDigits, long roundedBytes) {
-            this.value = value;
-            this.units = units;
-            this.fractionDigits = fractionDigits;
-            this.roundedBytes = roundedBytes;
+        if (result > 900) {
+            suffix = com.android.internal.R.string.gigabyteShort;
+            mult *= 1000;
+            result = result / 1000;
         }
-
-        /**
-         * Returns a RoundedBytesResult object based on the input size in bytes and the rounding
-         * flags. The result can be used for formatting.
-         */
-        static RoundedBytesResult roundBytes(long sizeBytes, int flags) {
-            final boolean isNegative = (sizeBytes < 0);
-            float result = isNegative ? -sizeBytes : sizeBytes;
-            MeasureUnit units = MeasureUnit.BYTE;
-            long mult = 1;
-            if (result > 900) {
-                units = MeasureUnit.KILOBYTE;
-                mult = 1000;
-                result = result / 1000;
-            }
-            if (result > 900) {
-                units = MeasureUnit.MEGABYTE;
-                mult *= 1000;
-                result = result / 1000;
-            }
-            if (result > 900) {
-                units = MeasureUnit.GIGABYTE;
-                mult *= 1000;
-                result = result / 1000;
-            }
-            if (result > 900) {
-                units = MeasureUnit.TERABYTE;
-                mult *= 1000;
-                result = result / 1000;
-            }
-            if (result > 900) {
-                units = PETABYTE;
-                mult *= 1000;
-                result = result / 1000;
-            }
-            // Note we calculate the rounded long by ourselves, but still let NumberFormat compute
-            // the rounded value. NumberFormat.format(0.1) might not return "0.1" due to floating
-            // point errors.
-            final int roundFactor;
-            final int roundDigits;
-            if (mult == 1 || result >= 100) {
-                roundFactor = 1;
-                roundDigits = 0;
-            } else if (result < 1) {
+        if (result > 900) {
+            suffix = com.android.internal.R.string.terabyteShort;
+            mult *= 1000;
+            result = result / 1000;
+        }
+        if (result > 900) {
+            suffix = com.android.internal.R.string.petabyteShort;
+            mult *= 1000;
+            result = result / 1000;
+        }
+        // Note we calculate the rounded long by ourselves, but still let String.format()
+        // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to
+        // floating point errors.
+        final int roundFactor;
+        final String roundFormat;
+        if (mult == 1 || result >= 100) {
+            roundFactor = 1;
+            roundFormat = "%.0f";
+        } else if (result < 1) {
+            roundFactor = 100;
+            roundFormat = "%.2f";
+        } else if (result < 10) {
+            if ((flags & FLAG_SHORTER) != 0) {
+                roundFactor = 10;
+                roundFormat = "%.1f";
+            } else {
                 roundFactor = 100;
-                roundDigits = 2;
-            } else if (result < 10) {
-                if ((flags & FLAG_SHORTER) != 0) {
-                    roundFactor = 10;
-                    roundDigits = 1;
-                } else {
-                    roundFactor = 100;
-                    roundDigits = 2;
-                }
-            } else { // 10 <= result < 100
-                if ((flags & FLAG_SHORTER) != 0) {
-                    roundFactor = 1;
-                    roundDigits = 0;
-                } else {
-                    roundFactor = 100;
-                    roundDigits = 2;
-                }
+                roundFormat = "%.2f";
             }
-
-            if (isNegative) {
-                result = -result;
+        } else { // 10 <= result < 100
+            if ((flags & FLAG_SHORTER) != 0) {
+                roundFactor = 1;
+                roundFormat = "%.0f";
+            } else {
+                roundFactor = 100;
+                roundFormat = "%.2f";
             }
-
-            // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like
-            // 80PB so it's okay (for now)...
-            final long roundedBytes =
-                    (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0
-                    : (((long) Math.round(result * roundFactor)) * mult / roundFactor);
-
-            return new RoundedBytesResult(result, units, roundDigits, roundedBytes);
         }
+
+        if (isNegative) {
+            result = -result;
+        }
+        final String roundedString = String.format(roundFormat, result);
+
+        // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so
+        // it's okay (for now)...
+        final long roundedBytes =
+                (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0
+                : (((long) Math.round(result * roundFactor)) * mult / roundFactor);
+
+        final String units = res.getString(suffix);
+
+        return new BytesResult(roundedString, units, roundedBytes);
     }
 
     /**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index bfb5130..d31bc1f 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -38,7 +38,6 @@
     static {
         DEFAULT_FLAGS = new HashMap<>();
         DEFAULT_FLAGS.put("device_info_v2", "true");
-        DEFAULT_FLAGS.put("new_settings_suggestion", "true");
         DEFAULT_FLAGS.put("settings_search_v2", "true");
         DEFAULT_FLAGS.put("settings_app_info_v2", "false");
         DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index 26a3c36..c25b272 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -53,7 +53,7 @@
      * @return true if successful
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
+    public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
@@ -76,7 +76,7 @@
      * @return true if successful
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean removeConfiguration(String configKey) {
+    public boolean removeConfiguration(long configKey) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
@@ -100,7 +100,7 @@
      * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getData(String configKey) {
+    public byte[] getData(long configKey) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index e448f14..3dcfd00 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.view.DisplayCutoutProto.BOUNDS;
+import static android.view.DisplayCutoutProto.INSETS;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
@@ -28,6 +30,7 @@
 import android.graphics.Region;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -155,6 +158,16 @@
     }
 
     /**
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        mSafeInsets.writeToProto(proto, INSETS);
+        mBounds.getBounds().writeToProto(proto, BOUNDS);
+        proto.end(token);
+    }
+
+    /**
      * Insets the reference frame of the cutout in the given directions.
      *
      * @return a copy of this instance which has been inset
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 998fd01..3fd4696 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -461,8 +461,10 @@
                     + "refer to a bitmap drawable.");
         }
 
+        final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+        validateHotSpot(bitmap, hotSpotX, hotSpotY);
         // Set the properties now that we have successfully loaded the icon.
-        mBitmap = ((BitmapDrawable)drawable).getBitmap();
+        mBitmap = bitmap;
         mHotSpotX = hotSpotX;
         mHotSpotY = hotSpotY;
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cc63a62..f62189e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4182,6 +4182,11 @@
      */
     private static boolean sUseDefaultFocusHighlight;
 
+    /**
+     * True if zero-sized views can be focused.
+     */
+    private static boolean sCanFocusZeroSized;
+
     private String mTransitionName;
 
     static class TintInfo {
@@ -4798,6 +4803,8 @@
 
             sThrowOnInvalidFloatProperties = targetSdkVersion >= Build.VERSION_CODES.P;
 
+            sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P;
+
             sCompatibilityDone = true;
         }
     }
@@ -7010,6 +7017,7 @@
     void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
         if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
             mPrivateFlags &= ~PFLAG_FOCUSED;
+            clearParentsWantFocus();
 
             if (propagate && mParent != null) {
                 mParent.clearChildFocus(this);
@@ -10046,6 +10054,13 @@
     }
 
     /**
+     * @return {@code true} if laid-out and not about to do another layout.
+     */
+    boolean isLayoutValid() {
+        return isLaidOut() && ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == 0);
+    }
+
+    /**
      * If this view doesn't do any drawing on its own, set this flag to
      * allow further optimizations. By default, this flag is not set on
      * View, but could be set on some View subclasses such as ViewGroup.
@@ -10817,7 +10832,7 @@
         if (views == null) {
             return;
         }
-        if (!isFocusable() || !isEnabled()) {
+        if (!canTakeFocus()) {
             return;
         }
         if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
@@ -11031,8 +11046,9 @@
      * descendants.
      *
      * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns
-     * false), or if it is focusable and it is not focusable in touch mode
-     * ({@link #isFocusableInTouchMode}) while the device is in touch mode.
+     * false), or if it can't be focused due to other conditions (not focusable in touch mode
+     * ({@link #isFocusableInTouchMode}) while the device is in touch mode, not visible, not
+     * enabled, or has no size).
      *
      * See also {@link #focusSearch(int)}, which is what you call to say that you
      * have focus, and you want your parent to look for the next one.
@@ -11139,9 +11155,7 @@
 
     private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
         // need to be focusable
-        if ((mViewFlags & FOCUSABLE) != FOCUSABLE
-                || (mViewFlags & VISIBILITY_MASK) != VISIBLE
-                || (mViewFlags & ENABLED_MASK) != ENABLED) {
+        if (!canTakeFocus()) {
             return false;
         }
 
@@ -11156,10 +11170,21 @@
             return false;
         }
 
+        if (!isLayoutValid()) {
+            mPrivateFlags |= PFLAG_WANTS_FOCUS;
+        }
+
         handleFocusGainInternal(direction, previouslyFocusedRect);
         return true;
     }
 
+    void clearParentsWantFocus() {
+        if (mParent instanceof View) {
+            ((View) mParent).mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+            ((View) mParent).clearParentsWantFocus();
+        }
+    }
+
     /**
      * Call this to try to give focus to a specific view or to one of its descendants. This is a
      * special variant of {@link #requestFocus() } that will allow views that are not focusable in
@@ -13531,6 +13556,13 @@
         mAttachInfo.mUnbufferedDispatchRequested = true;
     }
 
+    private boolean canTakeFocus() {
+        return ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
+                && ((mViewFlags & FOCUSABLE) == FOCUSABLE)
+                && ((mViewFlags & ENABLED_MASK) == ENABLED)
+                && (sCanFocusZeroSized || !isLayoutValid() || (mBottom > mTop) && (mRight > mLeft));
+    }
+
     /**
      * Set flags controlling behavior of this view.
      *
@@ -13550,6 +13582,7 @@
             return;
         }
         int privateFlags = mPrivateFlags;
+        boolean shouldNotifyFocusableAvailable = false;
 
         // If focusable is auto, update the FOCUSABLE bit.
         int focusableChangedByAuto = 0;
@@ -13588,7 +13621,7 @@
                             || focusableChangedByAuto == 0
                             || viewRootImpl == null
                             || viewRootImpl.mThread == Thread.currentThread()) {
-                        mParent.focusableViewAvailable(this);
+                        shouldNotifyFocusableAvailable = true;
                     }
                 }
             }
@@ -13611,10 +13644,7 @@
                 // about in case nothing has focus.  even if this specific view
                 // isn't focusable, it may contain something that is, so let
                 // the root view try to give this focus if nothing else does.
-                if ((mParent != null) && ((mViewFlags & ENABLED_MASK) == ENABLED)
-                        && (mBottom > mTop) && (mRight > mLeft)) {
-                    mParent.focusableViewAvailable(this);
-                }
+                shouldNotifyFocusableAvailable = true;
             }
         }
 
@@ -13623,17 +13653,18 @@
                 // a view becoming enabled should notify the parent as long as the view is also
                 // visible and the parent wasn't already notified by becoming visible during this
                 // setFlags invocation.
-                if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
-                        && ((changed & VISIBILITY_MASK) == 0)) {
-                    if ((mParent != null) && (mViewFlags & ENABLED_MASK) == ENABLED) {
-                        mParent.focusableViewAvailable(this);
-                    }
-                }
+                shouldNotifyFocusableAvailable = true;
             } else {
                 if (hasFocus()) clearFocus();
             }
         }
 
+        if (shouldNotifyFocusableAvailable) {
+            if (mParent != null && canTakeFocus()) {
+                mParent.focusableViewAvailable(this);
+            }
+        }
+
         /* Check if the GONE bit has changed */
         if ((changed & GONE) != 0) {
             needGlobalAttributesUpdate(false);
@@ -18936,7 +18967,7 @@
      *
      * @hide
      */
-    public Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
+    public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) {
         int width = mRight - mLeft;
         int height = mBottom - mTop;
 
@@ -18945,71 +18976,48 @@
         width = (int) ((width * scale) + 0.5f);
         height = (int) ((height * scale) + 0.5f);
 
-        Bitmap bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
-                width > 0 ? width : 1, height > 0 ? height : 1, quality);
-        if (bitmap == null) {
-            throw new OutOfMemoryError();
-        }
+        Canvas oldCanvas = null;
+        try {
+            Canvas canvas = canvasProvider.getCanvas(this,
+                    width > 0 ? width : 1, height > 0 ? height : 1);
 
-        Resources resources = getResources();
-        if (resources != null) {
-            bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
-        }
-
-        Canvas canvas;
-        if (attachInfo != null) {
-            canvas = attachInfo.mCanvas;
-            if (canvas == null) {
-                canvas = new Canvas();
+            if (attachInfo != null) {
+                oldCanvas = attachInfo.mCanvas;
+                // Temporarily clobber the cached Canvas in case one of our children
+                // is also using a drawing cache. Without this, the children would
+                // steal the canvas by attaching their own bitmap to it and bad, bad
+                // things would happen (invisible views, corrupted drawings, etc.)
+                attachInfo.mCanvas = null;
             }
-            canvas.setBitmap(bitmap);
-            // Temporarily clobber the cached Canvas in case one of our children
-            // is also using a drawing cache. Without this, the children would
-            // steal the canvas by attaching their own bitmap to it and bad, bad
-            // things would happen (invisible views, corrupted drawings, etc.)
-            attachInfo.mCanvas = null;
-        } else {
-            // This case should hopefully never or seldom happen
-            canvas = new Canvas(bitmap);
-        }
-        boolean enabledHwBitmapsInSwMode = canvas.isHwBitmapsInSwModeEnabled();
-        canvas.setHwBitmapsInSwModeEnabled(true);
-        if ((backgroundColor & 0xff000000) != 0) {
-            bitmap.eraseColor(backgroundColor);
-        }
 
-        computeScroll();
-        final int restoreCount = canvas.save();
-        canvas.scale(scale, scale);
-        canvas.translate(-mScrollX, -mScrollY);
+            computeScroll();
+            final int restoreCount = canvas.save();
+            canvas.scale(scale, scale);
+            canvas.translate(-mScrollX, -mScrollY);
 
-        // Temporarily remove the dirty mask
-        int flags = mPrivateFlags;
-        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
+            // Temporarily remove the dirty mask
+            int flags = mPrivateFlags;
+            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
 
-        // Fast path for layouts with no backgrounds
-        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
-            dispatchDraw(canvas);
-            drawAutofilledHighlight(canvas);
-            if (mOverlay != null && !mOverlay.isEmpty()) {
-                mOverlay.getOverlayView().draw(canvas);
+            // Fast path for layouts with no backgrounds
+            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
+                dispatchDraw(canvas);
+                drawAutofilledHighlight(canvas);
+                if (mOverlay != null && !mOverlay.isEmpty()) {
+                    mOverlay.getOverlayView().draw(canvas);
+                }
+            } else {
+                draw(canvas);
             }
-        } else {
-            draw(canvas);
+
+            mPrivateFlags = flags;
+            canvas.restoreToCount(restoreCount);
+            return canvasProvider.createBitmap();
+        } finally {
+            if (oldCanvas != null) {
+                attachInfo.mCanvas = oldCanvas;
+            }
         }
-
-        mPrivateFlags = flags;
-
-        canvas.restoreToCount(restoreCount);
-        canvas.setBitmap(null);
-        canvas.setHwBitmapsInSwModeEnabled(enabledHwBitmapsInSwMode);
-
-        if (attachInfo != null) {
-            // Restore the cached Canvas for our siblings
-            attachInfo.mCanvas = canvas;
-        }
-
-        return bitmap;
     }
 
     /**
@@ -20160,15 +20168,58 @@
             }
         }
 
+        final boolean wasLayoutValid = isLayoutValid();
+
         mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
         mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
 
+        if (!wasLayoutValid && isFocused()) {
+            mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+            if (canTakeFocus()) {
+                // We have a robust focus, so parents should no longer be wanting focus.
+                clearParentsWantFocus();
+            } else if (!getViewRootImpl().isInLayout()) {
+                // This is a weird case. Most-likely the user, rather than ViewRootImpl, called
+                // layout. In this case, there's no guarantee that parent layouts will be evaluated
+                // and thus the safest action is to clear focus here.
+                clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+                clearParentsWantFocus();
+            } else if (!hasParentWantsFocus()) {
+                // original requestFocus was likely on this view directly, so just clear focus
+                clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+            }
+            // otherwise, we let parents handle re-assigning focus during their layout passes.
+        } else if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
+            mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+            View focused = findFocus();
+            if (focused != null) {
+                // Try to restore focus as close as possible to our starting focus.
+                if (!restoreDefaultFocus() && !hasParentWantsFocus()) {
+                    // Give up and clear focus once we've reached the top-most parent which wants
+                    // focus.
+                    focused.clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+                }
+            }
+        }
+
         if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
             mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
             notifyEnterOrExitForAutoFillIfNeeded(true);
         }
     }
 
+    private boolean hasParentWantsFocus() {
+        ViewParent parent = mParent;
+        while (parent instanceof ViewGroup) {
+            ViewGroup pv = (ViewGroup) parent;
+            if ((pv.mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
+                return true;
+            }
+            parent = pv.mParent;
+        }
+        return false;
+    }
+
     /**
      * Called from layout when this view should
      * assign a size and position to each of its children.
@@ -20275,6 +20326,23 @@
             mOverlay.getOverlayView().setRight(newWidth);
             mOverlay.getOverlayView().setBottom(newHeight);
         }
+        // If this isn't laid out yet, focus assignment will be handled during the "deferment/
+        // backtracking" of requestFocus during layout, so don't touch focus here.
+        if (!sCanFocusZeroSized && isLayoutValid()) {
+            if (newWidth <= 0 || newHeight <= 0) {
+                if (hasFocus()) {
+                    clearFocus();
+                    if (mParent instanceof ViewGroup) {
+                        ((ViewGroup) mParent).clearFocusedInCluster();
+                    }
+                }
+                clearAccessibilityFocus();
+            } else if (oldWidth <= 0 || oldHeight <= 0) {
+                if (mParent != null && canTakeFocus()) {
+                    mParent.focusableViewAvailable(this);
+                }
+            }
+        }
         rebuildOutline();
     }
 
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index afa9413..b09934e 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
@@ -773,16 +774,15 @@
             final CountDownLatch latch = new CountDownLatch(1);
             final Bitmap[] cache = new Bitmap[1];
 
-            captureView.post(new Runnable() {
-                public void run() {
-                    try {
-                        cache[0] = captureView.createSnapshot(
-                                Bitmap.Config.ARGB_8888, 0, skipChildren);
-                    } catch (OutOfMemoryError e) {
-                        Log.w("View", "Out of memory for bitmap");
-                    } finally {
-                        latch.countDown();
-                    }
+            captureView.post(() -> {
+                try {
+                    CanvasProvider provider = captureView.isHardwareAccelerated()
+                            ? new HardwareCanvasProvider() : new SoftwareCanvasProvider();
+                    cache[0] = captureView.createSnapshot(provider, skipChildren);
+                } catch (OutOfMemoryError e) {
+                    Log.w("View", "Out of memory for bitmap");
+                } finally {
+                    latch.countDown();
                 }
             });
 
@@ -1740,4 +1740,86 @@
             }
         });
     }
+
+    /**
+     * @hide
+     */
+    public static class SoftwareCanvasProvider implements CanvasProvider {
+
+        private Canvas mCanvas;
+        private Bitmap mBitmap;
+        private boolean mEnabledHwBitmapsInSwMode;
+
+        @Override
+        public Canvas getCanvas(View view, int width, int height) {
+            mBitmap = Bitmap.createBitmap(view.getResources().getDisplayMetrics(),
+                    width, height, Bitmap.Config.ARGB_8888);
+            if (mBitmap == null) {
+                throw new OutOfMemoryError();
+            }
+            mBitmap.setDensity(view.getResources().getDisplayMetrics().densityDpi);
+
+            if (view.mAttachInfo != null) {
+                mCanvas = view.mAttachInfo.mCanvas;
+            }
+            if (mCanvas == null) {
+                mCanvas = new Canvas();
+            }
+            mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled();
+            mCanvas.setBitmap(mBitmap);
+            return mCanvas;
+        }
+
+        @Override
+        public Bitmap createBitmap() {
+            mCanvas.setBitmap(null);
+            mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode);
+            return mBitmap;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static class HardwareCanvasProvider implements CanvasProvider {
+
+        private View mView;
+        private Point mSize;
+        private RenderNode mNode;
+        private DisplayListCanvas mCanvas;
+
+        @Override
+        public Canvas getCanvas(View view, int width, int height) {
+            mView = view;
+            mSize = new Point(width, height);
+            mNode = RenderNode.create("ViewDebug", mView);
+            mNode.setLeftTopRightBottom(0, 0, width, height);
+            mNode.setClipToBounds(false);
+            mCanvas = mNode.start(width, height);
+            return mCanvas;
+        }
+
+        @Override
+        public Bitmap createBitmap() {
+            mNode.end(mCanvas);
+            return ThreadedRenderer.createHardwareBitmap(mNode, mSize.x, mSize.y);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public interface CanvasProvider {
+
+        /**
+         * Returns a canvas which can be used to draw {@param view}
+         */
+        Canvas getCanvas(View view, int width, int height);
+
+        /**
+         * Creates a bitmap from previously returned canvas
+         * @return
+         */
+        Bitmap createBitmap();
+    }
 }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 122df93..703364f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3215,22 +3215,31 @@
         }
         int descendantFocusability = getDescendantFocusability();
 
+        boolean result;
         switch (descendantFocusability) {
             case FOCUS_BLOCK_DESCENDANTS:
-                return super.requestFocus(direction, previouslyFocusedRect);
+                result = super.requestFocus(direction, previouslyFocusedRect);
+                break;
             case FOCUS_BEFORE_DESCENDANTS: {
                 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
-                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
+                result = took ? took : onRequestFocusInDescendants(direction,
+                        previouslyFocusedRect);
+                break;
             }
             case FOCUS_AFTER_DESCENDANTS: {
                 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
-                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
+                result = took ? took : super.requestFocus(direction, previouslyFocusedRect);
+                break;
             }
             default:
                 throw new IllegalStateException("descendant focusability must be "
                         + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
                         + "but is " + descendantFocusability);
         }
+        if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
+            mPrivateFlags |= PFLAG_WANTS_FOCUS;
+        }
+        return result;
     }
 
     /**
@@ -3854,7 +3863,7 @@
      * @hide
      */
     @Override
-    public Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
+    public Bitmap createSnapshot(ViewDebug.CanvasProvider canvasProvider, boolean skipChildren) {
         int count = mChildrenCount;
         int[] visibilities = null;
 
@@ -3870,17 +3879,17 @@
             }
         }
 
-        Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
-
-        if (skipChildren) {
-            for (int i = 0; i < count; i++) {
-                View child = getChildAt(i);
-                child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
-                        | (visibilities[i] & View.VISIBILITY_MASK);
+        try {
+            return super.createSnapshot(canvasProvider, skipChildren);
+        } finally {
+            if (skipChildren) {
+                for (int i = 0; i < count; i++) {
+                    View child = getChildAt(i);
+                    child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
+                            | (visibilities[i] & View.VISIBILITY_MASK);
+                }
             }
         }
-
-        return b;
     }
 
     /** Return true if this ViewGroup is laying out using optical bounds. */
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index d665dde..1d94abe 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -26,6 +26,8 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.List;
 
 /**
@@ -204,6 +206,16 @@
     public abstract void setTextLines(int[] charOffsets, int[] baselines);
 
     /**
+     * Sets the identifier used to set the text associated with this view.
+     *
+     * <p>Should only be set when the node is used for autofill purposes - it will be ignored
+     * when used for Assist.
+     */
+    public void setTextIdEntry(@NonNull String entryName) {
+        Preconditions.checkNotNull(entryName);
+    }
+
+    /**
      * Set optional hint text associated with this view; this is for example the text that is
      * shown by an EditText when it is empty to indicate to the user the kind of text to input.
      */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cbe012a..a65aba1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -17,6 +17,32 @@
 package android.view;
 
 import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
+import static android.view.WindowLayoutParamsProto.ALPHA;
+import static android.view.WindowLayoutParamsProto.BUTTON_BRIGHTNESS;
+import static android.view.WindowLayoutParamsProto.COLOR_MODE;
+import static android.view.WindowLayoutParamsProto.FLAGS;
+import static android.view.WindowLayoutParamsProto.FLAGS_EXTRA;
+import static android.view.WindowLayoutParamsProto.FORMAT;
+import static android.view.WindowLayoutParamsProto.GRAVITY;
+import static android.view.WindowLayoutParamsProto.HAS_SYSTEM_UI_LISTENERS;
+import static android.view.WindowLayoutParamsProto.HEIGHT;
+import static android.view.WindowLayoutParamsProto.HORIZONTAL_MARGIN;
+import static android.view.WindowLayoutParamsProto.INPUT_FEATURE_FLAGS;
+import static android.view.WindowLayoutParamsProto.NEEDS_MENU_KEY;
+import static android.view.WindowLayoutParamsProto.PREFERRED_REFRESH_RATE;
+import static android.view.WindowLayoutParamsProto.PRIVATE_FLAGS;
+import static android.view.WindowLayoutParamsProto.ROTATION_ANIMATION;
+import static android.view.WindowLayoutParamsProto.SCREEN_BRIGHTNESS;
+import static android.view.WindowLayoutParamsProto.SOFT_INPUT_MODE;
+import static android.view.WindowLayoutParamsProto.SUBTREE_SYSTEM_UI_VISIBILITY_FLAGS;
+import static android.view.WindowLayoutParamsProto.SYSTEM_UI_VISIBILITY_FLAGS;
+import static android.view.WindowLayoutParamsProto.TYPE;
+import static android.view.WindowLayoutParamsProto.USER_ACTIVITY_TIMEOUT;
+import static android.view.WindowLayoutParamsProto.VERTICAL_MARGIN;
+import static android.view.WindowLayoutParamsProto.WIDTH;
+import static android.view.WindowLayoutParamsProto.WINDOW_ANIMATIONS;
+import static android.view.WindowLayoutParamsProto.X;
+import static android.view.WindowLayoutParamsProto.Y;
 
 import android.Manifest.permission;
 import android.annotation.IntDef;
@@ -2722,7 +2748,33 @@
          */
         public void writeToProto(ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
-            proto.write(WindowLayoutParamsProto.TYPE, type);
+            proto.write(TYPE, type);
+            proto.write(X, x);
+            proto.write(Y, y);
+            proto.write(WIDTH, width);
+            proto.write(HEIGHT, height);
+            proto.write(HORIZONTAL_MARGIN, horizontalMargin);
+            proto.write(VERTICAL_MARGIN, verticalMargin);
+            proto.write(GRAVITY, gravity);
+            proto.write(SOFT_INPUT_MODE, softInputMode);
+            proto.write(FORMAT, format);
+            proto.write(WINDOW_ANIMATIONS, windowAnimations);
+            proto.write(ALPHA, alpha);
+            proto.write(SCREEN_BRIGHTNESS, screenBrightness);
+            proto.write(BUTTON_BRIGHTNESS, buttonBrightness);
+            proto.write(ROTATION_ANIMATION, rotationAnimation);
+            proto.write(PREFERRED_REFRESH_RATE, preferredRefreshRate);
+            proto.write(WindowLayoutParamsProto.PREFERRED_DISPLAY_MODE_ID, preferredDisplayModeId);
+            proto.write(HAS_SYSTEM_UI_LISTENERS, hasSystemUiListeners);
+            proto.write(INPUT_FEATURE_FLAGS, inputFeatures);
+            proto.write(USER_ACTIVITY_TIMEOUT, userActivityTimeout);
+            proto.write(NEEDS_MENU_KEY, needsMenuKey);
+            proto.write(COLOR_MODE, mColorMode);
+            proto.write(FLAGS, flags);
+            proto.write(FLAGS_EXTRA, flags2);
+            proto.write(PRIVATE_FLAGS, privateFlags);
+            proto.write(SYSTEM_UI_VISIBILITY_FLAGS, systemUiVisibility);
+            proto.write(SUBTREE_SYSTEM_UI_VISIBILITY_FLAGS, subtreeSystemUiVisibility);
             proto.end(token);
         }
 
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 1d19a9f..6c2d349 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -38,19 +38,14 @@
  * <p>
  * An accessibility event is fired by an individual view which populates the event with
  * data for its state and requests from its parent to send the event to interested
- * parties. The parent can optionally add an {@link AccessibilityRecord} for itself before
- * dispatching a similar request to its parent. A parent can also choose not to respect the
- * request for sending an event. The accessibility event is sent by the topmost view in the
- * view tree. Therefore, an {@link android.accessibilityservice.AccessibilityService} can
- * explore all records in an accessibility event to obtain more information about the
- * context in which the event was fired.
+ * parties. The parent can optionally modify or even block the event based on its broader
+ * understanding of the user interface's context.
  * </p>
  * <p>
- * The main purpose of an accessibility event is to expose enough information for an
- * {@link android.accessibilityservice.AccessibilityService} to provide meaningful feedback
- * to the user. Sometimes however, an accessibility service may need more contextual
- * information then the one in the event pay-load. In such cases the service can obtain
- * the event source which is an {@link AccessibilityNodeInfo} (snapshot of a View state)
+ * The main purpose of an accessibility event is to communicate changes in the UI to an
+ * {@link android.accessibilityservice.AccessibilityService}. The service may then inspect,
+ * if needed the user interface by examining the View hierarchy, as represented by a tree of
+ * {@link AccessibilityNodeInfo}s (snapshot of a View state)
  * which can be used for exploring the window content. Note that the privilege for accessing
  * an event's source, thus the window content, has to be explicitly requested. For more
  * details refer to {@link android.accessibilityservice.AccessibilityService}. If an
@@ -85,21 +80,6 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #isPassword()} - Whether the source is password.</li>
- *   <li>{@link #isChecked()} - Whether the source is checked.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getScrollX()} - The offset of the source left edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getScrollY()} - The offset of the source top edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getToIndex()} - The zero based index of the last visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getItemCount()} - The total items of the source
- *       (for descendants of AdapterView).</li>
  * </ul>
  * </p>
  * <p>
@@ -113,21 +93,6 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #isPassword()} - Whether the source is password.</li>
- *   <li>{@link #isChecked()} - Whether the source is checked.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getScrollX()} - The offset of the source left edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getScrollY()} - The offset of the source top edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getToIndex()} - The zero based index of the last visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getItemCount()} - The total items of the source
- *       (for descendants of AdapterView).</li>
  * </ul>
  * </p>
  * <p>
@@ -141,23 +106,6 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #isPassword()} - Whether the source is password.</li>
- *   <li>{@link #isChecked()} - Whether the source is checked.</li>
- *   <li>{@link #getItemCount()} - The number of selectable items of the source.</li>
- *   <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getScrollX()} - The offset of the source left edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getScrollY()} - The offset of the source top edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getToIndex()} - The zero based index of the last visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getItemCount()} - The total items of the source
- *       (for descendants of AdapterView).</li>
  * </ul>
  * </p>
  * <p>
@@ -171,23 +119,6 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #isPassword()} - Whether the source is password.</li>
- *   <li>{@link #isChecked()} - Whether the source is checked.</li>
- *   <li>{@link #getItemCount()} - The number of focusable items on the screen.</li>
- *   <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getScrollX()} - The offset of the source left edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getScrollY()} - The offset of the source top edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getToIndex()} - The zero based index of the last visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getItemCount()} - The total items of the source
- *       (for descendants of AdapterView).</li>
  * </ul>
  * </p>
  * <p>
@@ -201,15 +132,11 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #isPassword()} - Whether the source is password.</li>
- *   <li>{@link #isChecked()} - Whether the source is checked.</li>
+ *   <li>{@link #getText()} - The new text of the source.</li>
+ *   <li>{@link #getBeforeText()} - The text of the source before the change.</li>
  *   <li>{@link #getFromIndex()} - The text change start index.</li>
  *   <li>{@link #getAddedCount()} - The number of added characters.</li>
  *   <li>{@link #getRemovedCount()} - The number of removed characters.</li>
- *   <li>{@link #getBeforeText()} - The text of the source before the change.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
  * </ul>
  * </p>
  * <p>
@@ -223,13 +150,6 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source.</li>
- *   <li>{@link #isPassword()} - Whether the source is password.</li>
- *   <li>{@link #getFromIndex()} - The selection start index.</li>
- *   <li>{@link #getToIndex()} - The selection end index.</li>
- *   <li>{@link #getItemCount()} - The length of the source text.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
  * </ul>
  * </p>
  * <b>View text traversed at movement granularity</b> - represents the event of traversing the
@@ -251,23 +171,11 @@
  *   <li>{@link #getToIndex()} - The end of the text that was skipped over in this movement.
  *       This is the ending point when moving forward through the text, but not when moving
  *       back.</li>
- *   <li>{@link #isPassword()} - Whether the source is password.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text
- *       was traversed.</li>
  *   <li>{@link #getAction()} - Gets traversal action which specifies the direction.</li>
  * </ul>
  * </p>
  * <p>
- * <b>View scrolled</b> - represents the event of scrolling a view. If
- * the source is a descendant of {@link android.widget.AdapterView} the
- * scroll is reported in terms of visible items - the first visible item,
- * the last visible item, and the total items - because the the source
- * is unaware of its pixel size since its adapter is responsible for
- * creating views. In all other cases the scroll is reported as the current
- * scroll on the X and Y axis respectively plus the height of the source in
- * pixels.</br>
+ * <b>View scrolled</b> - represents the event of scrolling a view. </br>
  * <em>Type:</em> {@link #TYPE_VIEW_SCROLLED}</br>
  * <em>Properties:</em></br>
  * <ul>
@@ -276,29 +184,9 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getScrollX()} - The offset of the source left edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getScrollY()} - The offset of the source top edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getToIndex()} - The zero based index of the last visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getItemCount()} - The total items of the source
- *       (for descendants of AdapterView).</li>
+ *   <li>{@link #getScrollDeltaX()} - The difference in the horizontal position.</li>
+ *   <li>{@link #getScrollDeltaY()} - The difference in the vertical position.</li>
  * </ul>
- * <em>Note:</em> This event type is not dispatched to descendants though
- * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
- * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event
- * source {@link android.view.View} and the sub-tree rooted at it will not receive
- * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent)
- * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add
- * text content to such events is by setting the
- * {@link android.R.styleable#View_contentDescription contentDescription} of the source
- * view.</br>
  * </p>
  * <p>
  * <b>TRANSITION TYPES</b></br>
@@ -316,7 +204,6 @@
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
  *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
  * </ul>
  * </p>
  * <p>
@@ -325,10 +212,6 @@
  * a view size, etc.</br>
  * </p>
  * <p>
- * <strong>Note:</strong> This event is fired only for the window source of the
- * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED}
- * and its purpose is to notify clients that the content of the user interaction
- * window has changed.</br>
  * <em>Type:</em> {@link #TYPE_WINDOW_CONTENT_CHANGED}</br>
  * <em>Properties:</em></br>
  * <ul>
@@ -339,32 +222,26 @@
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
  * </ul>
- * <em>Note:</em> This event type is not dispatched to descendants though
- * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
- * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event
- * source {@link android.view.View} and the sub-tree rooted at it will not receive
- * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent)
- * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add
- * text content to such events is by setting the
- * {@link android.R.styleable#View_contentDescription contentDescription} of the source
- * view.</br>
  * </p>
  * <p>
- * <b>Windows changed</b> - represents the event of changes in the windows shown on
+ * <b>Windows changed</b> - represents a change in the windows shown on
  * the screen such as a window appeared, a window disappeared, a window size changed,
- * a window layer changed, etc.</br>
+ * a window layer changed, etc. These events should only come from the system, which is responsible
+ * for managing windows. The list of windows is available from
+ * {@link android.accessibilityservice.AccessibilityService#getWindows()}.
+ * For regions of the user interface that are presented as windows but are
+ * controlled by an app's process, use {@link #TYPE_WINDOW_STATE_CHANGED}.</br>
  * <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br>
  * <em>Properties:</em></br>
  * <ul>
  *   <li>{@link #getEventType()} - The type of the event.</li>
  *   <li>{@link #getEventTime()} - The event time.</li>
+ *   <li>{@link #getWindowChanges()}</li> - The specific change to the source window
  * </ul>
  * <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window
- * source of the event via {@link AccessibilityEvent#getSource()} to get the source
- * node on which then call {@link AccessibilityNodeInfo#getWindow()
- * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can
- * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows()
- * android.accessibilityservice.AccessibilityService.getWindows()}.
+ * source of the event by looking through the list returned by
+ * {@link android.accessibilityservice.AccessibilityService#getWindows()} for the window whose ID
+ * matches {@link #getWindowId()}.
  * </p>
  * <p>
  * <b>NOTIFICATION TYPES</b></br>
@@ -402,19 +279,6 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getScrollX()} - The offset of the source left edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getScrollY()} - The offset of the source top edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getToIndex()} - The zero based index of the last visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getItemCount()} - The total items of the source
- *       (for descendants of AdapterView).</li>
  * </ul>
  * </p>
  * <b>View hover exit</b> - represents the event of stopping to hover
@@ -428,19 +292,6 @@
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
- *   <li>{@link #getText()} - The text of the source's sub-tree.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
- *   <li>{@link #getContentDescription()} - The content description of the source.</li>
- *   <li>{@link #getScrollX()} - The offset of the source left edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getScrollY()} - The offset of the source top edge in pixels
- *       (without descendants of AdapterView).</li>
- *   <li>{@link #getFromIndex()} - The zero based index of the first visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getToIndex()} - The zero based index of the last visible item of the source,
- *       inclusive (for descendants of AdapterView).</li>
- *   <li>{@link #getItemCount()} - The total items of the source
- *       (for descendants of AdapterView).</li>
  * </ul>
  * </p>
  * <p>
@@ -513,10 +364,10 @@
  * <b>MISCELLANEOUS TYPES</b></br>
  * </p>
  * <p>
- * <b>Announcement</b> - represents the event of an application making an
- * announcement. Usually this announcement is related to some sort of a context
- * change for which none of the events representing UI transitions is a good fit.
- * For example, announcing a new page in a book.</br>
+ * <b>Announcement</b> - represents the event of an application requesting a screen reader to make
+ * an announcement. Because the event carries no semantic meaning, this event is appropriate only
+ * in exceptional situations where additional screen reader output is needed but other types of
+ * accessibility services do not need to be aware of the change.</br>
  * <em>Type:</em> {@link #TYPE_ANNOUNCEMENT}</br>
  * <em>Properties:</em></br>
  * <ul>
@@ -526,7 +377,6 @@
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
  *   <li>{@link #getEventTime()}  - The event time.</li>
  *   <li>{@link #getText()} - The text of the announcement.</li>
- *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
  * </ul>
  * </p>
  *
@@ -674,7 +524,8 @@
     public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
 
     /**
-     * Represents the event change in the windows shown on the screen.
+     * Represents the event change in the system windows shown on the screen. This event type should
+     * only be dispatched by the system.
      */
     public static final int TYPE_WINDOWS_CHANGED = 0x00400000;
 
@@ -696,7 +547,8 @@
 
     /**
      * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
-     * A node in the subtree rooted at the source node was added or removed.
+     * One or more content changes occurred in the the subtree rooted at the source node,
+     * or the subtree's structure changed when a node was added or removed.
      */
     public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0x00000001;
 
@@ -712,6 +564,99 @@
      */
     public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
 
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window was added.
+     */
+    public static final int WINDOWS_CHANGE_ADDED = 0x00000001;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * A window was removed.
+     */
+    public static final int WINDOWS_CHANGE_REMOVED = 0x00000002;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's title changed.
+     */
+    public static final int WINDOWS_CHANGE_TITLE = 0x00000004;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's bounds changed.
+     */
+    public static final int WINDOWS_CHANGE_BOUNDS = 0x00000008;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's layer changed.
+     */
+    public static final int WINDOWS_CHANGE_LAYER = 0x00000010;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's {@link AccessibilityWindowInfo#isActive()} changed.
+     */
+    public static final int WINDOWS_CHANGE_ACTIVE = 0x00000020;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's {@link AccessibilityWindowInfo#isFocused()} changed.
+     */
+    public static final int WINDOWS_CHANGE_FOCUSED = 0x00000040;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's {@link AccessibilityWindowInfo#isAccessibilityFocused()} changed.
+     */
+    public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 0x00000080;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's parent changed.
+     */
+    public static final int WINDOWS_CHANGE_PARENT = 0x00000100;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window's children changed.
+     */
+    public static final int WINDOWS_CHANGE_CHILDREN = 0x00000200;
+
+    /**
+     * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+     * The window either entered or exited picture-in-picture mode.
+     */
+    public static final int WINDOWS_CHANGE_PIP = 0x00000400;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "WINDOWS_CHANGE_" }, value = {
+            WINDOWS_CHANGE_ADDED,
+            WINDOWS_CHANGE_REMOVED,
+            WINDOWS_CHANGE_TITLE,
+            WINDOWS_CHANGE_BOUNDS,
+            WINDOWS_CHANGE_LAYER,
+            WINDOWS_CHANGE_ACTIVE,
+            WINDOWS_CHANGE_FOCUSED,
+            WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED,
+            WINDOWS_CHANGE_PARENT,
+            WINDOWS_CHANGE_CHILDREN,
+            WINDOWS_CHANGE_PIP
+    })
+    public @interface WindowsChangeTypes {}
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "CONTENT_CHANGE_TYPE_" },
+            value = {
+                    CONTENT_CHANGE_TYPE_UNDEFINED,
+                    CONTENT_CHANGE_TYPE_SUBTREE,
+                    CONTENT_CHANGE_TYPE_TEXT,
+                    CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION
+            })
+    public @interface ContentChangeTypes {}
 
     /** @hide */
     @IntDef(flag = true, prefix = { "TYPE_" }, value = {
@@ -782,6 +727,7 @@
     int mMovementGranularity;
     int mAction;
     int mContentChangeTypes;
+    int mWindowChangeTypes;
 
     private ArrayList<AccessibilityRecord> mRecords;
 
@@ -802,6 +748,7 @@
         mMovementGranularity = event.mMovementGranularity;
         mAction = event.mAction;
         mContentChangeTypes = event.mContentChangeTypes;
+        mWindowChangeTypes = event.mWindowChangeTypes;
         mEventTime = event.mEventTime;
         mPackageName = event.mPackageName;
     }
@@ -885,6 +832,7 @@
      *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
      *         </ul>
      */
+    @ContentChangeTypes
     public int getContentChangeTypes() {
         return mContentChangeTypes;
     }
@@ -913,12 +861,49 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      * @see #getContentChangeTypes()
      */
-    public void setContentChangeTypes(int changeTypes) {
+    public void setContentChangeTypes(@ContentChangeTypes int changeTypes) {
         enforceNotSealed();
         mContentChangeTypes = changeTypes;
     }
 
     /**
+     * Get the bit mask of change types signaled by a {@link #TYPE_WINDOWS_CHANGED} event. A
+     * single event may represent multiple change types.
+     *
+     * @return The bit mask of change types.
+     */
+    @WindowsChangeTypes
+    public int getWindowChanges() {
+        return mWindowChangeTypes;
+    }
+
+    /** @hide  */
+    public void setWindowChanges(@WindowsChangeTypes int changes) {
+        mWindowChangeTypes = changes;
+    }
+
+    private static String windowChangeTypesToString(@WindowsChangeTypes int types) {
+        return BitUtils.flagsToString(types, AccessibilityEvent::singleWindowChangeTypeToString);
+    }
+
+    private static String singleWindowChangeTypeToString(int type) {
+        switch (type) {
+            case WINDOWS_CHANGE_ADDED: return "WINDOWS_CHANGE_ADDED";
+            case WINDOWS_CHANGE_REMOVED: return "WINDOWS_CHANGE_REMOVED";
+            case WINDOWS_CHANGE_TITLE: return "WINDOWS_CHANGE_TITLE";
+            case WINDOWS_CHANGE_BOUNDS: return "WINDOWS_CHANGE_BOUNDS";
+            case WINDOWS_CHANGE_LAYER: return "WINDOWS_CHANGE_LAYER";
+            case WINDOWS_CHANGE_ACTIVE: return "WINDOWS_CHANGE_ACTIVE";
+            case WINDOWS_CHANGE_FOCUSED: return "WINDOWS_CHANGE_FOCUSED";
+            case WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED:
+                return "WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED";
+            case WINDOWS_CHANGE_PARENT: return "WINDOWS_CHANGE_PARENT";
+            case WINDOWS_CHANGE_CHILDREN: return "WINDOWS_CHANGE_CHILDREN";
+            default: return Integer.toHexString(type);
+        }
+    }
+
+    /**
      * Sets the event type.
      *
      * @param eventType The event type.
@@ -1025,6 +1010,26 @@
     }
 
     /**
+     * Convenience method to obtain a {@link #TYPE_WINDOWS_CHANGED} event for a specific window and
+     * change set.
+     *
+     * @param windowId The ID of the window that changed
+     * @param windowChangeTypes The changes to populate
+     * @return An instance of a TYPE_WINDOWS_CHANGED, populated with the requested fields and with
+     *         importantForAccessibility set to {@code true}.
+     *
+     * @hide
+     */
+    public static AccessibilityEvent obtainWindowsChangedEvent(
+            int windowId, int windowChangeTypes) {
+        final AccessibilityEvent event = AccessibilityEvent.obtain(TYPE_WINDOWS_CHANGED);
+        event.setWindowId(windowId);
+        event.setWindowChanges(windowChangeTypes);
+        event.setImportantForAccessibility(true);
+        return event;
+    }
+
+    /**
      * Returns a cached instance if such is available or a new one is
      * instantiated with its type property set.
      *
@@ -1099,6 +1104,7 @@
         mMovementGranularity = 0;
         mAction = 0;
         mContentChangeTypes = 0;
+        mWindowChangeTypes = 0;
         mPackageName = null;
         mEventTime = 0;
         if (mRecords != null) {
@@ -1120,6 +1126,7 @@
         mMovementGranularity = parcel.readInt();
         mAction = parcel.readInt();
         mContentChangeTypes = parcel.readInt();
+        mWindowChangeTypes = parcel.readInt();
         mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         mEventTime = parcel.readLong();
         mConnectionId = parcel.readInt();
@@ -1178,6 +1185,7 @@
         parcel.writeInt(mMovementGranularity);
         parcel.writeInt(mAction);
         parcel.writeInt(mContentChangeTypes);
+        parcel.writeInt(mWindowChangeTypes);
         TextUtils.writeToParcel(mPackageName, parcel, 0);
         parcel.writeLong(mEventTime);
         parcel.writeInt(mConnectionId);
@@ -1238,11 +1246,13 @@
         builder.append("; PackageName: ").append(mPackageName);
         builder.append("; MovementGranularity: ").append(mMovementGranularity);
         builder.append("; Action: ").append(mAction);
+        builder.append("; ContentChangeTypes: ").append(
+                contentChangeTypesToString(mContentChangeTypes));
+        builder.append("; WindowChangeTypes: ").append(
+                windowChangeTypesToString(mWindowChangeTypes));
         builder.append(super.toString());
         if (DEBUG) {
             builder.append("\n");
-            builder.append("; ContentChangeTypes: ").append(
-                    contentChangeTypesToString(mContentChangeTypes));
             builder.append("; sourceWindowId: ").append(mSourceWindowId);
             builder.append("; mSourceNodeId: ").append(mSourceNodeId);
             for (int i = 0; i < getRecordCount(); i++) {
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index ef1a3f3..c1c9174 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -21,9 +21,12 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.LongArray;
 import android.util.Pools.SynchronizedPool;
+import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes;
 
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -575,7 +578,7 @@
         StringBuilder builder = new StringBuilder();
         builder.append("AccessibilityWindowInfo[");
         builder.append("title=").append(mTitle);
-        builder.append("id=").append(mId);
+        builder.append(", id=").append(mId);
         builder.append(", type=").append(typeToString(mType));
         builder.append(", layer=").append(mLayer);
         builder.append(", bounds=").append(mBoundsInScreen);
@@ -713,6 +716,60 @@
         return false;
     }
 
+    /**
+     * Reports how this window differs from a possibly different state of the same window. The
+     * argument must have the same id and type as neither of those properties may change.
+     *
+     * @param other The new state.
+     * @return A set of flags showing how the window has changes, or 0 if the two states are the
+     * same.
+     *
+     * @hide
+     */
+    @WindowsChangeTypes
+    public int differenceFrom(AccessibilityWindowInfo other) {
+        if (other.mId != mId) {
+            throw new IllegalArgumentException("Not same window.");
+        }
+        if (other.mType != mType) {
+            throw new IllegalArgumentException("Not same type.");
+        }
+        int changes = 0;
+        if (!TextUtils.equals(mTitle, other.mTitle)) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE;
+        }
+
+        if (!mBoundsInScreen.equals(other.mBoundsInScreen)) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
+        }
+        if (mLayer != other.mLayer) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_LAYER;
+        }
+        if (getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)
+                != other.getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_ACTIVE;
+        }
+        if (getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)
+                != other.getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
+        }
+        if (getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)
+                != other.getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
+        }
+        if (getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)
+                != other.getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_PIP;
+        }
+        if (mParentId != other.mParentId) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_PARENT;
+        }
+        if (!Objects.equals(mChildIds, other.mChildIds)) {
+            changes |= AccessibilityEvent.WINDOWS_CHANGE_CHILDREN;
+        }
+        return changes;
+    }
+
     public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
             new Creator<AccessibilityWindowInfo>() {
         @Override
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 2697454..78b41c6 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -53,6 +53,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -1027,7 +1028,9 @@
      * Gets the user data used for
      * <a href="AutofillService.html#FieldClassification">field classification</a>.
      *
-     * <p><b>Note:</b> This method should only be called by an app providing an autofill service.
+     * <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.
      *
      * @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.
@@ -1079,6 +1082,49 @@
     }
 
     /**
+     * Gets the name of the default algorithm used for
+     * <a href="AutofillService.html#FieldClassification">field classification</a>.
+     *
+     * <p>The default algorithm is used when the algorithm on {@link UserData} is invalid or not
+     * set.
+     *
+     * <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.
+     */
+    @Nullable
+    public String getDefaultFieldClassificationAlgorithm() {
+        try {
+            return mService.getDefaultFieldClassificationAlgorithm();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Gets the name of all algorithms currently available for
+     * <a href="AutofillService.html#FieldClassification">field 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.
+     *
+     * @return list of all algorithms currently available, or an empty list if the caller currently
+     * does not have an enabled autofill service for the user.
+     */
+    @NonNull
+    public List<String> getAvailableFieldClassificationAlgorithms() {
+        try {
+            final List<String> names = mService.getAvailableFieldClassificationAlgorithms();
+            return names != null ? names : Collections.emptyList();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
      * 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/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 38bb311..1afa35e 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -58,4 +58,6 @@
     void setUserData(in UserData userData);
     boolean isFieldClassificationEnabled();
     ComponentName getAutofillServiceComponentName();
+    List<String> getAvailableFieldClassificationAlgorithms();
+    String getDefaultFieldClassificationAlgorithm();
 }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1e17f34..1618d62 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -44,6 +44,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -785,9 +786,11 @@
     // mAutoSizeStepGranularityInPx.
     private boolean mHasPresetAutoSizeValues = false;
 
-    // Indicates whether the text was set from resources or dynamically, so it can be used to
+    // Indicates whether the text was set statically or dynamically, so it can be used to
     // sanitize autofill requests.
-    private boolean mTextFromResource = false;
+    private boolean mSetFromXmlOrResourceId = false;
+    // Resource id used to set the text - used for autofill purposes.
+    private @StringRes int mTextId = ResourceId.ID_NULL;
 
     /**
      * Kick-start the font cache for the zygote process (to pay the cost of
@@ -926,7 +929,8 @@
 
         int n = a.getIndexCount();
 
-        boolean fromResourceId = false;
+        // Must set id in a temporary variable because it will be reset by setText()
+        boolean setFromXml = false;
         for (int i = 0; i < n; i++) {
             int attr = a.getIndex(i);
 
@@ -1068,7 +1072,8 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_text:
-                    fromResourceId = true;
+                    setFromXml = true;
+                    mTextId = a.getResourceId(attr, ResourceId.ID_NULL);
                     text = a.getText(attr);
                     break;
 
@@ -1460,8 +1465,8 @@
         }
 
         setText(text, bufferType);
-        if (fromResourceId) {
-            mTextFromResource = true;
+        if (setFromXml) {
+            mSetFromXmlOrResourceId = true;
         }
 
         if (hint != null) setHint(hint);
@@ -5278,7 +5283,7 @@
 
     private void setText(CharSequence text, BufferType type,
                          boolean notifyBefore, int oldlen) {
-        mTextFromResource = false;
+        mSetFromXmlOrResourceId = false;
         if (text == null) {
             text = "";
         }
@@ -5516,7 +5521,8 @@
     @android.view.RemotableViewMethod
     public final void setText(@StringRes int resid) {
         setText(getContext().getResources().getText(resid));
-        mTextFromResource = true;
+        mSetFromXmlOrResourceId = true;
+        mTextId = resid;
     }
 
     /**
@@ -5543,7 +5549,8 @@
      */
     public final void setText(@StringRes int resid, BufferType type) {
         setText(getContext().getResources().getText(resid), type);
-        mTextFromResource = true;
+        mSetFromXmlOrResourceId = true;
+        mTextId = resid;
     }
 
     /**
@@ -10234,7 +10241,10 @@
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
         if (forAutofill) {
-            structure.setDataIsSensitive(!mTextFromResource);
+            structure.setDataIsSensitive(!mSetFromXmlOrResourceId);
+            if (mTextId != ResourceId.ID_NULL) {
+                structure.setTextIdEntry(getResources().getResourceEntryName(mTextId));
+            }
         }
 
         if (!isPassword || forAutofill) {
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index d138241..46f47a3 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -199,7 +199,7 @@
                 text.setTextLocale(item.getLocale());
                 text.setContentDescription(item.getContentDescription(mCountryMode));
                 if (mCountryMode) {
-                    int layoutDir = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
+                    int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent());
                     //noinspection ResourceType
                     convertView.setLayoutDirection(layoutDir);
                     text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL
diff --git a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index 7e88369..ee01a23 100644
--- a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -22,5 +22,6 @@
 interface INetworkWatchlistManager {
     boolean startWatchlistLogging();
     boolean stopWatchlistLogging();
+    void reloadWatchlist();
     void reportWatchlistIfNecessary();
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 95513ff..722dc75 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -652,6 +652,14 @@
             new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
 
     /**
+     * The WiFi Overall wakelock timer
+     * This timer tracks the actual aggregate time for which MC wakelocks are enabled
+     * since addition of per UID timers would not result in an accurate value due to overlapp of
+     * per uid wakelock timers
+     */
+    StopwatchTimer mWifiMulticastWakelockTimer;
+
+    /**
      * The WiFi controller activity (time in tx, rx, idle, and power consumed) for the device.
      */
     ControllerActivityCounterImpl mWifiActivity;
@@ -5620,6 +5628,12 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+
+            // Start Wifi Multicast overall timer
+            if (!mWifiMulticastWakelockTimer.isRunningLocked()) {
+                if (DEBUG_HISTORY) Slog.v(TAG, "WiFi Multicast Overall Timer Started");
+                mWifiMulticastWakelockTimer.startRunningLocked(elapsedRealtime);
+            }
         }
         mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -5635,6 +5649,12 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+
+            // Stop Wifi Multicast overall timer
+            if (mWifiMulticastWakelockTimer.isRunningLocked()) {
+                if (DEBUG_HISTORY) Slog.v(TAG, "Multicast Overall Timer Stopped");
+                mWifiMulticastWakelockTimer.stopRunningLocked(elapsedRealtime);
+            }
         }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
     }
@@ -6016,6 +6036,16 @@
         return (int)mMobileRadioActiveUnknownCount.getCountLocked(which);
     }
 
+    @Override public long getWifiMulticastWakelockTime(
+            long elapsedRealtimeUs, int which) {
+        return mWifiMulticastWakelockTimer.getTotalTimeLocked(
+                elapsedRealtimeUs, which);
+    }
+
+    @Override public int getWifiMulticastWakelockCount(int which) {
+        return mWifiMulticastWakelockTimer.getCountLocked(which);
+    }
+
     @Override public long getWifiOnTime(long elapsedRealtimeUs, int which) {
         return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
@@ -9604,6 +9634,8 @@
         mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
+        mWifiMulticastWakelockTimer = new StopwatchTimer(mClocks, null,
+                WIFI_AGGREGATE_MULTICAST_ENABLED, null, mOnBatteryTimeBase);
         mWifiOnTimer = new StopwatchTimer(mClocks, null, -4, null, mOnBatteryTimeBase);
         mGlobalWifiRunningTimer = new StopwatchTimer(mClocks, null, -5, null, mOnBatteryTimeBase);
         for (int i=0; i<NUM_WIFI_STATES; i++) {
@@ -10305,6 +10337,7 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].reset(false);
         }
+        mWifiMulticastWakelockTimer.reset(false);
         mWifiActivity.reset(false);
         mBluetoothActivity.reset(false);
         mModemActivity.reset(false);
@@ -12751,6 +12784,7 @@
         mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
+        mWifiMulticastWakelockTimer.readSummaryFromParcelLocked(in);
         mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         mWifiOn = false;
         mWifiOnTimer.readSummaryFromParcelLocked(in);
@@ -13191,6 +13225,7 @@
         mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
+        mWifiMulticastWakelockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         for (int i=0; i<NUM_WIFI_STATES; i++) {
@@ -13654,6 +13689,8 @@
         mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
+        mWifiMulticastWakelockTimer = new StopwatchTimer(mClocks, null, -4, null,
+                mOnBatteryTimeBase, in);
         mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         mWifiOn = false;
         mWifiOnTimer = new StopwatchTimer(mClocks, null, -4, null, mOnBatteryTimeBase, in);
@@ -13860,6 +13897,7 @@
         mMobileRadioActiveAdjustedTime.writeToParcel(out);
         mMobileRadioActiveUnknownTime.writeToParcel(out);
         mMobileRadioActiveUnknownCount.writeToParcel(out);
+        mWifiMulticastWakelockTimer.writeToParcel(out, uSecRealtime);
         mWifiOnTimer.writeToParcel(out, uSecRealtime);
         mGlobalWifiRunningTimer.writeToParcel(out, uSecRealtime);
         for (int i=0; i<NUM_WIFI_STATES; i++) {
@@ -14046,6 +14084,8 @@
             mMobileRadioActiveTimer.logState(pr, "  ");
             pr.println("*** Mobile network active adjusted timer:");
             mMobileRadioActiveAdjustedTime.logState(pr, "  ");
+            pr.println("*** Wifi Multicast WakeLock Timer:");
+            mWifiMulticastWakelockTimer.logState(pr, "  ");
             pr.println("*** mWifiRadioPowerState=" + mWifiRadioPowerState);
             pr.println("*** Wifi timer:");
             mWifiOnTimer.logState(pr, "  ");
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 77250eb..862288b 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -63,22 +63,21 @@
     // RecoverableKeyStoreLoader methods.
     // {@code ServiceSpecificException} may be thrown to signal an error, which caller can
     // convert to  {@code RecoverableKeyStoreLoader}.
-    void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList,
-            int userId);
-    KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId);
+    void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
+    KeyStoreRecoveryData getRecoveryData(in byte[] account);
     byte[] generateAndStoreKey(String alias);
-    void setSnapshotCreatedPendingIntent(in PendingIntent intent, int userId);
-    Map getRecoverySnapshotVersions(int userId);
-    void setServerParameters(long serverParameters, int userId);
-    void setRecoveryStatus(in String packageName, in String[] aliases, int status, int userId);
-    Map getRecoveryStatus(in String packageName, int userId);
-    void setRecoverySecretTypes(in int[] secretTypes, int userId);
-    int[] getRecoverySecretTypes(int userId);
-    int[] getPendingRecoverySecretTypes(int userId);
-    void recoverySecretAvailable(in KeyStoreRecoveryMetadata recoverySecret, int userId);
+    void setSnapshotCreatedPendingIntent(in PendingIntent intent);
+    Map getRecoverySnapshotVersions();
+    void setServerParameters(long serverParameters);
+    void setRecoveryStatus(in String packageName, in String[] aliases, int status);
+    Map getRecoveryStatus(in String packageName);
+    void setRecoverySecretTypes(in int[] secretTypes);
+    int[] getRecoverySecretTypes();
+    int[] getPendingRecoverySecretTypes();
+    void recoverySecretAvailable(in KeyStoreRecoveryMetadata recoverySecret);
     byte[] startRecoverySession(in String sessionId,
             in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
-            in List<KeyStoreRecoveryMetadata> secrets, int userId);
+            in List<KeyStoreRecoveryMetadata> secrets);
     Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
-            in List<KeyEntryRecoveryData> applicationKeys, int userId);
+            in List<KeyEntryRecoveryData> applicationKeys);
 }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index b7a6719..c5af897 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -633,6 +633,11 @@
             addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
         }
 
+        // Help legacy devices that may not have updated their static config
+        if (StorageManager.hasAdoptable()) {
+            addFeature(PackageManager.FEATURE_ADOPTABLE_STORAGE, 0);
+        }
+
         if (ActivityManager.isLowRamDeviceStatic()) {
             addFeature(PackageManager.FEATURE_RAM_LOW, 0);
         } else {
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 52c84a4..8fee7ba 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -18,6 +18,8 @@
 
 //#define LOG_NDEBUG 0
 
+#include <inttypes.h>
+
 #include <nativehelper/JNIHelp.h>
 
 #include <android_runtime/AndroidRuntime.h>
@@ -78,8 +80,8 @@
 
     void setFdEvents(int events);
 
-    const char* getInputChannelName() {
-        return mInputConsumer.getChannel()->getName().c_str();
+    const std::string getInputChannelName() {
+        return mInputConsumer.getChannel()->getName();
     }
 
     virtual int handleEvent(int receiveFd, int events, void* data);
@@ -93,7 +95,7 @@
         mInputConsumer(inputChannel), mMessageQueue(messageQueue),
         mBatchedInputEventPending(false), mFdEvents(0) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
+        ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
     }
 }
 
@@ -109,7 +111,7 @@
 
 void NativeInputEventReceiver::dispose() {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
+        ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName().c_str());
     }
 
     setFdEvents(0);
@@ -117,7 +119,7 @@
 
 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
+        ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
     }
 
     status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
@@ -125,7 +127,7 @@
         if (status == WOULD_BLOCK) {
             if (kDebugDispatchCycle) {
                 ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
-                        "Enqueued for later.", getInputChannelName());
+                        "Enqueued for later.", getInputChannelName().c_str());
             }
             Finish finish;
             finish.seq = seq;
@@ -137,7 +139,7 @@
             return OK;
         }
         ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
-                getInputChannelName(), status);
+                getInputChannelName().c_str(), status);
     }
     return status;
 }
@@ -161,7 +163,7 @@
         // the consumer will soon be disposed as well.
         if (kDebugDispatchCycle) {
             ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
-                    "events=0x%x", getInputChannelName(), events);
+                    "events=0x%x", getInputChannelName().c_str(), events);
         }
         return 0; // remove the callback
     }
@@ -183,13 +185,13 @@
                 if (status == WOULD_BLOCK) {
                     if (kDebugDispatchCycle) {
                         ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
-                                getInputChannelName(), i, mFinishQueue.size());
+                                getInputChannelName().c_str(), i, mFinishQueue.size());
                     }
                     return 1; // keep the callback, try again later
                 }
 
                 ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
-                        getInputChannelName(), status);
+                        getInputChannelName().c_str(), status);
                 if (status != DEAD_OBJECT) {
                     JNIEnv* env = AndroidRuntime::getJNIEnv();
                     String8 message;
@@ -202,7 +204,7 @@
         }
         if (kDebugDispatchCycle) {
             ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",
-                    getInputChannelName(), mFinishQueue.size());
+                    getInputChannelName().c_str(), mFinishQueue.size());
         }
         mFinishQueue.clear();
         setFdEvents(ALOOPER_EVENT_INPUT);
@@ -210,15 +212,16 @@
     }
 
     ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
-            "events=0x%x", getInputChannelName(), events);
+            "events=0x%x", getInputChannelName().c_str(), events);
     return 1;
 }
 
 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
         bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
-                getInputChannelName(), consumeBatches ? "true" : "false", (long long)frameTime);
+        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
+                getInputChannelName().c_str(),
+                consumeBatches ? "true" : "false", frameTime);
     }
 
     if (consumeBatches) {
@@ -245,7 +248,7 @@
                         receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                         if (!receiverObj.get()) {
                             ALOGW("channel '%s' ~ Receiver object was finalized "
-                                    "without being disposed.", getInputChannelName());
+                                    "without being disposed.", getInputChannelName().c_str());
                             return DEAD_OBJECT;
                         }
                     }
@@ -253,7 +256,7 @@
                     mBatchedInputEventPending = true;
                     if (kDebugDispatchCycle) {
                         ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
-                                getInputChannelName());
+                                getInputChannelName().c_str());
                     }
                     env->CallVoidMethod(receiverObj.get(),
                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
@@ -265,7 +268,7 @@
                 return OK;
             }
             ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
-                    getInputChannelName(), status);
+                    getInputChannelName().c_str(), status);
             return status;
         }
         assert(inputEvent);
@@ -275,7 +278,7 @@
                 receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                 if (!receiverObj.get()) {
                     ALOGW("channel '%s' ~ Receiver object was finalized "
-                            "without being disposed.", getInputChannelName());
+                            "without being disposed.", getInputChannelName().c_str());
                     return DEAD_OBJECT;
                 }
             }
@@ -284,7 +287,7 @@
             switch (inputEvent->getType()) {
             case AINPUT_EVENT_TYPE_KEY:
                 if (kDebugDispatchCycle) {
-                    ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
+                    ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
                 }
                 inputEventObj = android_view_KeyEvent_fromNative(env,
                         static_cast<KeyEvent*>(inputEvent));
@@ -292,7 +295,7 @@
 
             case AINPUT_EVENT_TYPE_MOTION: {
                 if (kDebugDispatchCycle) {
-                    ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
+                    ALOGD("channel '%s' ~ Received motion event.", getInputChannelName().c_str());
                 }
                 MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
                 if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
@@ -309,7 +312,7 @@
 
             if (inputEventObj) {
                 if (kDebugDispatchCycle) {
-                    ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
+                    ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
                 }
                 env->CallVoidMethod(receiverObj.get(),
                         gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
@@ -320,7 +323,8 @@
                 }
                 env->DeleteLocalRef(inputEventObj);
             } else {
-                ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
+                ALOGW("channel '%s' ~ Failed to obtain event object.",
+                        getInputChannelName().c_str());
                 skipCallbacks = true;
             }
         }
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index f87abac..effeed6 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -70,8 +70,8 @@
     KeyedVector<uint32_t, uint32_t> mPublishedSeqMap;
     uint32_t mNextPublishedSeq;
 
-    const char* getInputChannelName() {
-        return mInputPublisher.getChannel()->getName().c_str();
+    const std::string getInputChannelName() {
+        return mInputPublisher.getChannel()->getName();
     }
 
     virtual int handleEvent(int receiveFd, int events, void* data);
@@ -86,7 +86,7 @@
         mInputPublisher(inputChannel), mMessageQueue(messageQueue),
         mNextPublishedSeq(1) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName());
+        ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName().c_str());
     }
 }
 
@@ -103,7 +103,7 @@
 
 void NativeInputEventSender::dispose() {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName());
+        ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName().c_str());
     }
 
     mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
@@ -111,7 +111,7 @@
 
 status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq);
+        ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName().c_str(), seq);
     }
 
     uint32_t publishedSeq = mNextPublishedSeq++;
@@ -121,7 +121,7 @@
             event->getRepeatCount(), event->getDownTime(), event->getEventTime());
     if (status) {
         ALOGW("Failed to send key event on channel '%s'.  status=%d",
-                getInputChannelName(), status);
+                getInputChannelName().c_str(), status);
         return status;
     }
     mPublishedSeqMap.add(publishedSeq, seq);
@@ -130,7 +130,7 @@
 
 status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName(), seq);
+        ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName().c_str(), seq);
     }
 
     uint32_t publishedSeq;
@@ -148,7 +148,7 @@
                 event->getHistoricalRawPointerCoords(0, i));
         if (status) {
             ALOGW("Failed to send motion event sample on channel '%s'.  status=%d",
-                    getInputChannelName(), status);
+                    getInputChannelName().c_str(), status);
             return status;
         }
     }
@@ -163,7 +163,7 @@
         // soon be disposed as well.
         if (kDebugDispatchCycle) {
             ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred.  "
-                    "events=0x%x", getInputChannelName(), events);
+                    "events=0x%x", getInputChannelName().c_str(), events);
         }
 
         return 0; // remove the callback
@@ -171,7 +171,7 @@
 
     if (!(events & ALOOPER_EVENT_INPUT)) {
         ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
-                "events=0x%x", getInputChannelName(), events);
+                "events=0x%x", getInputChannelName().c_str(), events);
         return 1;
     }
 
@@ -183,7 +183,7 @@
 
 status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName());
+        ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName().c_str());
     }
 
     ScopedLocalRef<jobject> senderObj(env, NULL);
@@ -197,7 +197,7 @@
                 return OK;
             }
             ALOGE("channel '%s' ~ Failed to consume finished signals.  status=%d",
-                    getInputChannelName(), status);
+                    getInputChannelName().c_str(), status);
             return status;
         }
 
@@ -209,7 +209,7 @@
             if (kDebugDispatchCycle) {
                 ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
                         "pendingEvents=%zu.",
-                        getInputChannelName(), seq, handled ? "true" : "false",
+                        getInputChannelName().c_str(), seq, handled ? "true" : "false",
                         mPublishedSeqMap.size());
             }
 
@@ -218,7 +218,7 @@
                     senderObj.reset(jniGetReferent(env, mSenderWeakGlobal));
                     if (!senderObj.get()) {
                         ALOGW("channel '%s' ~ Sender object was finalized "
-                                "without being disposed.", getInputChannelName());
+                                "without being disposed.", getInputChannelName().c_str());
                         return DEAD_OBJECT;
                     }
                 }
diff --git a/core/proto/android/graphics/pixelformat.proto b/core/proto/android/graphics/pixelformat.proto
new file mode 100644
index 0000000..4e42c92
--- /dev/null
+++ b/core/proto/android/graphics/pixelformat.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.graphics;
+option java_multiple_files = true;
+
+message PixelFormatProto {
+  enum Format {
+      UNKNOWN      = 0;
+      TRANSLUCENT  = -3;
+      TRANSPARENT  = -2;
+      OPAQUE       = -1;
+      RGBA_8888    = 1;
+      RGBX_8888    = 2;
+      RGB_888      = 3;
+      RGB_565      = 4;
+      RGBA_F16     = 0x16;
+      RGBA_1010102 = 0x2B;
+  }
+}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 0fa428e..9999b4e 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -537,7 +537,24 @@
       // Screen-off CPU time in milliseconds.
       optional int64 screen_off_duration_ms = 3;
     }
+    // CPU times accumulated across all process states.
     repeated ByFrequency by_frequency = 3;
+
+    enum ProcessState {
+      TOP = 0;
+      FOREGROUND_SERVICE = 1;
+      FOREGROUND = 2;
+      BACKGROUND = 3;
+      TOP_SLEEPING = 4;
+      HEAVY_WEIGHT = 5;
+      CACHED = 6;
+    }
+    // CPU times at different process states.
+    message ByProcessState {
+      optional ProcessState process_state = 1;
+      repeated ByFrequency by_frequency = 2;
+    }
+    repeated ByProcessState by_process_state = 4;
   }
   optional Cpu cpu = 7;
 
@@ -655,7 +672,7 @@
     // In approximate order or priority (top being what the framework considers
     // most important and is thus least likely to kill when resources are
     // needed:
-    // top > foreground service > top sleeping > foreground > background > cache
+    // top > foreground service > foreground > background > top sleeping > heavy weight > cache
     enum State {
       // Time this uid has any processes in the top state (or above such as
       // persistent).
@@ -663,20 +680,26 @@
       // Time this uid has any process with a started out bound foreground
       // service, but none in the "top" state.
       PROCESS_STATE_FOREGROUND_SERVICE = 1;
-      // Time this uid has any process that is top while the device is sleeping,
-      // but none in the "foreground service" or better state. Sleeping is
-      // mostly screen off, but also includes the time when the screen is on but
-      // the device has not yet been unlocked.
-      PROCESS_STATE_TOP_SLEEPING = 2;
       // Time this uid has any process in an active foreground state, but none
       // in the "top sleeping" or better state.
-      PROCESS_STATE_FOREGROUND = 3;
+      PROCESS_STATE_FOREGROUND = 2;
       // Time this uid has any process in an active background state, but none
       // in the "foreground" or better state.
-      PROCESS_STATE_BACKGROUND = 4;
+      PROCESS_STATE_BACKGROUND = 3;
+      // Time this uid has any process that is top while the device is sleeping,
+      // but not active for any other reason. We consider is a kind of cached
+      // process for execution restrictions. Sleeping is mostly screen off, but
+      // also includes the time when the screen is on but the device has not yet
+      // been unlocked.
+      PROCESS_STATE_TOP_SLEEPING = 4;
+      // Time this uid has any process that is in the background but it has an
+      // activity marked as "can't save state".  This is essentially a cached
+      // process, though the system will try much harder than normal to avoid
+      // killing it.
+      PROCESS_STATE_HEAVY_WEIGHT = 5;
       // Time this uid has any processes that are sitting around cached, not in
       // one of the other active states.
-      PROCESS_STATE_CACHED = 5;
+      PROCESS_STATE_CACHED = 6;
     }
     optional State state = 1;
     optional int64 duration_ms = 2;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index ef7b6b0..12aca78 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -18,6 +18,7 @@
 
 import "frameworks/base/core/proto/android/content/configuration.proto";
 import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/view/displaycutout.proto";
 import "frameworks/base/core/proto/android/view/displayinfo.proto";
 import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
 
@@ -172,6 +173,27 @@
   repeated WindowStateProto child_windows = 15;
   optional .android.graphics.RectProto surface_position = 16;
   optional .android.graphics.RectProto shown_position = 17;
+  optional int32 requested_width = 18;
+  optional int32 requested_height = 19;
+  optional int32 view_visibility = 20;
+  optional int32 system_ui_visibility = 21;
+  optional bool has_surface = 22;
+  optional bool is_ready_for_display = 23;
+  optional .android.graphics.RectProto display_frame = 24;
+  optional .android.graphics.RectProto overscan_frame = 25;
+  optional .android.graphics.RectProto visible_frame = 26;
+  optional .android.graphics.RectProto decor_frame = 27;
+  optional .android.graphics.RectProto outset_frame = 28;
+  optional .android.graphics.RectProto overscan_insets = 29;
+  optional .android.graphics.RectProto visible_insets = 30;
+  optional .android.graphics.RectProto stable_insets = 31;
+  optional .android.graphics.RectProto outsets = 32;
+  optional .android.view.DisplayCutoutProto cutout = 33;
+  optional bool remove_on_exit = 34;
+  optional bool destroying = 35;
+  optional bool removed = 36;
+  optional bool is_on_screen = 37;
+  optional bool is_visible = 38;
 }
 
 message IdentifierProto {
@@ -184,6 +206,15 @@
 message WindowStateAnimatorProto {
   optional .android.graphics.RectProto last_clip_rect = 1;
   optional WindowSurfaceControllerProto surface = 2;
+  enum DrawState {
+    NO_SURFACE = 0;
+    DRAW_PENDING = 1;
+    COMMIT_DRAW_PENDING = 2;
+    READY_TO_SHOW = 3;
+    HAS_DRAWN = 4;
+  }
+  optional DrawState draw_state = 3;
+  optional .android.graphics.RectProto system_decor_rect = 4;
 }
 
 /* represents WindowSurfaceController */
diff --git a/core/proto/android/view/display.proto b/core/proto/android/view/display.proto
index 210c6d1..cac0830 100644
--- a/core/proto/android/view/display.proto
+++ b/core/proto/android/view/display.proto
@@ -38,4 +38,16 @@
         // The display is on and optimized for VR mode.
         DISPLAY_STATE_VR = 5;
     }
+    enum ColorMode {
+        COLOR_MODE_INVALID = -1;
+        COLOR_MODE_BT601_625 = 1;
+        COLOR_MODE_BT601_625_UNADJUSTED = 2;
+        COLOR_MODE_BT601_525 = 3;
+        COLOR_MODE_BT601_525_UNADJUSTED = 4;
+        COLOR_MODE_BT709 = 5;
+        COLOR_MODE_DCI_P3 = 6;
+        COLOR_MODE_SRGB = 7;
+        COLOR_MODE_ADOBE_RGB = 8;
+        COLOR_MODE_DISPLAY_P3 = 9;
+    }
 }
diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto
new file mode 100644
index 0000000..ff13fab
--- /dev/null
+++ b/core/proto/android/view/displaycutout.proto
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/graphics/rect.proto";
+
+package android.view;
+option java_multiple_files = true;
+
+message DisplayCutoutProto {
+  optional .android.graphics.RectProto insets = 1;
+  optional .android.graphics.RectProto bounds = 2;
+}
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index 7821212..b81cd1f 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -15,11 +15,51 @@
  */
 
 syntax = "proto2";
-package android.view;
 
+import "frameworks/base/core/proto/android/graphics/pixelformat.proto";
+import "frameworks/base/core/proto/android/view/display.proto";
+
+package android.view;
 option java_multiple_files = true;
 
 /* represents WindowManager.LayoutParams */
 message WindowLayoutParamsProto {
   optional int32 type = 1;
+  optional int32 x = 2;
+  optional int32 y = 3;
+  optional int32 width = 4;
+  optional int32 height = 5;
+  optional float horizontal_margin = 6;
+  optional float vertical_margin = 7;
+  optional int32 gravity = 8;
+  optional int32 soft_input_mode = 9;
+  optional .android.graphics.PixelFormatProto.Format format = 10;
+  optional int32 window_animations = 11;
+  optional float alpha = 12;
+  optional float screen_brightness = 13;
+  optional float button_brightness = 14;
+  enum RotationAnimation {
+    ROTATION_ANIMATION_UNSPECIFIED = -1;
+    ROTATION_ANIMATION_CROSSFADE = 1;
+    ROTATION_ANIMATION_JUMPCUT = 2;
+    ROTATION_ANIMATION_SEAMLESS = 3;
+  }
+  optional RotationAnimation rotation_animation = 15;
+  optional float preferred_refresh_rate = 16;
+  optional int32 preferred_display_mode_id = 17;
+  optional bool has_system_ui_listeners = 18;
+  optional uint32 input_feature_flags = 19;
+  optional int64 user_activity_timeout = 20;
+  enum NeedsMenuState {
+    NEEDS_MENU_UNSET = 0;
+    NEEDS_MENU_SET_TRUE = 1;
+    NEEDS_MENU_SET_FALSE = 2;
+  }
+  optional NeedsMenuState needs_menu_key = 22;
+  optional .android.view.DisplayProto.ColorMode color_mode = 23;
+  optional uint32 flags = 24;
+  optional uint64 flags_extra = 25;
+  optional uint32 private_flags = 26;
+  optional uint32 system_ui_visibility_flags = 27;
+  optional uint32 subtree_system_ui_visibility_flags = 28;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 272e3c7..52bfcde 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3870,6 +3870,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.updates.NetworkWatchlistInstallReceiver"
+                  android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_NETWORK_WATCHLIST" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.server.updates.ApnDbInstallReceiver"
                 android:permission="android.permission.UPDATE_CONFIG">
             <intent-filter>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 64febf1..1b3d6ce 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8699,8 +8699,11 @@
          common values are 400 for regular weight and 700 for bold weight. If unspecified, the value
          in the font's header tables will be used. -->
         <attr name="fontWeight" format="integer" />
-        <!-- The index of the font in the tcc font file. If the font file referenced is not in the
-         tcc format, this attribute needs not be specified. -->
+        <!-- The index of the font in the ttc (TrueType Collection) font file. If the font file
+         referenced is not in the ttc format, this attribute needs not be specified.
+         {@see android.graphics.Typeface#Builder.setTtcIndex(int)}.
+         The default value is 0. More details about the TrueType Collection font format can be found
+         here: https://en.wikipedia.org/wiki/TrueType#TrueType_Collection. -->
         <attr name="ttcIndex" format="integer" />
         <!-- The variation settings to be applied to the font. The string should be in the following
          format: "'tag1' value1, 'tag2' value2, ...". If the default variation settings should be
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index e2498df7..7048511 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -31,10 +31,8 @@
     <color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
     <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
 
-    <color name="accent_device_default_700">@color/accent_material_700</color>
     <color name="accent_device_default_light">@color/accent_material_light</color>
     <color name="accent_device_default_dark">@color/accent_material_dark</color>
-    <color name="accent_device_default_50">@color/accent_material_50</color>
 
     <color name="background_device_default_dark">@color/background_material_dark</color>
     <color name="background_device_default_light">@color/background_material_light</color>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 8c37d4b..0413100 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -39,10 +39,8 @@
     <color name="tertiary_material_settings">@color/material_blue_grey_700</color>
     <color name="quaternary_material_settings">@color/material_blue_grey_200</color>
 
-    <color name="accent_material_700">@color/material_deep_teal_700</color>
     <color name="accent_material_light">@color/material_deep_teal_500</color>
     <color name="accent_material_dark">@color/material_deep_teal_200</color>
-    <color name="accent_material_50">@color/material_deep_teal_50</color>
 
     <color name="button_material_dark">#ff5a595b</color>
     <color name="button_material_light">#ffd6d7d7</color>
@@ -95,12 +93,10 @@
     <color name="material_grey_100">#fff5f5f5</color>
     <color name="material_grey_50">#fffafafa</color>
 
-    <color name="material_deep_teal_50">#ffe0f2f1</color>
     <color name="material_deep_teal_100">#ffb2dfdb</color>
     <color name="material_deep_teal_200">#ff80cbc4</color>
     <color name="material_deep_teal_300">#ff4db6ac</color>
     <color name="material_deep_teal_500">#ff009688</color>
-    <color name="material_deep_teal_700">#ff00796b</color>
 
     <color name="material_blue_grey_200">#ffb0bec5</color>
     <color name="material_blue_grey_700">#ff455a64</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dc791cf..fd05bb4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1302,8 +1302,8 @@
 
          If this is defined then:
             - config_autoBrightnessLcdBacklightValues should not be defined
-            - config_screenBrightnessMinimumNits must be defined
-            - config_screenBrightnessMaximumNits must be defined
+            - config_screenBrightnessNits must be defined
+            - config_screenBrightnessBacklight must be defined
 
          This array should have size one greater than the size of the config_autoBrightnessLevels
          array. The brightness values must be non-negative and non-decreasing. This must be
@@ -1347,28 +1347,23 @@
         <item>200</item>
     </integer-array>
 
-    <!-- The minimum brightness of the display in nits. On OLED displays this should be measured
-         with an all white image while the display is fully on and the backlight is set to
-         config_screenBrightnessSettingMinimum or config_screenBrightnessSettingDark, whichever
-         is darker.
+    <!-- An array describing the screen's backlight values corresponding to the brightness
+         values in the config_screenBrightnessNits array.
 
-         If this and config_screenBrightnessMinimumNits are set, then the display's brightness
-         range is assumed to be linear between
-         (config_screenBrightnessSettingMinimum, config_screenBrightnessMinimumNits) and
-         (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). -->
-    <item name="config_screenBrightnessMinimumNits" format="float" type="dimen">-1.0</item>
+         This array should be equal in size to config_screenBrightnessBacklight. -->
+    <integer-array name="config_screenBrightnessBacklight">
+    </integer-array>
 
-    <!-- The maximum brightness of the display in nits. On OLED displays this should be measured
-         with an all white image while the display is fully on and the "backlight" is set to
-         config_screenBrightnessSettingMaximum. Note that this value should *not* reflect the
-         maximum brightness value for any high brightness modes but only the maximum brightness
-         value obtainable in a sustainable manner.
+    <!-- An array of floats describing the screen brightness in nits corresponding to the backlight
+         values in the config_screenBrightnessBacklight array.  On OLED displays these  values
+         should be measured with an all white image while the display is in the fully on state.
+         Note that this value should *not* reflect the maximum brightness value for any high
+         brightness modes but only the maximum brightness value obtainable in a sustainable manner.
 
-         If this and config_screenBrightnessMinimumNits are set to something non-negative, then the
-         display's brightness range is assumed to be linear between
-         (config_screenBrightnessSettingMinimum, config_screenBrightnessMaximumNits) and
-         (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). -->
-    <item name="config_screenBrightnessMaximumNits" format="float" type="dimen">-1.0</item>
+         This array should be equal in size to config_screenBrightnessBacklight -->
+    <array name="config_screenBrightnessNits">
+    </array>
+
 
     <!-- Array of ambient lux threshold values. This is used for determining hysteresis constraint
          values by calculating the index to use for lookup and then setting the constraint value
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5783435..c54f799 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -20,13 +20,23 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Suffix added to a number to signify size in bytes. -->
     <string name="byteShort">B</string>
+    <!-- Suffix added to a number to signify size in kilobytes (1000 bytes).
+        If you retain the Latin script for the localization, please use the lowercase
+        'k', as it signifies 1000 bytes as opposed to 1024 bytes. -->
+    <string name="kilobyteShort">kB</string>
+    <!-- Suffix added to a number to signify size in megabytes. -->
+    <string name="megabyteShort">MB</string>
+    <!-- Suffix added to a number to signify size in gigabytes. -->
+    <string name="gigabyteShort">GB</string>
+    <!-- Suffix added to a number to signify size in terabytes. -->
+    <string name="terabyteShort">TB</string>
     <!-- Suffix added to a number to signify size in petabytes. -->
     <string name="petabyteShort">PB</string>
-    <!-- Format string used to add a suffix like "B" or "PB" to a number
-         to display a size in bytes or petabytes.
-         Some languages may want to remove the space between the placeholders
-         or replace it with a non-breaking space. -->
-    <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="B">%2$s</xliff:g></string>
+    <!-- Format string used to add a suffix like "kB" or "MB" to a number
+         to display a size in kilobytes, megabytes, or other size units.
+         Some languages (like French) will want to add a space between
+         the placeholders. -->
+    <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="MB">%2$s</xliff:g></string>
 
     <!-- Used in Contacts for a field that has no label and in Note Pad
          for a note with no name. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8e391d3..4343ba0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -705,6 +705,7 @@
   <java-symbol type="string" name="fileSizeSuffix" />
   <java-symbol type="string" name="force_close" />
   <java-symbol type="string" name="gadget_host_error_inflating" />
+  <java-symbol type="string" name="gigabyteShort" />
   <java-symbol type="string" name="gpsNotifMessage" />
   <java-symbol type="string" name="gpsNotifTicker" />
   <java-symbol type="string" name="gpsNotifTitle" />
@@ -760,6 +761,7 @@
   <java-symbol type="string" name="keyboardview_keycode_enter" />
   <java-symbol type="string" name="keyboardview_keycode_mode_change" />
   <java-symbol type="string" name="keyboardview_keycode_shift" />
+  <java-symbol type="string" name="kilobyteShort" />
   <java-symbol type="string" name="last_month" />
   <java-symbol type="string" name="launchBrowserDefault" />
   <java-symbol type="string" name="lock_to_app_toast" />
@@ -779,6 +781,7 @@
   <java-symbol type="string" name="lockscreen_emergency_call" />
   <java-symbol type="string" name="lockscreen_return_to_call" />
   <java-symbol type="string" name="low_memory" />
+  <java-symbol type="string" name="megabyteShort" />
   <java-symbol type="string" name="midnight" />
   <java-symbol type="string" name="mismatchPin" />
   <java-symbol type="string" name="mmiComplete" />
@@ -981,6 +984,7 @@
   <java-symbol type="string" name="sync_really_delete" />
   <java-symbol type="string" name="sync_too_many_deletes_desc" />
   <java-symbol type="string" name="sync_undo_deletes" />
+  <java-symbol type="string" name="terabyteShort" />
   <java-symbol type="string" name="text_copied" />
   <java-symbol type="string" name="time_of_day" />
   <java-symbol type="string" name="time_picker_decrement_hour_button" />
@@ -3193,7 +3197,7 @@
   <java-symbol type="string" name="global_action_logout" />
   <java-symbol type="drawable" name="ic_logout" />
 
-  <java-symbol type="dimen" name="config_screenBrightnessMinimumNits" />
-  <java-symbol type="dimen" name="config_screenBrightnessMaximumNits" />
   <java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" />
+  <java-symbol type="array" name="config_screenBrightnessBacklight" />
+  <java-symbol type="array" name="config_screenBrightnessNits" />
 </resources>
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index aefc47e..c19a343 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -42,7 +42,8 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-@Presubmit
+// TODO: b/70616950
+//@Presubmit
 public class ObjectPoolTests {
 
     // 1. Check if two obtained objects from pool are not the same.
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 9c544f4..04d2dad 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -17,7 +17,6 @@
 package android.text.format;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -210,24 +209,4 @@
 
         Locale.setDefault(locale);
     }
-
-    /**
-     * Verifies that Formatter doesn't "leak" the locally defined petabyte unit into ICU via the
-     * {@link MeasureUnit} registry. This test can fail for two reasons:
-     * 1. we regressed and started leaking again. In this case the code needs to be fixed.
-     * 2. ICU started supporting petabyte as a unit, in which case change one needs to revert this
-     * change (I494fb59a3b3742f35cbdf6b8705817f404a2c6b0), remove Formatter.PETABYTE and replace any
-     * usages of that field with just MeasureUnit.PETABYTE.
-     */
-    // http://b/65632959
-    @Test
-    public void doesNotLeakPetabyte() {
-        // Ensure that the Formatter class is loaded when we call .getAvailable().
-        Formatter.formatFileSize(mContext, Long.MAX_VALUE);
-        Set<MeasureUnit> digitalUnits = MeasureUnit.getAvailable("digital");
-        for (MeasureUnit unit : digitalUnits) {
-            // This assert can fail if we don't leak PETABYTE, but ICU has added it, see #2 above.
-            assertNotEquals("petabyte", unit.getSubtype());
-        }
-    }
 }
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 1b4d4a2..4405934 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -25,10 +25,14 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseIntArray;
+import android.view.ViewDebug.CanvasProvider;
+import android.view.ViewDebug.HardwareCanvasProvider;
+import android.view.ViewDebug.SoftwareCanvasProvider;
 
 import com.android.frameworks.coretests.R;
 
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -55,23 +59,42 @@
     @Before
     public void setUp() throws Exception {
         mActivity = mActivityRule.getActivity();
-        mViewToCapture = (ViewGroup) mActivity.findViewById(R.id.capture);
+        mViewToCapture = mActivity.findViewById(R.id.capture);
     }
 
     @Test
     @SmallTest
-    public void testCreateSnapshot() {
+    public void testCreateSnapshot_software() {
         assertChildrenVisibility();
-        testCreateSnapshot(true, R.drawable.view_capture_test_no_children_golden);
+        testCreateSnapshot(new SoftwareCanvasProvider(), true,
+                R.drawable.view_capture_test_no_children_golden);
         assertChildrenVisibility();
-        testCreateSnapshot(false, R.drawable.view_capture_test_with_children_golden);
+        testCreateSnapshot(new SoftwareCanvasProvider(), false,
+                R.drawable.view_capture_test_with_children_golden);
         assertChildrenVisibility();
     }
 
-    private void testCreateSnapshot(boolean skipChildren, int goldenResId) {
-        Bitmap result = mViewToCapture.createSnapshot(Bitmap.Config.ARGB_8888, 0, skipChildren);
+    @Test
+    @SmallTest
+    public void testCreateSnapshot_hardware() {
+        Assume.assumeTrue(mViewToCapture.isHardwareAccelerated());
+        assertChildrenVisibility();
+        testCreateSnapshot(new HardwareCanvasProvider(), true,
+                R.drawable.view_capture_test_no_children_golden);
+        assertChildrenVisibility();
+        testCreateSnapshot(new HardwareCanvasProvider(), false,
+                R.drawable.view_capture_test_with_children_golden);
+        assertChildrenVisibility();
+    }
+
+    private void testCreateSnapshot(
+            CanvasProvider canvasProvider, boolean skipChildren, int goldenResId) {
+        Bitmap result = mViewToCapture.createSnapshot(canvasProvider, skipChildren);
         result.setHasAlpha(false); // resource will have no alpha, since content is opaque
         Bitmap golden = BitmapFactory.decodeResource(mActivity.getResources(), goldenResId);
+
+        // We dont care about the config of the bitmap, so convert to same config before comparing
+        result = result.copy(golden.getConfig(), false);
         assertTrue(golden.sameAs(result));
     }
 
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
new file mode 100644
index 0000000..fed197e
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.view.accessibility;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * AccessibilityEvent is public, so CTS covers it pretty well. Verifying hidden methods here.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityEventTest {
+    @Test
+    public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
+        AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setImportantForAccessibility(true);
+        assertTrue(copyEventViaParcel(event).isImportantForAccessibility());
+
+        event.setImportantForAccessibility(false);
+        assertFalse(copyEventViaParcel(event).isImportantForAccessibility());
+    }
+
+    @Test
+    public void testSouceNodeId_getSetWorkAcrossParceling() {
+        final long sourceNodeId = 0x1234567890ABCDEFL;
+        AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setSourceNodeId(sourceNodeId);
+        assertEquals(sourceNodeId, copyEventViaParcel(event).getSourceNodeId());
+    }
+
+    @Test
+    public void testWindowChanges_getSetWorkAcrossParceling() {
+        final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE
+                | AccessibilityEvent.WINDOWS_CHANGE_ACTIVE
+                | AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
+        AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setWindowChanges(windowChanges);
+        assertEquals(windowChanges, copyEventViaParcel(event).getWindowChanges());
+    }
+
+    private AccessibilityEvent copyEventViaParcel(AccessibilityEvent event) {
+        Parcel parcel = Parcel.obtain();
+        event.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        return AccessibilityEvent.CREATOR.createFromParcel(parcel);
+    }
+}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 3d65bd2..68b7ac2 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -429,7 +429,7 @@
         }
 
         /**
-         * Sets an index of the font collection.
+         * Sets an index of the font collection. See {@link android.R.attr#ttcIndex}.
          *
          * Can not be used for Typeface source. build() method will return null for invalid index.
          * @param ttcIndex An index of the font collection. If the font source is not font
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 7c7417d..5a8fa07 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -34,7 +34,8 @@
     void setUserSelectable(String alias, boolean isUserSelectable);
 
     boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
-    boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain);
+    boolean attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
+            out KeymasterCertificateChain chain);
     boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
 
     // APIs used by CertInstaller and DevicePolicyManager
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index 0811100..efee8b4 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -99,48 +99,35 @@
         }
     }
 
-    /**
-     * Performs attestation of the device's identifiers. This method returns a certificate chain
-     * whose first element contains the requested device identifiers in an extension. The device's
-     * manufacturer, model, brand, device and product are always also included in the attestation.
-     * If the device supports attestation in secure hardware, the chain will be rooted at a
-     * trustworthy CA key. Otherwise, the chain will be rooted at an untrusted certificate. See
-     * <a href="https://developer.android.com/training/articles/security-key-attestation.html">
-     * Key Attestation</a> for the format of the certificate extension.
-     * <p>
-     * Attestation will only be successful when all of the following are true:
-     * 1) The device has been set up to support device identifier attestation at the factory.
-     * 2) The user has not permanently disabled device identifier attestation.
-     * 3) You have permission to access the device identifiers you are requesting attestation for.
-     * <p>
-     * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is
-     * unsuccessful, the device may not support it in general or the user may have permanently
-     * disabled it.
-     *
-     * @param context the context to use for retrieving device identifiers.
-     * @param idTypes the types of device identifiers to attest.
-     * @param attestationChallenge a blob to include in the certificate alongside the device
-     * identifiers.
-     *
-     * @return a certificate chain containing the requested device identifiers in the first element
-     *
-     * @exception SecurityException if you are not permitted to obtain an attestation of the
-     * device's identifiers.
-     * @exception DeviceIdAttestationException if the attestation operation fails.
-     */
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @NonNull public static X509Certificate[] attestDeviceIds(Context context,
-            @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
+    @NonNull private static KeymasterArguments prepareAttestationArgumentsForDeviceId(
+            Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
             DeviceIdAttestationException {
-        // Check method arguments, retrieve requested device IDs and prepare attestation arguments.
+        // Verify that device ID attestation types are provided.
         if (idTypes == null) {
             throw new NullPointerException("Missing id types");
         }
+
+        return prepareAttestationArguments(context, idTypes, attestationChallenge);
+    }
+
+    /**
+     * Prepares Keymaster Arguments with attestation data.
+     * @hide should only be used by KeyChain.
+     */
+    @NonNull public static KeymasterArguments prepareAttestationArguments(Context context,
+            @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
+            DeviceIdAttestationException {
+        // Check method arguments, retrieve requested device IDs and prepare attestation arguments.
         if (attestationChallenge == null) {
             throw new NullPointerException("Missing attestation challenge");
         }
         final KeymasterArguments attestArgs = new KeymasterArguments();
         attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, attestationChallenge);
+        // Return early if the caller did not request any device identifiers to be included in the
+        // attestation record.
+        if (idTypes == null) {
+            return attestArgs;
+        }
         final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
         for (int idType : idTypes) {
             idTypesSet.add(idType);
@@ -191,6 +178,44 @@
                 Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8));
         attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL,
                 Build.MODEL.getBytes(StandardCharsets.UTF_8));
+        return attestArgs;
+    }
+
+    /**
+     * Performs attestation of the device's identifiers. This method returns a certificate chain
+     * whose first element contains the requested device identifiers in an extension. The device's
+     * manufacturer, model, brand, device and product are always also included in the attestation.
+     * If the device supports attestation in secure hardware, the chain will be rooted at a
+     * trustworthy CA key. Otherwise, the chain will be rooted at an untrusted certificate. See
+     * <a href="https://developer.android.com/training/articles/security-key-attestation.html">
+     * Key Attestation</a> for the format of the certificate extension.
+     * <p>
+     * Attestation will only be successful when all of the following are true:
+     * 1) The device has been set up to support device identifier attestation at the factory.
+     * 2) The user has not permanently disabled device identifier attestation.
+     * 3) You have permission to access the device identifiers you are requesting attestation for.
+     * <p>
+     * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is
+     * unsuccessful, the device may not support it in general or the user may have permanently
+     * disabled it.
+     *
+     * @param context the context to use for retrieving device identifiers.
+     * @param idTypes the types of device identifiers to attest.
+     * @param attestationChallenge a blob to include in the certificate alongside the device
+     * identifiers.
+     *
+     * @return a certificate chain containing the requested device identifiers in the first element
+     *
+     * @exception SecurityException if you are not permitted to obtain an attestation of the
+     * device's identifiers.
+     * @exception DeviceIdAttestationException if the attestation operation fails.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @NonNull public static X509Certificate[] attestDeviceIds(Context context,
+            @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
+            DeviceIdAttestationException {
+        final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId(
+                context, idTypes, attestationChallenge);
 
         // Perform attestation.
         final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 1e42425..4a0d6ee 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -122,15 +122,19 @@
 void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, float x,
                                  float y) {
     auto utf16 = asciiToUtf16(text);
-    canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, minikin::Bidi::LTR, paint,
-                     nullptr);
+    SkPaint glyphPaint(paint);
+    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, minikin::Bidi::LTR,
+            glyphPaint, nullptr);
 }
 
 void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
                                  const SkPath& path) {
     auto utf16 = asciiToUtf16(text);
-    canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, paint,
-                           nullptr);
+    SkPaint glyphPaint(paint);
+    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
+            nullptr);
 }
 
 void TestUtils::TestTask::run() {
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index bf0aed9..38999cb 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -40,22 +40,18 @@
     }
 
     void doFrame(int frameNr) override {
-        std::unique_ptr<uint16_t[]> text =
-                TestUtils::asciiToUtf16("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
-        ssize_t textLength = 26 * 2;
+        const char* text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
         std::unique_ptr<Canvas> canvas(
                 Canvas::create_recording_canvas(container->stagingProperties().getWidth(),
                                                 container->stagingProperties().getHeight()));
 
         Paint paint;
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setAntiAlias(true);
         paint.setColor(Color::Black);
         for (int i = 0; i < 5; i++) {
             paint.setTextSize(10 + (frameNr % 20) + i * 20);
-            canvas->drawText(text.get(), 0, textLength, textLength, 0, 100 * (i + 2),
-                             minikin::Bidi::FORCE_LTR, paint, nullptr);
+            TestUtils::drawUtf8ToCanvas(canvas.get(), text, paint, 0, 100 * (i + 2));
         }
 
         container->setStagingDisplayList(canvas->finishRecording());
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index 6bae80c..58c9980 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -36,7 +36,6 @@
         SkPaint textPaint;
         textPaint.setTextSize(dp(20));
         textPaint.setAntiAlias(true);
-        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         TestUtils::drawUtf8ToCanvas(&canvas, "not that long long text", textPaint, dp(10), dp(30));
 
         SkPoint pts[2];
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index d7ec288..fd8c252 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -83,7 +83,6 @@
         canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
 
         SkPaint textPaint;
-        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500);
         textPaint.setTextSize(dp(20));
         textPaint.setAntiAlias(true);
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index 9eddc20..aa537b4 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -38,7 +38,6 @@
         card = TestUtils::createNode(
                 0, 0, width, height, [&](RenderProperties& props, Canvas& canvas) {
                     SkPaint paint;
-                    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
                     paint.setAntiAlias(true);
                     paint.setTextSize(50);
 
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
index fee0659..3befce4 100644
--- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -42,10 +42,8 @@
 
         mBluePaint.setColor(SkColorSetARGB(255, 0, 0, 255));
         mBluePaint.setTextSize(padding);
-        mBluePaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         mGreenPaint.setColor(SkColorSetARGB(255, 0, 255, 0));
         mGreenPaint.setTextSize(padding);
-        mGreenPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
         // interleave drawText and drawRect with saveLayer ops
         for (int i = 0; i < regions; i++, top += smallRectHeight) {
@@ -54,18 +52,15 @@
             canvas.drawColor(SkColorSetARGB(255, 255, 255, 0), SkBlendMode::kSrcOver);
             std::string stri = std::to_string(i);
             std::string offscreen = "offscreen line " + stri;
-            std::unique_ptr<uint16_t[]> offtext = TestUtils::asciiToUtf16(offscreen.c_str());
-            canvas.drawText(offtext.get(), 0, offscreen.length(), offscreen.length(), bounds.fLeft,
-                            top + padding, minikin::Bidi::FORCE_LTR, mBluePaint, nullptr);
+            TestUtils::drawUtf8ToCanvas(&canvas, offscreen.c_str(), mBluePaint, bounds.fLeft,
+                    top + padding);
             canvas.restore();
 
             canvas.drawRect(bounds.fLeft, top + padding, bounds.fRight,
                             top + smallRectHeight - padding, mBluePaint);
             std::string onscreen = "onscreen line " + stri;
-            std::unique_ptr<uint16_t[]> ontext = TestUtils::asciiToUtf16(onscreen.c_str());
-            canvas.drawText(ontext.get(), 0, onscreen.length(), onscreen.length(), bounds.fLeft,
-                            top + smallRectHeight - padding, minikin::Bidi::FORCE_LTR, mGreenPaint,
-                            nullptr);
+            TestUtils::drawUtf8ToCanvas(&canvas, onscreen.c_str(), mGreenPaint, bounds.fLeft,
+                    top + smallRectHeight - padding);
         }
     }
     void doFrame(int frameNr) override {}
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index a502116..a16b1784 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -29,7 +29,6 @@
         card = TestUtils::createNode(0, 0, width, height, [](RenderProperties& props,
                                                              Canvas& canvas) {
             SkPaint paint;
-            paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
             paint.setAntiAlias(true);
             paint.setTextSize(50);
 
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index c845e6c..003d8e9 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -117,7 +117,6 @@
                                          canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver);
 
                                          SkPaint paint;
-                                         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
                                          paint.setAntiAlias(true);
                                          paint.setTextSize(24);
 
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index e56d2f8..4eb7751 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -537,7 +537,6 @@
                 canvas.save(SaveFlags::MatrixClip);
                 canvas.clipPath(&path, SkClipOp::kIntersect);
                 SkPaint paint;
-                paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
                 paint.setAntiAlias(true);
                 paint.setTextSize(50);
                 TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
@@ -569,7 +568,6 @@
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, [](RenderProperties& props,
                                                                           RecordingCanvas& canvas) {
         SkPaint paint;
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setAntiAlias(true);
         paint.setTextSize(50);
         TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0);  // will be top clipped
@@ -603,7 +601,6 @@
                 textPaint.setAntiAlias(true);
                 textPaint.setTextSize(20);
                 textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag);
-                textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
                 for (int i = 0; i < LOOPS; i++) {
                     TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
                 }
@@ -654,7 +651,6 @@
     auto node = TestUtils::createNode<RecordingCanvas>(
             0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) {
                 SkPaint paint;
-                paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
                 paint.setAntiAlias(true);
                 paint.setTextSize(50);
                 paint.setStrokeWidth(10);
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 5aae15f..8a9e34f 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -175,7 +175,6 @@
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(20);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
     });
 
@@ -196,7 +195,6 @@
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(20);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         for (int i = 0; i < 2; i++) {
             for (int j = 0; j < 2; j++) {
                 uint32_t flags = paint.getFlags();
@@ -238,7 +236,6 @@
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(20);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setTextAlign(SkPaint::kLeft_Align);
         TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
         paint.setTextAlign(SkPaint::kCenter_Align);
@@ -805,9 +802,7 @@
         Paint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(20);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
-        canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::Bidi::FORCE_LTR, paint, NULL);
+        TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25);
     });
 
     int count = 0;
@@ -829,9 +824,7 @@
         paint.setColor(SK_ColorWHITE);
         paint.setAntiAlias(true);
         paint.setTextSize(20);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
-        canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::Bidi::FORCE_LTR, paint, NULL);
+        TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25);
     });
     Properties::enableHighContrastText = false;
 
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 4138f59..1d7dc3d 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -36,7 +36,6 @@
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(20);
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         static const char* text = "testing text bounds";
 
         // draw text directly into Recording canvas
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index 78d75d6..92d05e4 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -29,6 +29,7 @@
 RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
     SkPaint paint;
     paint.setTextSize(20);
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     GammaFontRenderer gammaFontRenderer;
     FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java
index 6d9c5e2..5d0c8e2 100644
--- a/media/java/android/media/AudioFocusInfo.java
+++ b/media/java/android/media/AudioFocusInfo.java
@@ -130,13 +130,11 @@
         dest.writeInt(mSdkTarget);
     }
 
-    @SystemApi
     @Override
     public int hashCode() {
         return Objects.hash(mAttributes, mClientUid, mClientId, mPackageName, mGainRequest, mFlags);
     }
 
-    @SystemApi
     @Override
     public boolean equals(Object obj) {
         if (this == obj)
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index f844cc1..2a82fc9 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.app.usage.StorageStatsManager;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.UriPermission;
 import android.database.Cursor;
 import android.database.MatrixCursor;
@@ -28,7 +27,9 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.IBinder;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
@@ -95,6 +96,7 @@
     private static final String ROOT_ID_HOME = "home";
 
     private StorageManager mStorageManager;
+    private UserManager mUserManager;
 
     private final Object mRootsLock = new Object();
 
@@ -105,12 +107,35 @@
     public boolean onCreate() {
         super.onCreate(DEFAULT_DOCUMENT_PROJECTION);
 
-        mStorageManager = (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
+        mStorageManager = getContext().getSystemService(StorageManager.class);
+        mUserManager = getContext().getSystemService(UserManager.class);
 
         updateVolumes();
         return true;
     }
 
+    private void enforceShellRestrictions() {
+        if (UserHandle.getCallingAppId() == android.os.Process.SHELL_UID
+                && mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) {
+            throw new SecurityException(
+                    "Shell user cannot access files for user " + UserHandle.myUserId());
+        }
+    }
+
+    @Override
+    protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
+            throws SecurityException {
+        enforceShellRestrictions();
+        return super.enforceReadPermissionInner(uri, callingPkg, callerToken);
+    }
+
+    @Override
+    protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
+            throws SecurityException {
+        enforceShellRestrictions();
+        return super.enforceWritePermissionInner(uri, callingPkg, callerToken);
+    }
+
     public void updateVolumes() {
         synchronized (mRootsLock) {
             updateVolumesLocked();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java
index 7a80a8b..24cf218 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java
@@ -21,6 +21,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+
 import com.android.printspooler.R;
 
 /**
@@ -126,6 +127,7 @@
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int childCount = getChildCount();
         final int rowCount = childCount / mColumnCount + childCount % mColumnCount;
+        final boolean isLayoutRtl = isLayoutRtl();
 
         int cellStart = getPaddingStart();
         int cellTop = getPaddingTop();
@@ -134,7 +136,13 @@
             int rowHeight = 0;
 
             for (int col = 0; col < mColumnCount; col++) {
-                final int childIndex = row * mColumnCount + col;
+                final int childIndex;
+                if (isLayoutRtl) {
+                    // if RTL, layout the right most child first
+                    childIndex = row * mColumnCount + (mColumnCount - col - 1);
+                } else {
+                    childIndex = row * mColumnCount + col;
+                }
 
                 if (childIndex >= childCount) {
                     break;
@@ -148,14 +156,14 @@
 
                 MarginLayoutParams childParams = (MarginLayoutParams) child.getLayoutParams();
 
-                final int childLeft = cellStart + childParams.getMarginStart();
+                final int childStart = cellStart + childParams.getMarginStart();
                 final int childTop = cellTop + childParams.topMargin;
-                final int childRight = childLeft + child.getMeasuredWidth();
+                final int childEnd = childStart + child.getMeasuredWidth();
                 final int childBottom = childTop + child.getMeasuredHeight();
 
-                child.layout(childLeft, childTop, childRight, childBottom);
+                child.layout(childStart, childTop, childEnd, childBottom);
 
-                cellStart = childRight + childParams.getMarginEnd();
+                cellStart = childEnd + childParams.getMarginEnd();
 
                 rowHeight = Math.max(rowHeight, child.getMeasuredHeight()
                         + childParams.topMargin + childParams.bottomMargin);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index d82db41..b035b70 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -510,8 +510,6 @@
     <string name="bluetooth_show_devices_without_names">Show Bluetooth devices without names</string>
     <!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
-    <!-- Setting Checkbox title for enabling Bluetooth inband ringing -->
-    <string name="bluetooth_enable_inband_ringing">Enable in-band ringing</string>
 
     <!-- UI debug setting: Select Bluetooth AVRCP Version -->
     <string name="bluetooth_select_avrcp_version_string">Bluetooth AVRCP Version</string>
@@ -604,8 +602,6 @@
     <string name="bluetooth_show_devices_without_names_summary">Bluetooth devices without names (MAC addresses only) will be displayed</string>
     <!-- Summary of checkbox for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
-    <!-- Summary of checkbox for enabling Bluetooth inband ringing -->
-    <string name="bluetooth_enable_inband_ringing_summary">Allow ringtones on the phone to be played on Bluetooth headsets</string>
 
     <!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
     <string name="enable_terminal_title">Local terminal</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 88f0d2b..d14b53b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -69,4 +69,12 @@
             pref.setVisible(isVisible);
         }
     }
+
+
+    /**
+     * @return a String for the summary of the preference.
+     */
+    public String getSummary() {
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index b9bf80d..b8adb6a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -98,9 +98,6 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        // Set initial content
-        showSlice(Slice.bindSlice(getContext(), mKeyguardSliceUri));
-
         // Make sure we always have the most current slice
         mLiveData.observeForever(this);
     }
@@ -265,7 +262,6 @@
 
         if (wasObserving) {
             mLiveData.observeForever(this);
-            showSlice(Slice.bindSlice(getContext(), mKeyguardSliceUri));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 593bb50..a59c97e 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -494,7 +494,8 @@
                     }
                     if (mBackground != null) {
                         RectF dest = new RectF(left, top, right, bottom);
-                        // add a filter bitmap?
+                        Log.i(TAG, "Redrawing in rect: " + dest + " with surface size: "
+                                + mLastRequestedWidth + "x" + mLastRequestedHeight);
                         c.drawBitmap(mBackground, null, dest, null);
                     }
                 } finally {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 0f0402d..bfb3a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -66,7 +66,7 @@
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
                 new DozeScreenState(wrappedService, handler),
                 createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
-                new DozeWallpaperState()
+                new DozeWallpaperState(context)
         });
 
         return machine;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index ee41001..5156272 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -23,6 +23,10 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
 import java.io.PrintWriter;
 
@@ -34,18 +38,28 @@
     private static final String TAG = "DozeWallpaperState";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    @VisibleForTesting
-    final IWallpaperManager mWallpaperManagerService;
+    private final IWallpaperManager mWallpaperManagerService;
+    private boolean mKeyguardVisible;
     private boolean mIsAmbientMode;
+    private final DozeParameters mDozeParameters;
 
-    public DozeWallpaperState() {
+    public DozeWallpaperState(Context context) {
         this(IWallpaperManager.Stub.asInterface(
-                ServiceManager.getService(Context.WALLPAPER_SERVICE)));
+                ServiceManager.getService(Context.WALLPAPER_SERVICE)),
+                new DozeParameters(context), KeyguardUpdateMonitor.getInstance(context));
     }
 
     @VisibleForTesting
-    DozeWallpaperState(IWallpaperManager wallpaperManagerService) {
+    DozeWallpaperState(IWallpaperManager wallpaperManagerService, DozeParameters parameters,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
         mWallpaperManagerService = wallpaperManagerService;
+        mDozeParameters = parameters;
+        keyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
+            @Override
+            public void onKeyguardVisibilityChanged(boolean showing) {
+                mKeyguardVisible = showing;
+            }
+        });
     }
 
     @Override
@@ -58,17 +72,25 @@
             case DOZE_REQUEST_PULSE:
             case DOZE_PULSING:
             case DOZE_PULSE_DONE:
-                isAmbientMode = true;
+                isAmbientMode = mDozeParameters.getAlwaysOn();
                 break;
             default:
                 isAmbientMode = false;
         }
 
+        final boolean animated;
+        if (isAmbientMode) {
+            animated = mDozeParameters.getCanControlScreenOffAnimation() && !mKeyguardVisible;
+        } else {
+            animated = !mDozeParameters.getDisplayNeedsBlanking();
+        }
+
         if (isAmbientMode != mIsAmbientMode) {
             mIsAmbientMode = isAmbientMode;
             try {
-                Log.i(TAG, "AoD wallpaper state changed to: " + mIsAmbientMode);
-                mWallpaperManagerService.setInAmbientMode(mIsAmbientMode);
+                Log.i(TAG, "AoD wallpaper state changed to: " + mIsAmbientMode
+                        + ", animated: " + animated);
+                mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated);
             } catch (RemoteException e) {
                 // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
                 Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 5c8c3f3..e008148 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -14,10 +14,13 @@
 
 package com.android.systemui.globalactions;
 
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
 import android.app.ActivityManager;
 import android.app.Dialog;
+import android.app.KeyguardManager;
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -120,6 +123,8 @@
     private final AudioManager mAudioManager;
     private final IDreamManager mDreamManager;
     private final DevicePolicyManager mDevicePolicyManager;
+    private final LockPatternUtils mLockPatternUtils;
+    private final KeyguardManager mKeyguardManager;
 
     private ArrayList<Action> mItems;
     private ActionsDialog mDialog;
@@ -150,6 +155,8 @@
                 ServiceManager.getService(DreamService.DREAM_SERVICE));
         mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        mLockPatternUtils = new LockPatternUtils(mContext);
+        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -323,7 +330,8 @@
                 mItems.add(getSettingsAction());
             } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
                 if (Settings.Secure.getInt(mContext.getContentResolver(),
-                            Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0) != 0) {
+                            Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0) != 0
+                        && shouldDisplayLockdown()) {
                     mItems.add(getLockdownAction());
                 }
             } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
@@ -372,6 +380,19 @@
         return dialog;
     }
 
+    private boolean shouldDisplayLockdown() {
+        int userId = getCurrentUser().id;
+        // Lockdown is meaningless without a place to go.
+        if (!mKeyguardManager.isDeviceSecure(userId)) {
+            return false;
+        }
+
+        // Only show the lockdown button if the device isn't locked down (for whatever reason).
+        int state = mLockPatternUtils.getStrongAuthForUser(userId);
+        return (state == STRONG_AUTH_NOT_REQUIRED
+                || state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
+    }
+
     private final class PowerAction extends SinglePressAction implements LongPressAction {
         private PowerAction() {
             super(R.drawable.ic_lock_power_off,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
new file mode 100644
index 0000000..f0e4ccc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
@@ -0,0 +1,89 @@
+/*
+ * 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.systemui.pip.phone;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
+
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OnOpChangedListener;
+import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Pair;
+
+public class PipAppOpsListener {
+    private static final String TAG = PipAppOpsListener.class.getSimpleName();
+
+    private Context mContext;
+    private IActivityManager mActivityManager;
+    private AppOpsManager mAppOpsManager;
+
+    private PipMotionHelper mMotionHelper;
+
+    private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
+        @Override
+        public void onOpChanged(String op, String packageName) {
+            try {
+                // Dismiss the PiP once the user disables the app ops setting for that package
+                final Pair<ComponentName, Integer> topPipActivityInfo =
+                        PipUtils.getTopPinnedActivity(mContext, mActivityManager);
+                if (topPipActivityInfo.first != null) {
+                    final ApplicationInfo appInfo = mContext.getPackageManager()
+                            .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second);
+                    if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
+                            mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
+                                    packageName) != MODE_ALLOWED) {
+                        mMotionHelper.dismissPip();
+                    }
+                }
+            } catch (NameNotFoundException e) {
+                // Unregister the listener if the package can't be found
+                unregisterAppOpsListener();
+            }
+        }
+    };
+
+    public PipAppOpsListener(Context context, IActivityManager activityManager,
+            PipMotionHelper motionHelper) {
+        mContext = context;
+        mActivityManager = activityManager;
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mMotionHelper = motionHelper;
+    }
+
+    public void onActivityPinned(String packageName) {
+        // Register for changes to the app ops setting for this package while it is in PiP
+        registerAppOpsListener(packageName);
+    }
+
+    public void onActivityUnpinned() {
+        // Unregister for changes to the previously PiP'ed package
+        unregisterAppOpsListener();
+    }
+
+    private void registerAppOpsListener(String packageName) {
+        mAppOpsManager.startWatchingMode(OP_PICTURE_IN_PICTURE, packageName,
+                mAppOpsChangedListener);
+    }
+
+    private void unregisterAppOpsListener() {
+        mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index dce3e24..36531bb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -65,6 +65,7 @@
     private PipMenuActivityController mMenuController;
     private PipMediaController mMediaController;
     private PipTouchHandler mTouchHandler;
+    private PipAppOpsListener mAppOpsListener;
 
     /**
      * Handler for system task stack changes.
@@ -75,6 +76,7 @@
             mTouchHandler.onActivityPinned();
             mMediaController.onActivityPinned();
             mMenuController.onActivityPinned();
+            mAppOpsListener.onActivityPinned(packageName);
 
             SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
         }
@@ -87,6 +89,7 @@
             final int userId = topActivity != null ? topPipActivityInfo.second : 0;
             mMenuController.onActivityUnpinned();
             mTouchHandler.onActivityUnpinned(topActivity);
+            mAppOpsListener.onActivityUnpinned();
 
             SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null);
         }
@@ -177,6 +180,8 @@
                 mInputConsumerController);
         mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController,
                 mInputConsumerController);
+        mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
+                mTouchHandler.getMotionHelper());
         EventBus.getDefault().register(this);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 8e7f83d..2705bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -16,33 +16,90 @@
 
 package com.android.systemui.doze;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.IWallpaperManager;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.DozeParameters;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(JUnit4.class)
 @SmallTest
 public class DozeWallpaperStateTest extends SysuiTestCase {
 
+    private DozeWallpaperState mDozeWallpaperState;
+    @Mock IWallpaperManager mIWallpaperManager;
+    @Mock DozeParameters mDozeParameters;
+    @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mDozeParameters,
+                mKeyguardUpdateMonitor);
+    }
+
     @Test
     public void testDreamNotification() throws RemoteException {
-        IWallpaperManager wallpaperManagerService = mock(IWallpaperManager.class);
-        DozeWallpaperState dozeWallpaperState = new DozeWallpaperState(wallpaperManagerService);
-        dozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
+        // Pre-condition
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+
+        mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
                 DozeMachine.State.DOZE_AOD);
-        verify(wallpaperManagerService).setInAmbientMode(eq(true));
-        dozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
-        verify(wallpaperManagerService).setInAmbientMode(eq(false));
+        verify(mIWallpaperManager).setInAmbientMode(eq(true), anyBoolean());
+        mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
+        verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+
+        // Make sure we're sending false when AoD is off
+        reset(mDozeParameters);
+        mDozeWallpaperState.transitionTo(DozeMachine.State.FINISH, DozeMachine.State.DOZE_AOD);
+        verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+    }
+
+    @Test
+    public void testAnimates_whenSupported() throws RemoteException {
+        // Pre-conditions
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+        when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(true);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+
+        mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
+                DozeMachine.State.DOZE_AOD);
+        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(true));
+
+        mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
+        verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+    }
+
+    @Test
+    public void testDoesNotAnimate_whenNotSupported() throws RemoteException {
+        // Pre-conditions
+        when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
+        when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(false);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+
+        mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
+                DozeMachine.State.DOZE_AOD);
+        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+
+        mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
+        verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(false));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java
index f1965a2..56de32d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java
@@ -33,6 +33,8 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -40,6 +42,30 @@
 
     private LeakDetector mLeakDetector;
 
+    // The references for which collection is observed are stored in fields. The allocation and
+    // of these references happens in separate methods (trackObjectWith/trackCollectionWith)
+    // from where they are set to null. The generated code might keep the allocated reference
+    // alive in a dex register when compiling in release mode. As R8 is used to compile this
+    // test the --dontoptimize flag is also required to ensure that these methods are not
+    // inlined, as that would defeat the purpose of having the mutation in methods.
+    private Object mObject;
+    private Collection<?> mCollection;
+
+    private CollectionWaiter trackObjectWith(Consumer<Object> tracker) {
+        mObject = new Object();
+        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(mObject);
+        tracker.accept(mObject);
+        return collectionWaiter;
+    }
+
+    private CollectionWaiter trackCollectionWith(
+            BiConsumer<? super Collection<?>, String> tracker) {
+        mCollection = new ArrayList<>();
+        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(mCollection);
+        tracker.accept(mCollection, "tag");
+        return collectionWaiter;
+    }
+
     @Before
     public void setup() {
         mLeakDetector = LeakDetector.create();
@@ -51,31 +77,22 @@
 
     @Test
     public void trackInstance_doesNotLeakTrackedObject() {
-        Object object = new Object();
-        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object);
-
-        mLeakDetector.trackInstance(object);
-        object = null;
+        CollectionWaiter collectionWaiter = trackObjectWith(mLeakDetector::trackInstance);
+        mObject = null;
         collectionWaiter.waitForCollection();
     }
 
     @Test
     public void trackCollection_doesNotLeakTrackedObject() {
-        Collection<?> object = new ArrayList<>();
-        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object);
-
-        mLeakDetector.trackCollection(object, "tag");
-        object = null;
+        CollectionWaiter collectionWaiter = trackCollectionWith(mLeakDetector::trackCollection);
+        mCollection = null;
         collectionWaiter.waitForCollection();
     }
 
     @Test
     public void trackGarbage_doesNotLeakTrackedObject() {
-        Object object = new Object();
-        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object);
-
-        mLeakDetector.trackGarbage(object);
-        object = null;
+        CollectionWaiter collectionWaiter = trackObjectWith(mLeakDetector::trackGarbage);
+        mObject = null;
         collectionWaiter.waitForCollection();
     }
 
@@ -108,4 +125,4 @@
         FileOutputStream fos = new FileOutputStream("/dev/null");
         mLeakDetector.dump(fos.getFD(), new PrintWriter(fos), new String[0]);
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java
index 9787df9..ce6212e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java
@@ -41,6 +41,13 @@
         mMap = new WeakIdentityHashMap<>();
     }
 
+    private CollectionWaiter addObjectToMap(WeakIdentityHashMap<Object, Object> map) {
+      Object object = new Object();
+      CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object);
+      map.put(object, "value");
+      return collectionWaiter;
+    }
+
     @Test
     public void testUsesIdentity() {
         String a1 = new String("a");
@@ -56,11 +63,12 @@
 
     @Test
     public void testWeaklyReferences() {
-        Object object = new Object();
-        CollectionWaiter collectionWaiter = ReferenceTestUtils.createCollectionWaiter(object);
-
-        mMap.put(object, "value");
-        object = null;
+        // Allocate and add an object to the weak map in a separate method to avoid a live
+        // reference to the allocated object in a dex register. As R8 is used to compile this
+        // test the --dontoptimize flag is also required to ensure that the method is not
+        // inlined, as that would defeat the purpose of having the allocation in a separate
+        // method.
+        CollectionWaiter collectionWaiter = addObjectToMap(mMap);
 
         // Wait until object has been collected. We'll also need to wait for mMap to become empty,
         // because our collection waiter may be told about the collection earlier than mMap.
@@ -70,4 +78,4 @@
         assertEquals(0, mMap.size());
         assertTrue(mMap.isEmpty());
     }
-}
\ No newline at end of file
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 935b787..1aaa538 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5094,6 +5094,36 @@
     // Tag used to report autofill field classification scores
     FIELD_AUTOFILL_MATCH_SCORE = 1274;
 
+    // ACTION: Usb config has been changed to charging
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_USB_CONFIG_CHARGING = 1275;
+
+    // ACTION: Usb config has been changed to mtp (file transfer)
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_USB_CONFIG_MTP = 1276;
+
+    // ACTION: Usb config has been changed to ptp (photo transfer)
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_USB_CONFIG_PTP = 1277;
+
+    // ACTION: Usb config has been changed to rndis (usb tethering)
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_USB_CONFIG_RNDIS = 1278;
+
+    // ACTION: Usb config has been changed to midi
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_USB_CONFIG_MIDI = 1279;
+
+    // ACTION: Usb config has been changed to accessory
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_USB_CONFIG_ACCESSORY = 1280;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 50b0be1..ba8ce59 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.accessibility;
 
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
 
@@ -762,7 +763,6 @@
                 mPictureInPictureActionReplacingConnection = wrapper;
                 wrapper.linkToDeath();
             }
-            mSecurityPolicy.notifyWindowsChanged();
         }
     }
 
@@ -2283,6 +2283,14 @@
         }
     }
 
+    private void sendAccessibilityEventLocked(AccessibilityEvent event, int userId) {
+        // Resync to avoid calling out with the lock held
+        event.setEventTime(SystemClock.uptimeMillis());
+        mMainHandler.obtainMessage(
+                MainHandler.MSG_SEND_ACCESSIBILITY_EVENT, userId, 0 /* unused */, event)
+                .sendToTarget();
+    }
+
     /**
      * AIDL-exposed method. System only.
      * Inform accessibility that a fingerprint gesture was performed
@@ -2419,6 +2427,7 @@
         public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
         public static final int MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER = 14;
         public static final int MSG_INIT_SERVICE = 15;
+        public static final int MSG_SEND_ACCESSIBILITY_EVENT = 16;
 
         public MainHandler(Looper looper) {
             super(looper);
@@ -2519,6 +2528,12 @@
                             (AccessibilityServiceConnection) msg.obj;
                     service.initializeService();
                 } break;
+
+                case MSG_SEND_ACCESSIBILITY_EVENT: {
+                    final AccessibilityEvent event = (AccessibilityEvent) msg.obj;
+                    final int userId = msg.arg1;
+                    sendAccessibilityEvent(event, userId);
+                }
             }
         }
 
@@ -2533,7 +2548,7 @@
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_ANNOUNCEMENT);
                     event.getText().add(message);
-                    sendAccessibilityEvent(event, mCurrentUserId);
+                    sendAccessibilityEventLocked(event, mCurrentUserId);
                 }
             }
         }
@@ -2961,21 +2976,21 @@
     public class SecurityPolicy {
         public static final int INVALID_WINDOW_ID = -1;
 
-        private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
-            AccessibilityEvent.TYPE_VIEW_CLICKED
-            | AccessibilityEvent.TYPE_VIEW_FOCUSED
-            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
-            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
-            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
-            | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
-            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
-            | AccessibilityEvent.TYPE_VIEW_SELECTED
-            | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
-            | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
-            | AccessibilityEvent.TYPE_VIEW_SCROLLED
-            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
-            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
-            | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
+        private static final int KEEP_SOURCE_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED
+                | AccessibilityEvent.TYPE_VIEW_FOCUSED
+                | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+                | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+                | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
+                | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+                | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+                | AccessibilityEvent.TYPE_WINDOWS_CHANGED
+                | AccessibilityEvent.TYPE_VIEW_SELECTED
+                | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+                | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
+                | AccessibilityEvent.TYPE_VIEW_SCROLLED
+                | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
+                | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+                | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
 
         // In Z order
         public List<AccessibilityWindowInfo> mWindows;
@@ -3137,10 +3152,10 @@
                 mWindows = new ArrayList<>();
             }
 
-            final int oldWindowCount = mWindows.size();
-            for (int i = oldWindowCount - 1; i >= 0; i--) {
-                mWindows.remove(i).recycle();
-            }
+            List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows);
+            SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone();
+
+            mWindows.clear();
             mA11yWindowInfoById.clear();
 
             for (int i = 0; i < mWindowInfoById.size(); i++) {
@@ -3202,7 +3217,49 @@
                 }
             }
 
-            notifyWindowsChanged();
+            sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById);
+
+            final int oldWindowCount = oldWindowList.size();
+            for (int i = oldWindowCount - 1; i >= 0; i--) {
+                oldWindowList.remove(i).recycle();
+            }
+        }
+
+        private void sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows,
+                SparseArray<AccessibilityWindowInfo> oldWindowsById) {
+            List<AccessibilityEvent> events = new ArrayList<>();
+            // Send events for all removed windows
+            final int oldWindowsCount = oldWindows.size();
+            for (int i = 0; i < oldWindowsCount; i++) {
+                final AccessibilityWindowInfo window = oldWindows.get(i);
+                if (mA11yWindowInfoById.get(window.getId()) == null) {
+                    events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+                            window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
+                }
+            }
+
+            // Look for other changes
+            int oldWindowIndex = 0;
+            final int newWindowCount = mWindows.size();
+            for (int i = 0; i < newWindowCount; i++) {
+                final AccessibilityWindowInfo newWindow = mWindows.get(i);
+                final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId());
+                if (oldWindow == null) {
+                    events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+                            newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED));
+                } else {
+                    int changes = newWindow.differenceFrom(oldWindow);
+                    if (changes !=  0) {
+                        events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+                                newWindow.getId(), changes));
+                    }
+                }
+            }
+
+            final int numEvents = events.size();
+            for (int i = 0; i < numEvents; i++) {
+                sendAccessibilityEventLocked(events.get(i), mCurrentUserId);
+            }
         }
 
         public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
@@ -3243,7 +3300,7 @@
         }
 
         public void updateEventSourceLocked(AccessibilityEvent event) {
-            if ((event.getEventType() & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
+            if ((event.getEventType() & KEEP_SOURCE_EVENT_TYPES) == 0) {
                 event.setSource((View) null);
             }
         }
@@ -3357,46 +3414,55 @@
 
         private void setActiveWindowLocked(int windowId) {
             if (mActiveWindowId != windowId) {
+                sendAccessibilityEventLocked(
+                        AccessibilityEvent.obtainWindowsChangedEvent(
+                                mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE),
+                        mCurrentUserId);
+
                 mActiveWindowId = windowId;
                 if (mWindows != null) {
                     final int windowCount = mWindows.size();
                     for (int i = 0; i < windowCount; i++) {
                         AccessibilityWindowInfo window = mWindows.get(i);
-                        window.setActive(window.getId() == windowId);
+                        if (window.getId() == windowId) {
+                            window.setActive(true);
+                            sendAccessibilityEventLocked(
+                                    AccessibilityEvent.obtainWindowsChangedEvent(windowId,
+                                            AccessibilityEvent.WINDOWS_CHANGE_ACTIVE),
+                                    mCurrentUserId);
+                        } else {
+                            window.setActive(false);
+                        }
                     }
                 }
-                notifyWindowsChanged();
             }
         }
 
         private void setAccessibilityFocusedWindowLocked(int windowId) {
             if (mAccessibilityFocusedWindowId != windowId) {
+                sendAccessibilityEventLocked(
+                        AccessibilityEvent.obtainWindowsChangedEvent(
+                                mAccessibilityFocusedWindowId,
+                                WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED),
+                        mCurrentUserId);
+
                 mAccessibilityFocusedWindowId = windowId;
                 if (mWindows != null) {
                     final int windowCount = mWindows.size();
                     for (int i = 0; i < windowCount; i++) {
                         AccessibilityWindowInfo window = mWindows.get(i);
-                        window.setAccessibilityFocused(window.getId() == windowId);
+                        if (window.getId() == windowId) {
+                            window.setAccessibilityFocused(true);
+                            sendAccessibilityEventLocked(
+                                    AccessibilityEvent.obtainWindowsChangedEvent(
+                                            windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED),
+                                    mCurrentUserId);
+
+                        } else {
+                            window.setAccessibilityFocused(false);
+                        }
                     }
                 }
-
-                notifyWindowsChanged();
-            }
-        }
-
-        public void notifyWindowsChanged() {
-            if (mWindowsForAccessibilityCallback == null) {
-                return;
-            }
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                // Let the client know the windows changed.
-                AccessibilityEvent event = AccessibilityEvent.obtain(
-                        AccessibilityEvent.TYPE_WINDOWS_CHANGED);
-                event.setEventTime(SystemClock.uptimeMillis());
-                sendAccessibilityEvent(event, mCurrentUserId);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/GestureUtils.java b/services/accessibility/java/com/android/server/accessibility/GestureUtils.java
index abfdb68..d5b53bc 100644
--- a/services/accessibility/java/com/android/server/accessibility/GestureUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/GestureUtils.java
@@ -40,12 +40,6 @@
         return (deltaTime >= timeout);
     }
 
-    public static boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
-        return (first.getPointerIdBits() == second.getPointerIdBits()
-                && first.getPointerId(first.getActionIndex())
-                        == second.getPointerId(second.getActionIndex()));
-    }
-
     /**
      * Determines whether a two pointer gesture is a dragging one.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 9b2b4eb..74d2ddd 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -229,7 +229,7 @@
     }
 
     void clearAndTransitionToStateDetecting() {
-        mCurrentState = mDelegatingState;
+        mCurrentState = mDetectingState;
         mDetectingState.clear();
         mViewportDraggingState.clear();
         mPanningScalingState.clear();
@@ -649,14 +649,19 @@
                 break;
                 case ACTION_MOVE: {
                     if (isFingerDown()
-                            && distance(mLastDown, /* move */ event) > mSwipeMinDistance
-                            // For convenience, viewport dragging on 3tap&hold takes precedence
-                            // over insta-delegating on 3tap&swipe
-                            // (which is a rare combo to be used aside from magnification)
-                            && !isMultiTapTriggered(2 /* taps */)) {
+                            && distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
 
-                        // Swipe detected - delegate skipping timeout
-                        transitionToDelegatingStateAndClear();
+                        // Swipe detected - transition immediately
+
+                        // For convenience, viewport dragging takes precedence
+                        // over insta-delegating on 3tap&swipe
+                        // (which is a rare combo to be used aside from magnification)
+                        if (isMultiTapTriggered(2 /* taps */)) {
+                            transitionTo(mViewportDraggingState);
+                            clear();
+                        } else {
+                            transitionToDelegatingStateAndClear();
+                        }
                     }
                 }
                 break;
@@ -755,10 +760,10 @@
                 int policyFlags) {
             if (event.getActionMasked() == ACTION_DOWN) {
                 mPreLastDown = mLastDown;
-                mLastDown = event;
+                mLastDown = MotionEvent.obtain(event);
             } else if (event.getActionMasked() == ACTION_UP) {
                 mPreLastUp = mLastUp;
-                mLastUp = event;
+                mLastUp = MotionEvent.obtain(event);
             }
 
             MotionEventInfo info = MotionEventInfo.obtain(event, rawEvent,
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e1cb154..cac7fed 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -479,7 +479,7 @@
         if (service != null) {
             service.destroySessionsLocked();
             service.updateLocked(disabled);
-            if (!service.isEnabled()) {
+            if (!service.isEnabledLocked()) {
                 removeCachedServiceLocked(userId);
             }
         }
@@ -621,6 +621,34 @@
         }
 
         @Override
+        public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
+            final int userId = UserHandle.getCallingUserId();
+
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        public List<String> getAvailableFieldClassificationAlgorithms() throws RemoteException {
+            final int userId = UserHandle.getCallingUserId();
+
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
+                }
+            }
+
+            return null;
+        }
+
+        @Override
         public ComponentName getAutofillServiceComponentName() throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4cdfd62..65984dd 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.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.EditDistanceScorer;
 import android.service.autofill.FieldClassification;
 import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillEventHistory;
@@ -63,6 +64,8 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.LocalLog;
+import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -81,6 +84,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
 
@@ -105,7 +109,10 @@
     private final AutoFillUI mUi;
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
+    @GuardedBy("mLock")
     private RemoteCallbackList<IAutoFillManagerClient> mClients;
+
+    @GuardedBy("mLock")
     private AutofillServiceInfo mInfo;
 
     private static final Random sRandom = new Random();
@@ -113,25 +120,74 @@
     private final LocalLog mRequestsHistory;
     private final LocalLog mUiLatencyHistory;
 
+    // TODO(b/70939974): temporary, will be moved to ExtServices
+    static final class FieldClassificationAlgorithmService {
+
+        /**
+         * Gets the name of all available algorithms.
+         */
+        @NonNull
+        public List<String> getAvailableAlgorithms() {
+            return Arrays.asList(EditDistanceScorer.NAME);
+        }
+
+        /**
+         * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
+         */
+        @NonNull
+        public String getDefaultAlgorithm() {
+            return EditDistanceScorer.NAME;
+        }
+
+        /**
+         * Gets a field classification score.
+         *
+         * @param algorithmName algorithm to be used. If invalid, the default algorithm will be used
+         * instead.
+         * @param algorithmArgs optional arguments to be passed to the algorithm.
+         * @param actualValue value entered by the user.
+         * @param userDataValue value from the user data.
+         *
+         * @return pair containing the algorithm used and the score.
+         */
+        // TODO(b/70939974): use parcelable instead of pair
+        Pair<String, Float> getScore(@NonNull String algorithmName, @Nullable Bundle algorithmArgs,
+                @NonNull AutofillValue actualValue, @NonNull String userDataValue) {
+            if (!EditDistanceScorer.NAME.equals(algorithmName)) {
+                Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using "
+                        + EditDistanceScorer.NAME + " instead");
+            }
+            return new Pair<>(EditDistanceScorer.NAME,
+                    EditDistanceScorer.getInstance().getScore(actualValue, userDataValue));
+        }
+    }
+
+    private final FieldClassificationAlgorithmService mFcService =
+            new FieldClassificationAlgorithmService();
+
     /**
      * Apps disabled by the service; key is package name, value is when they will be enabled again.
      */
+    @GuardedBy("mLock")
     private ArrayMap<String, Long> mDisabledApps;
 
     /**
      * Activities disabled by the service; key is component name, value is when they will be enabled
      * again.
      */
+    @GuardedBy("mLock")
     private ArrayMap<ComponentName, Long> mDisabledActivities;
 
     /**
      * Whether service was disabled for user due to {@link UserManager} restrictions.
      */
+    @GuardedBy("mLock")
     private boolean mDisabled;
 
     /**
      * Data used for field classification.
      */
+    @GuardedBy("mLock")
     private UserData mUserData;
 
     /**
@@ -235,7 +291,7 @@
     }
 
     void updateLocked(boolean disabled) {
-        final boolean wasEnabled = isEnabled();
+        final boolean wasEnabled = isEnabledLocked();
         if (sVerbose) {
             Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
                     + ", mSetupComplete= " + mSetupComplete
@@ -274,7 +330,7 @@
             Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
             mInfo = null;
         }
-        final boolean isEnabled = isEnabled();
+        final boolean isEnabled = isEnabledLocked();
         if (wasEnabled != isEnabled) {
             if (!isEnabled) {
                 final int sessionCount = mSessions.size();
@@ -292,7 +348,7 @@
             mClients = new RemoteCallbackList<>();
         }
         mClients.register(client);
-        return isEnabled();
+        return isEnabledLocked();
     }
 
     void removeClientLocked(IAutoFillManagerClient client) {
@@ -302,7 +358,7 @@
     }
 
     void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
-        if (!isEnabled()) {
+        if (!isEnabledLocked()) {
             return;
         }
         final Session session = mSessions.get(sessionId);
@@ -312,7 +368,7 @@
     }
 
     void setHasCallback(int sessionId, int uid, boolean hasIt) {
-        if (!isEnabled()) {
+        if (!isEnabledLocked()) {
             return;
         }
         final Session session = mSessions.get(sessionId);
@@ -327,7 +383,7 @@
             @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
             @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
             int flags, @NonNull ComponentName componentName) {
-        if (!isEnabled()) {
+        if (!isEnabledLocked()) {
             return 0;
         }
 
@@ -388,7 +444,7 @@
     }
 
     void finishSessionLocked(int sessionId, int uid) {
-        if (!isEnabled()) {
+        if (!isEnabledLocked()) {
             return;
         }
 
@@ -411,7 +467,7 @@
     }
 
     void cancelSessionLocked(int sessionId, int uid) {
-        if (!isEnabled()) {
+        if (!isEnabledLocked()) {
             return;
         }
 
@@ -799,14 +855,15 @@
     // Called by AutofillManager
     void setUserData(int callingUid, UserData userData) {
         synchronized (mLock) {
-            if (isCalledByServiceLocked("setUserData", callingUid)) {
-                mUserData = userData;
-                // Log it
-                int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length;
-                mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED,
-                        getServicePackageName(), null)
-                        .setCounterValue(numberFields));
+            if (!isCalledByServiceLocked("setUserData", callingUid)) {
+                return;
             }
+            mUserData = userData;
+            // Log it
+            int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length;
+            mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED,
+                    getServicePackageName(), null)
+                    .setCounterValue(numberFields));
         }
     }
 
@@ -917,6 +974,11 @@
             pw.println();
             mUserData.dump(prefix2, pw);
         }
+
+        pw.print(prefix); pw.print("Available Field Classification algorithms: ");
+        pw.println(mFcService.getAvailableAlgorithms());
+        pw.print(prefix); pw.print("Default Field Classification algorithm: ");
+        pw.println(mFcService.getDefaultAlgorithm());
     }
 
     void destroySessionsLocked() {
@@ -964,11 +1026,13 @@
                 final IAutoFillManagerClient client = clients.getBroadcastItem(i);
                 try {
                     final boolean resetSession;
+                    final boolean isEnabled;
                     synchronized (mLock) {
                         resetSession = resetClient || isClientSessionDestroyedLocked(client);
+                        isEnabled = isEnabledLocked();
                     }
                     int flags = 0;
-                    if (isEnabled()) {
+                    if (isEnabled) {
                         flags |= AutofillManager.SET_STATE_FLAG_ENABLED;
                     }
                     if (resetSession) {
@@ -1004,7 +1068,7 @@
         return true;
     }
 
-    boolean isEnabled() {
+    boolean isEnabledLocked() {
         return mSetupComplete && mInfo != null && !mDisabled;
     }
 
@@ -1093,9 +1157,9 @@
     }
 
     // Called by AutofillManager, checks UID.
-    boolean isFieldClassificationEnabled(int uid) {
+    boolean isFieldClassificationEnabled(int callingUid) {
         synchronized (mLock) {
-            if (!isCalledByServiceLocked("isFieldClassificationEnabled", uid)) {
+            if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) {
                 return false;
             }
             return isFieldClassificationEnabledLocked();
@@ -1110,6 +1174,28 @@
                 mUserId) == 1;
     }
 
+    FieldClassificationAlgorithmService getFieldClassificationService() {
+        return mFcService;
+    }
+
+    List<String> getAvailableFieldClassificationAlgorithms(int callingUid) {
+        synchronized (mLock) {
+            if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
+                return null;
+            }
+        }
+        return mFcService.getAvailableAlgorithms();
+    }
+
+    String getDefaultFieldClassificationAlgorithm(int callingUid) {
+        synchronized (mLock) {
+            if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
+                return null;
+            }
+        }
+        return mFcService.getDefaultAlgorithm();
+    }
+
     @Override
     public String toString() {
         return "AutofillManagerServiceImpl: [userId=" + mUserId
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 01f9084..9629690 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -69,6 +69,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -84,6 +85,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.autofill.AutofillManagerServiceImpl.FieldClassificationAlgorithmService;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.autofill.ui.PendingUi;
 
@@ -1088,7 +1090,8 @@
 
                     // Sets field classification score for field
                     if (userData!= null) {
-                        setScore(detectedFieldIds, detectedFieldClassifications, userData,
+                        setFieldClassificationScore(mService.getFieldClassificationService(),
+                                detectedFieldIds, detectedFieldClassifications, userData,
                                 viewState.id, currentValue);
                     }
                 } // else
@@ -1133,7 +1136,9 @@
      * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for
      * {@code fieldId} based on its {@code currentValue} and {@code userData}.
      */
-    private static void setScore(@NonNull ArrayList<AutofillId> detectedFieldIds,
+    private static void setFieldClassificationScore(
+            @NonNull AutofillManagerServiceImpl.FieldClassificationAlgorithmService  service,
+            @NonNull ArrayList<AutofillId> detectedFieldIds,
             @NonNull ArrayList<FieldClassification> detectedFieldClassifications,
             @NonNull UserData userData, @NonNull AutofillId fieldId,
             @NonNull AutofillValue currentValue) {
@@ -1150,11 +1155,16 @@
             return;
         }
 
+        final String algorithm = userData.getFieldClassificationAlgorithm();
+        final Bundle algorithmArgs = userData.getAlgorithmArgs();
         ArrayList<Match> matches = null;
         for (int i = 0; i < userValues.length; i++) {
             String remoteId = remoteIds[i];
             final String value = userValues[i];
-            final float score = userData.getScorer().getScore(currentValue, value);
+            final Pair<String, Float> result = service.getScore(algorithm, algorithmArgs,
+                    currentValue, value);
+            final String actualAlgorithm = result.first;
+            final float score = result.second;
             if (score > 0) {
                 if (sVerbose) {
                     Slog.v(TAG, "adding score " + score + " at index " + i + " and id " + fieldId);
@@ -1162,7 +1172,7 @@
                 if (matches == null) {
                     matches = new ArrayList<>(userValues.length);
                 }
-                matches.add(new Match(remoteId, score));
+                matches.add(new Match(remoteId, score, actualAlgorithm));
             }
             else if (sVerbose) Slog.v(TAG, "skipping score 0 at index " + i + " and id " + fieldId);
         }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index 537592e..b17f794 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -121,7 +121,7 @@
                 DEFAULT_FULL_BACKUP_REQUIRE_CHARGING);
         mFullBackupRequiredNetworkType = mParser.getInt(FULL_BACKUP_REQUIRED_NETWORK_TYPE,
                 DEFAULT_FULL_BACKUP_REQUIRED_NETWORK_TYPE);
-        final String backupFinishedNotificationReceivers = mParser.getString(
+        String backupFinishedNotificationReceivers = mParser.getString(
                 BACKUP_FINISHED_NOTIFICATION_RECEIVERS,
                 DEFAULT_BACKUP_FINISHED_NOTIFICATION_RECEIVERS);
         if (backupFinishedNotificationReceivers.isEmpty()) {
@@ -190,6 +190,9 @@
         return mFullBackupRequiredNetworkType;
     }
 
+    /**
+     * Returns an array of package names that should be notified whenever a backup finishes.
+     */
     public synchronized String[] getBackupFinishedNotificationReceivers() {
         if (RefactoredBackupManagerService.DEBUG_SCHEDULING) {
             Slog.v(TAG, "getBackupFinishedNotificationReceivers(...) returns "
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 3a37459..51c44e1 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -1424,6 +1424,8 @@
             final Intent notification = new Intent();
             notification.setAction(BACKUP_FINISHED_ACTION);
             notification.setPackage(receiver);
+            notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
+                    Intent.FLAG_RECEIVER_FOREGROUND);
             notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
             mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
         }
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 7efe5ca..2c8b5b4 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -66,7 +66,6 @@
     // Task in charge of monitoring timeouts
     private final BackupRestoreTask mMonitorTask;
 
-    private final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
     private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
 
     // Dedicated observer, if any
@@ -249,13 +248,12 @@
                                     Slog.d(TAG, "APK file; installing");
                                 }
                                 // Try to install the app.
-                                String installerName = mPackageInstallers.get(pkg);
+                                String installerPackageName = mPackageInstallers.get(pkg);
                                 boolean isSuccessfullyInstalled = RestoreUtils.installApk(
-                                        instream, mBackupManagerService.getPackageManager(),
-                                        mInstallObserver, mDeleteObserver, mManifestSignatures,
-                                        mPackagePolicies, info, installerName,
-                                        bytesReadListener, mBackupManagerService.getDataDir()
-                                                                                         );
+                                        instream, mBackupManagerService.getContext(),
+                                        mDeleteObserver, mManifestSignatures,
+                                        mPackagePolicies, info, installerPackageName,
+                                        bytesReadListener);
                                 // good to go; promote to ACCEPT
                                 mPackagePolicies.put(pkg, isSuccessfullyInstalled
                                         ? RestorePolicy.ACCEPT
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 3dc242f..54c2746 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -88,7 +88,6 @@
     private final String mDecryptPassword;
     private final AtomicBoolean mLatchObject;
     private final PackageManagerBackupAgent mPackageManagerBackupAgent;
-    private final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
     private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
 
     private IFullBackupRestoreObserver mObserver;
@@ -513,13 +512,11 @@
                                     Slog.d(TAG, "APK file; installing");
                                 }
                                 // Try to install the app.
-                                String installerName = mPackageInstallers.get(pkg);
-                                boolean isSuccessfullyInstalled = RestoreUtils.installApk(
-                                        instream, mBackupManagerService.getPackageManager(),
-                                        mInstallObserver, mDeleteObserver, mManifestSignatures,
-                                        mPackagePolicies, info, installerName,
-                                        bytesReadListener, mBackupManagerService.getDataDir()
-                                                                                         );
+                                String installerPackageName = mPackageInstallers.get(pkg);
+                                boolean isSuccessfullyInstalled = RestoreUtils.installApk(instream,
+                                        mBackupManagerService.getContext(),
+                                        mDeleteObserver, mManifestSignatures, mPackagePolicies,
+                                        info, installerPackageName, bytesReadListener);
                                 // good to go; promote to ACCEPT
                                 mPackagePolicies.put(pkg, isSuccessfullyInstalled
                                         ? RestorePolicy.ACCEPT
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreInstallObserver.java b/services/backup/java/com/android/server/backup/restore/RestoreInstallObserver.java
deleted file mode 100644
index 3d506f1..0000000
--- a/services/backup/java/com/android/server/backup/restore/RestoreInstallObserver.java
+++ /dev/null
@@ -1,89 +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 com.android.server.backup.restore;
-
-import android.app.PackageInstallObserver;
-import android.os.Bundle;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Synchronous implementation of PackageInstallObserver.
- *
- * Allows the caller to synchronously wait for package install event.
- */
-public class RestoreInstallObserver extends PackageInstallObserver {
-
-    @GuardedBy("mDone")
-    private final AtomicBoolean mDone = new AtomicBoolean();
-
-    private String mPackageName;
-    private int mResult;
-
-    public RestoreInstallObserver() {
-    }
-
-    /**
-     * Resets the observer to prepare for another installation.
-     */
-    public void reset() {
-        synchronized (mDone) {
-            mDone.set(false);
-        }
-    }
-
-    /**
-     * Synchronously waits for completion.
-     */
-    public void waitForCompletion() {
-        synchronized (mDone) {
-            while (mDone.get() == false) {
-                try {
-                    mDone.wait();
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns result code.
-     */
-    public int getResult() {
-        return mResult;
-    }
-
-    /**
-     * Returns installed package name.
-     */
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    @Override
-    public void onPackageInstalled(String packageName, int returnCode,
-            String msg, Bundle extras) {
-        synchronized (mDone) {
-            mResult = returnCode;
-            mPackageName = packageName;
-            mDone.set(true);
-            mDone.notifyAll();
-        }
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index ee4201e..632f5b5 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -19,23 +19,31 @@
 import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
 import static com.android.server.backup.RefactoredBackupManagerService.TAG;
 
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.Session;
+import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
-import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Process;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.restore.RestoreDeleteObserver;
-import com.android.server.backup.restore.RestoreInstallObserver;
 import com.android.server.backup.restore.RestorePolicy;
 
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.HashMap;
 
 /**
@@ -47,62 +55,73 @@
      * Reads apk contents from input stream and installs the apk.
      *
      * @param instream - input stream to read apk data from.
-     * @param packageManager - {@link PackageManager} instance.
-     * @param installObserver - {@link RestoreInstallObserver} instance.
+     * @param context - installing context
      * @param deleteObserver - {@link RestoreDeleteObserver} instance.
      * @param manifestSignatures - manifest signatures.
      * @param packagePolicies - package policies.
      * @param info - backup file info.
-     * @param installerPackage - installer package.
+     * @param installerPackageName - package name of installer.
      * @param bytesReadListener - listener to be called for counting bytes read.
-     * @param dataDir - directory where to create apk file.
      * @return true if apk was successfully read and installed and false otherwise.
      */
     // TODO: Refactor to get rid of unneeded params.
-    public static boolean installApk(InputStream instream, PackageManager packageManager,
-            RestoreInstallObserver installObserver, RestoreDeleteObserver deleteObserver,
+    public static boolean installApk(InputStream instream, Context context,
+            RestoreDeleteObserver deleteObserver,
             HashMap<String, Signature[]> manifestSignatures,
             HashMap<String, RestorePolicy> packagePolicies,
             FileMetadata info,
-            String installerPackage, BytesReadListener bytesReadListener,
-            File dataDir) {
+            String installerPackageName, BytesReadListener bytesReadListener) {
         boolean okay = true;
 
         if (DEBUG) {
             Slog.d(TAG, "Installing from backup: " + info.packageName);
         }
 
-        // The file content is an .apk file.  Copy it out to a staging location and
-        // attempt to install it.
-        File apkFile = new File(dataDir, info.packageName);
         try {
-            FileOutputStream apkStream = new FileOutputStream(apkFile);
-            byte[] buffer = new byte[32 * 1024];
-            long size = info.size;
-            while (size > 0) {
-                long toRead = (buffer.length < size) ? buffer.length : size;
-                int didRead = instream.read(buffer, 0, (int) toRead);
-                if (didRead >= 0) {
-                    bytesReadListener.onBytesRead(didRead);
+            LocalIntentReceiver receiver = new LocalIntentReceiver();
+            PackageManager packageManager = context.getPackageManager();
+            PackageInstaller installer = packageManager.getPackageInstaller();
+
+            SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+            params.setInstallerPackageName(installerPackageName);
+            int sessionId = installer.createSession(params);
+            try {
+                try (Session session = installer.openSession(sessionId)) {
+                    try (OutputStream apkStream = session.openWrite(info.packageName, 0,
+                            info.size)) {
+                        byte[] buffer = new byte[32 * 1024];
+                        long size = info.size;
+                        while (size > 0) {
+                            long toRead = (buffer.length < size) ? buffer.length : size;
+                            int didRead = instream.read(buffer, 0, (int) toRead);
+                            if (didRead >= 0) {
+                                bytesReadListener.onBytesRead(didRead);
+                            }
+                            apkStream.write(buffer, 0, didRead);
+                            size -= didRead;
+                        }
+                    }
+
+                    // Installation is current disabled
+                    session.abandon();
+                    // session.commit(receiver.getIntentSender());
                 }
-                apkStream.write(buffer, 0, didRead);
-                size -= didRead;
+            } catch (Exception t) {
+                installer.abandonSession(sessionId);
+
+                throw t;
             }
-            apkStream.close();
 
-            // make sure the installer can read it
-            apkFile.setReadable(true, false);
+            // Installation is current disabled
+            Intent result = null;
+            // Intent result = receiver.getResult();
 
-            // Now install it
-            Uri packageUri = Uri.fromFile(apkFile);
-            installObserver.reset();
-            // TODO: PackageManager.installPackage() is deprecated, refactor.
-            packageManager.installPackage(packageUri, installObserver,
-                    PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
-                    installerPackage);
-            installObserver.waitForCompletion();
+            // Installation is current disabled
+            int status = PackageInstaller.STATUS_FAILURE;
+            // int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+            //        PackageInstaller.STATUS_FAILURE);
 
-            if (installObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
+            if (status != PackageInstaller.STATUS_SUCCESS) {
                 // The only time we continue to accept install of data even if the
                 // apk install failed is if we had already determined that we could
                 // accept the data regardless.
@@ -112,10 +131,12 @@
             } else {
                 // Okay, the install succeeded.  Make sure it was the right app.
                 boolean uninstall = false;
-                if (!installObserver.getPackageName().equals(info.packageName)) {
+                final String installedPackageName = result.getStringExtra(
+                        PackageInstaller.EXTRA_PACKAGE_NAME);
+                if (!installedPackageName.equals(info.packageName)) {
                     Slog.w(TAG, "Restore stream claimed to include apk for "
                             + info.packageName + " but apk was really "
-                            + installObserver.getPackageName());
+                            + installedPackageName);
                     // delete the package we just put in place; it might be fraudulent
                     okay = false;
                     uninstall = true;
@@ -161,7 +182,7 @@
                 if (uninstall) {
                     deleteObserver.reset();
                     packageManager.deletePackage(
-                            installObserver.getPackageName(),
+                            installedPackageName,
                             deleteObserver, 0);
                     deleteObserver.waitForCompletion();
                 }
@@ -169,10 +190,44 @@
         } catch (IOException e) {
             Slog.e(TAG, "Unable to transcribe restored apk for install");
             okay = false;
-        } finally {
-            apkFile.delete();
         }
 
         return okay;
     }
+
+    private static class LocalIntentReceiver {
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
+        private Intent mResult = null;
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                synchronized (mLock) {
+                    mResult = intent;
+                    mLock.notifyAll();
+                }
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public Intent getResult() {
+            synchronized (mLock) {
+                while (mResult == null) {
+                    try {
+                        mLock.wait();
+                    } catch (InterruptedException e) {
+                        // ignored
+                    }
+                }
+
+                return mResult;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index 9877717..5e6e9d34 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -196,11 +196,14 @@
      * Mixes in the output from HW RNG (if present) into the Linux RNG.
      */
     private void addHwRandomEntropy() {
+        if (!new File(hwRandomDevice).exists()) {
+            // HW RNG not present/exposed -- ignore
+            return;
+        }
+
         try {
             RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false);
             Slog.i(TAG, "Added HW RNG output to entropy pool");
-        } catch (FileNotFoundException ignored) {
-            // HW RNG not present/exposed -- ignore
         } catch (IOException e) {
             Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e);
         }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d3ab125..989cb88 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.DUMP;
 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -1220,7 +1221,11 @@
                                 info.getSpiRecord(direction).getSpi());
             }
         } catch (ServiceSpecificException e) {
-            // FIXME: get the error code and throw is at an IOException from Errno Exception
+            if (e.errorCode == EINVAL) {
+                throw new IllegalArgumentException(e.toString());
+            } else {
+                throw e;
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 44c0227..33f7769 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -26,6 +26,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.database.ContentObserver;
 import android.location.LocationManager;
 import android.net.INetworkRecommendationProvider;
@@ -50,14 +51,17 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.TransferPipe;
+import com.android.internal.telephony.SmsApplication;
 import com.android.internal.util.DumpUtils;
 
 import java.io.FileDescriptor;
@@ -91,7 +95,8 @@
     private final Object mPackageMonitorLock = new Object();
     private final Object mServiceConnectionLock = new Object();
     private final Handler mHandler;
-    private final DispatchingContentObserver mContentObserver;
+    private final DispatchingContentObserver mRecommendationSettingsObserver;
+    private final ContentObserver mUseOpenWifiPackageObserver;
     private final Function<NetworkScorerAppData, ScoringServiceConnection> mServiceConnProducer;
 
     @GuardedBy("mPackageMonitorLock")
@@ -255,8 +260,40 @@
         mContext.registerReceiverAsUser(
                 mLocationModeReceiver, UserHandle.SYSTEM, locationModeFilter,
                 null /* broadcastPermission*/, mHandler);
-        mContentObserver = new DispatchingContentObserver(context, mHandler);
+        mRecommendationSettingsObserver = new DispatchingContentObserver(context, mHandler);
         mServiceConnProducer = serviceConnProducer;
+        mUseOpenWifiPackageObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri, int userId) {
+                Uri useOpenWifiPkgUri = Global.getUriFor(Global.USE_OPEN_WIFI_PACKAGE);
+                if (useOpenWifiPkgUri.equals(uri)) {
+                    String useOpenWifiPackage = Global.getString(mContext.getContentResolver(),
+                            Global.USE_OPEN_WIFI_PACKAGE);
+                    if (!TextUtils.isEmpty(useOpenWifiPackage)) {
+                        LocalServices.getService(PackageManagerInternal.class)
+                                .grantDefaultPermissionsToDefaultUseOpenWifiApp(useOpenWifiPackage,
+                                        userId);
+                    }
+                }
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Global.getUriFor(Global.USE_OPEN_WIFI_PACKAGE),
+                false /*notifyForDescendants*/,
+                mUseOpenWifiPackageObserver);
+        // Set a callback for the package manager to query the use open wifi app.
+        LocalServices.getService(PackageManagerInternal.class).setUseOpenWifiAppPackagesProvider(
+                new PackageManagerInternal.PackagesProvider() {
+                    @Override
+                    public String[] getPackages(int userId) {
+                        String useOpenWifiPackage = Global.getString(mContext.getContentResolver(),
+                                Global.USE_OPEN_WIFI_PACKAGE);
+                        if (!TextUtils.isEmpty(useOpenWifiPackage)) {
+                            return new String[]{useOpenWifiPackage};
+                        }
+                        return null;
+                    }
+                });
     }
 
     /** Called when the system is ready to run third-party code but before it actually does so. */
@@ -287,11 +324,11 @@
 
     private void registerRecommendationSettingsObserver() {
         final Uri packageNameUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_PACKAGE);
-        mContentObserver.observe(packageNameUri,
+        mRecommendationSettingsObserver.observe(packageNameUri,
                 ServiceHandler.MSG_RECOMMENDATIONS_PACKAGE_CHANGED);
 
         final Uri settingUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
-        mContentObserver.observe(settingUri,
+        mRecommendationSettingsObserver.observe(settingUri,
                 ServiceHandler.MSG_RECOMMENDATION_ENABLED_SETTING_CHANGED);
     }
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 6a0d3ff..90822d1 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -968,11 +968,6 @@
                         || mForceAdoptable) {
                     flags |= DiskInfo.FLAG_ADOPTABLE;
                 }
-                // Adoptable storage isn't currently supported on FBE devices
-                if (StorageManager.isFileEncryptedNativeOnly()
-                        && !SystemProperties.getBoolean(StorageManager.PROP_ADOPTABLE_FBE, false)) {
-                    flags &= ~DiskInfo.FLAG_ADOPTABLE;
-                }
                 mDisks.put(diskId, new DiskInfo(diskId, flags));
             }
         }
@@ -1910,12 +1905,6 @@
         }
 
         if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
-            if (StorageManager.isFileEncryptedNativeOnly()
-                    && !SystemProperties.getBoolean(StorageManager.PROP_ADOPTABLE_FBE, false)) {
-                throw new IllegalStateException(
-                        "Adoptable storage not available on device with native FBE");
-            }
-
             synchronized (mLock) {
                 mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
 
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 31aea63..8d2e3a2 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2007,11 +2007,11 @@
                     getAccountRemovedReceivers(accountToRename, accounts);
                 accounts.accountsDb.beginTransaction();
                 Account renamedAccount = new Account(newName, accountToRename.type);
-                if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
-                    Log.e(TAG, "renameAccount failed - account with new name already exists");
-                    return null;
-                }
                 try {
+                    if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
+                        Log.e(TAG, "renameAccount failed - account with new name already exists");
+                        return null;
+                    }
                     final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
                     if (accountId >= 0) {
                         accounts.accountsDb.renameCeAccount(accountId, newName);
@@ -5595,25 +5595,26 @@
         long ident = Binder.clearCallingIdentity();
         try {
             packages = mPackageManager.getPackagesForUid(callingUid);
+            if (packages != null) {
+                for (String name : packages) {
+                    try {
+                        PackageInfo packageInfo =
+                                mPackageManager.getPackageInfo(name, 0 /* flags */);
+                        if (packageInfo != null
+                                && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+                                != 0) {
+                            return true;
+                        }
+                    } catch (NameNotFoundException e) {
+                        Log.w(TAG, String.format("Could not find package [%s]", name), e);
+                    }
+                }
+            } else {
+                Log.w(TAG, "No known packages with uid " + callingUid);
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        if (packages != null) {
-            for (String name : packages) {
-                try {
-                    PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
-                    if (packageInfo != null
-                            && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
-                                    != 0) {
-                        return true;
-                    }
-                } catch (NameNotFoundException e) {
-                    Log.w(TAG, String.format("Could not find package [%s]", name), e);
-                }
-            }
-        } else {
-            Log.w(TAG, "No known packages with uid " + callingUid);
-        }
         return false;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0a42aa9..21085fa 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3116,6 +3116,10 @@
         // Need to make sure the pinned stack exist so we can resize it below...
         stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
 
+        // Calculate the target bounds here before the task is reparented back into pinned windowing
+        // mode (which will reset the saved bounds)
+        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
         try {
             final TaskRecord task = r.getTask();
             // Resize the pinned stack to match the current size of the task the activity we are
@@ -3154,11 +3158,6 @@
             mWindowManager.continueSurfaceLayout();
         }
 
-        // Calculate the default bounds (don't use existing stack bounds as we may have just created
-        // the stack, and schedule the start of the animation into PiP (the bounds animator that
-        // is triggered by this is posted on another thread)
-        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
         stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
                 true /* fromFullscreen */);
 
diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java
index 1e70809..014f708 100644
--- a/services/core/java/com/android/server/am/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java
@@ -21,7 +21,6 @@
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
 import android.app.servertransaction.ActivityLifecycleItem;
-import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -44,12 +43,8 @@
      */
     void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
         transaction.schedule();
-        if (!(transaction.getClient() instanceof Binder)) {
-            // If client is not an instance of Binder - it's a remote call and at this point it is
-            // safe to recycle the object. All objects used for local calls will be recycled after
-            // the transaction is executed on client in ActivityThread.
-            transaction.recycle();
-        }
+        // TODO: b/70616950
+        //transaction.recycle();
     }
 
     /**
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 3064144..ef51665 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -25,6 +25,7 @@
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
 import android.os.ParcelableException;
+import android.util.Slog;
 
 import com.android.server.SystemService;
 
@@ -33,6 +34,8 @@
 import java.util.OptionalInt;
 
 public class BroadcastRadioService extends SystemService {
+    private static final String TAG = "BcRadioSrv";
+
     private final ServiceImpl mServiceImpl = new ServiceImpl();
 
     private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1 =
@@ -84,13 +87,14 @@
         @Override
         public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
                 boolean withAudio, ITunerCallback callback) {
+            Slog.i(TAG, "openTuner(" + moduleId + ", _, " + withAudio + ", _)");
             enforcePolicyAccess();
             if (callback == null) {
                 throw new IllegalArgumentException("Callback must not be empty");
             }
             synchronized (mLock) {
                 if (mHal2.hasModule(moduleId)) {
-                    throw new RuntimeException("Not implemented");
+                    return mHal2.openSession(moduleId, callback);
                 } else {
                     return mHal1.openTuner(moduleId, bandConfig, withAudio, callback);
                 }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 7629477..413a27c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -17,6 +17,8 @@
 package com.android.server.broadcastradio.hal2;
 
 import android.annotation.NonNull;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
 import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
 import android.hidl.manager.V1_0.IServiceManager;
@@ -28,6 +30,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 public class BroadcastRadioService {
@@ -76,4 +79,15 @@
     public boolean hasModule(int id) {
         return mModules.containsKey(id);
     }
+
+    public ITuner openSession(int moduleId, @NonNull ITunerCallback callback) {
+        Objects.requireNonNull(callback);
+
+        RadioModule module = mModules.get(moduleId);
+        if (module == null) {
+            throw new IllegalArgumentException("Invalid module ID");
+        }
+
+        return module.openSession(callback);
+    }
 }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index c3394e9..434e262 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -18,12 +18,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.broadcastradio.V2_0.AmFmBandRange;
+import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
 import android.hardware.broadcastradio.V2_0.Properties;
+import android.hardware.broadcastradio.V2_0.Result;
 import android.hardware.broadcastradio.V2_0.VendorKeyValue;
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
+import android.os.ParcelableException;
 import android.util.Slog;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -35,6 +40,28 @@
 class Convert {
     private static final String TAG = "BcRadio2Srv.convert";
 
+    static void throwOnError(String action, int result) {
+        switch (result) {
+            case Result.OK:
+                return;
+            case Result.UNKNOWN_ERROR:
+                throw new ParcelableException(new RuntimeException(action + ": UNKNOWN_ERROR"));
+            case Result.INTERNAL_ERROR:
+                throw new ParcelableException(new RuntimeException(action + ": INTERNAL_ERROR"));
+            case Result.INVALID_ARGUMENTS:
+                throw new IllegalArgumentException(action + ": INVALID_ARGUMENTS");
+            case Result.INVALID_STATE:
+                throw new IllegalStateException(action + ": INVALID_STATE");
+            case Result.NOT_SUPPORTED:
+                throw new UnsupportedOperationException(action + ": NOT_SUPPORTED");
+            case Result.TIMEOUT:
+                throw new ParcelableException(new RuntimeException(action + ": TIMEOUT"));
+            default:
+                throw new ParcelableException(new RuntimeException(
+                        action + ": unknown error (" + result + ")"));
+        }
+    }
+
     private static @NonNull Map<String, String>
     vendorInfoFromHal(@Nullable List<VendorKeyValue> info) {
         if (info == null) return Collections.emptyMap();
@@ -94,12 +121,47 @@
         return pTypes.stream().mapToInt(Integer::intValue).toArray();
     }
 
-    static @NonNull RadioManager.ModuleProperties
-    propertiesFromHal(int id, @NonNull String serviceName, Properties prop) {
-        Objects.requireNonNull(prop);
+    private static @NonNull RadioManager.BandDescriptor[]
+    amfmConfigToBands(@Nullable AmFmRegionConfig config) {
+        if (config == null) return new RadioManager.BandDescriptor[0];
 
-        // TODO(b/69958423): implement region info
-        RadioManager.BandDescriptor[] bands = new RadioManager.BandDescriptor[0];
+        int len = config.ranges.size();
+        List<RadioManager.BandDescriptor> bands = new ArrayList<>(len);
+
+        // Just a dummy value.
+        int region = RadioManager.REGION_ITU_1;
+
+        for (AmFmBandRange range : config.ranges) {
+            FrequencyBand bandType = Utils.getBand(range.lowerBound);
+            if (bandType == FrequencyBand.UNKNOWN) {
+                Slog.e(TAG, "Unknown frequency band at " + range.lowerBound + "kHz");
+                continue;
+            }
+            if (bandType == FrequencyBand.FM) {
+                bands.add(new RadioManager.FmBandDescriptor(region, RadioManager.BAND_FM,
+                    range.lowerBound, range.upperBound, range.spacing,
+
+                    // TODO(b/69958777): stereo, rds, ta, af, ea
+                    true, true, true, true, true
+                ));
+            } else {  // AM
+                bands.add(new RadioManager.AmBandDescriptor(region, RadioManager.BAND_AM,
+                    range.lowerBound, range.upperBound, range.spacing,
+
+                    // TODO(b/69958777): stereo
+                    true
+                ));
+            }
+        }
+
+        return bands.toArray(new RadioManager.BandDescriptor[bands.size()]);
+    }
+
+    static @NonNull RadioManager.ModuleProperties
+    propertiesFromHal(int id, @NonNull String serviceName, @NonNull Properties prop,
+            @Nullable AmFmRegionConfig amfmConfig) {
+        Objects.requireNonNull(serviceName);
+        Objects.requireNonNull(prop);
 
         int[] supportedIdentifierTypes = prop.supportedIdentifierTypes.stream().
                 mapToInt(Integer::intValue).toArray();
@@ -123,7 +185,7 @@
                 1,      // numAudioSources
                 false,  // isCaptureSupported
 
-                bands,
+                amfmConfigToBands(amfmConfig),
                 true,  // isBgScanSupported is deprecated
                 supportedProgramTypes,
                 supportedIdentifierTypes,
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Mutable.java b/services/core/java/com/android/server/broadcastradio/hal2/Mutable.java
new file mode 100644
index 0000000..a9d8054
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Mutable.java
@@ -0,0 +1,46 @@
+/**
+ * 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.broadcastradio.hal2;
+
+/**
+ * A wrapper class for mutable objects to be used in non-mutable contexts
+ * (i.e. final variables catched in lambda closures).
+ *
+ * @param <E> type of boxed value.
+ */
+final class Mutable<E> {
+    /**
+     * A mutable value.
+     */
+    public E value;
+
+    /**
+     * Initialize value with null pointer.
+     */
+    public Mutable() {
+        value = null;
+    }
+
+    /**
+     * Initialize value with specific value.
+     *
+     * @param value initial value.
+     */
+    public Mutable(E value) {
+        this.value = value;
+    }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 34c1b0c..8a7ac73 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -18,9 +18,15 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.radio.ITuner;
 import android.hardware.radio.RadioManager;
+import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
 import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.hardware.broadcastradio.V2_0.ITunerSession;
+import android.hardware.broadcastradio.V2_0.Result;
+import android.os.ParcelableException;
 import android.os.RemoteException;
+import android.util.MutableInt;
 import android.util.Slog;
 
 import java.util.Objects;
@@ -42,8 +48,13 @@
             IBroadcastRadio service = IBroadcastRadio.getService();
             if (service == null) return null;
 
+            Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>();
+            service.getAmFmRegionConfig(false, (int result, AmFmRegionConfig config) -> {
+                if (result == Result.OK) amfmConfig.value = config;
+            });
+
             RadioManager.ModuleProperties prop =
-                    Convert.propertiesFromHal(idx, fqName, service.getProperties());
+                    Convert.propertiesFromHal(idx, fqName, service.getProperties(), amfmConfig.value);
 
             return new RadioModule(service, prop);
         } catch (RemoteException ex) {
@@ -51,4 +62,44 @@
             return null;
         }
     }
+
+    public @NonNull ITuner openSession(@NonNull android.hardware.radio.ITunerCallback userCb) {
+        TunerCallback cb = new TunerCallback(Objects.requireNonNull(userCb));
+        Mutable<ITunerSession> hwSession = new Mutable<>();
+        MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
+
+        try {
+            mService.openSession(cb, (int result, ITunerSession session) -> {
+                hwSession.value = session;
+                halResult.value = result;
+            });
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "failed to open session", ex);
+            throw new ParcelableException(ex);
+        }
+
+        Convert.throwOnError("openSession", halResult.value);
+        Objects.requireNonNull(hwSession.value);
+
+        TunerSession session = new TunerSession(hwSession.value, cb);
+
+        // send out legacy callback about band configuration
+        RadioManager.BandDescriptor[] bands = mProperties.getBands();
+        if (bands != null && bands.length > 0) {
+            RadioManager.BandDescriptor descr = bands[0];  // just pick first
+            Mutable<RadioManager.BandConfig> config = new Mutable<>();
+            if (descr instanceof RadioManager.FmBandDescriptor) {
+                config.value = new RadioManager.FmBandConfig((RadioManager.FmBandDescriptor)descr);
+            } else if (descr instanceof RadioManager.AmBandDescriptor) {
+                config.value = new RadioManager.AmBandConfig((RadioManager.AmBandDescriptor)descr);
+            } else {
+                Slog.w(TAG, "Descriptor is neither AM nor FM");
+            }
+            if (config.value != null) {
+                TunerCallback.dispatch(() -> userCb.onConfigurationChanged(config.value));
+            }
+        }
+
+        return session;
+    }
 }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java
new file mode 100644
index 0000000..5ee6a4c
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerCallback.java
@@ -0,0 +1,66 @@
+/**
+ * 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.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.hardware.broadcastradio.V2_0.ITunerCallback;
+import android.hardware.broadcastradio.V2_0.ProgramInfo;
+import android.hardware.broadcastradio.V2_0.ProgramListChunk;
+import android.hardware.broadcastradio.V2_0.ProgramSelector;
+import android.hardware.broadcastradio.V2_0.VendorKeyValue;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+class TunerCallback extends ITunerCallback.Stub {
+    private static final String TAG = "BcRadio2Srv.cb";
+
+    final android.hardware.radio.ITunerCallback mCb;
+
+    interface RunnableThrowingRemoteException {
+        void run() throws RemoteException;
+    }
+
+    TunerCallback(@NonNull android.hardware.radio.ITunerCallback clientCallback) {
+        mCb = Objects.requireNonNull(clientCallback);
+    }
+
+    static void dispatch(RunnableThrowingRemoteException func) {
+        try {
+            func.run();
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "callback call failed", ex);
+        }
+    }
+
+    @Override
+    public void onTuneFailed(int result, ProgramSelector selector) {}
+
+    @Override
+    public void onCurrentProgramInfoChanged(ProgramInfo info) {}
+
+    @Override
+    public void onProgramListUpdated(ProgramListChunk chunk) {}
+
+    @Override
+    public void onAntennaStateChange(boolean connected) {}
+
+    @Override
+    public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {}
+}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
new file mode 100644
index 0000000..e8faf3d
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -0,0 +1,137 @@
+/**
+ * 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.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.graphics.Bitmap;
+import android.hardware.broadcastradio.V2_0.ITunerSession;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.util.Slog;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+class TunerSession extends ITuner.Stub {
+    private static final String TAG = "BcRadio2Srv.session";
+
+    private final Object mLock = new Object();
+
+    private final ITunerSession mHwSession;
+    private final TunerCallback mCallback;
+    private boolean mIsClosed = false;
+
+    TunerSession(@NonNull ITunerSession hwSession, @NonNull TunerCallback callback) {
+        mHwSession = Objects.requireNonNull(hwSession);
+        mCallback = Objects.requireNonNull(callback);
+    }
+
+    @Override
+    public void close() {
+        synchronized (mLock) {
+            if (mIsClosed) return;
+            mIsClosed = true;
+        }
+    }
+
+    @Override
+    public boolean isClosed() {
+        return mIsClosed;
+    }
+
+    private void checkNotClosedLocked() {
+        if (mIsClosed) {
+            throw new IllegalStateException("Tuner is closed, no further operations are allowed");
+        }
+    }
+
+    @Override
+    public void setConfiguration(RadioManager.BandConfig config) {}
+
+    @Override
+    public RadioManager.BandConfig getConfiguration() {
+        return null;
+    }
+
+    @Override
+    public void setMuted(boolean mute) {}
+
+    @Override
+    public boolean isMuted() {
+        return false;
+    }
+
+    @Override
+    public void step(boolean directionDown, boolean skipSubChannel) {}
+
+    @Override
+    public void scan(boolean directionDown, boolean skipSubChannel) {}
+
+    @Override
+    public void tune(ProgramSelector selector) {}
+
+    @Override
+    public void cancel() {}
+
+    @Override
+    public void cancelAnnouncement() {}
+
+    @Override
+    public RadioManager.ProgramInfo getProgramInformation() {
+        return null;
+    }
+
+    @Override
+    public Bitmap getImage(int id) {
+        return null;
+    }
+
+    @Override
+    public boolean startBackgroundScan() {
+        return false;
+    }
+
+    @Override
+    public List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
+        return null;
+    }
+
+    @Override
+    public boolean isAnalogForced() {
+        return false;
+    }
+
+    @Override
+    public void setAnalogForced(boolean isForced) {}
+
+    @Override
+    public Map setParameters(Map parameters) {
+        return null;
+    }
+
+    @Override
+    public Map getParameters(List<String> keys) {
+        return null;
+    }
+
+    @Override
+    public boolean isAntennaConnected() {
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
new file mode 100644
index 0000000..3520f37
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
@@ -0,0 +1,40 @@
+/**
+ * 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.broadcastradio.hal2;
+
+enum FrequencyBand {
+    UNKNOWN,
+    FM,
+    AM_LW,
+    AM_MW,
+    AM_SW,
+};
+
+class Utils {
+    private static final String TAG = "BcRadio2Srv.utils";
+
+    static FrequencyBand getBand(int freq) {
+        // keep in sync with hardware/interfaces/broadcastradio/common/utils2x/Utils.cpp
+        if (freq < 30) return FrequencyBand.UNKNOWN;
+        if (freq < 500) return FrequencyBand.AM_LW;
+        if (freq < 1705) return FrequencyBand.AM_MW;
+        if (freq < 30000) return FrequencyBand.AM_SW;
+        if (freq < 60000) return FrequencyBand.UNKNOWN;
+        if (freq < 110000) return FrequencyBand.FM;
+        return FrequencyBand.UNKNOWN;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7715727..c7a4315 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -305,6 +305,7 @@
         } else {
             for (Network underlying : underlyingNetworks) {
                 final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
+                if (underlyingCaps == null) continue;
                 for (int underlyingType : underlyingCaps.getTransportTypes()) {
                     transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
                 }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 3b9d40f..0b62907 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,6 +17,8 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.hardware.display.BrightnessConfiguration;
 import android.os.PowerManager;
 import android.util.MathUtils;
@@ -41,11 +43,30 @@
     private static final boolean DEBUG = false;
 
     @Nullable
-    public static BrightnessMappingStrategy create(
-            float[] luxLevels, int[] brightnessLevelsBacklight, float[] brightnessLevelsNits,
-            float[] nitsRange, int[] backlightRange) {
+    public static BrightnessMappingStrategy create(Resources resources) {
+        float[] luxLevels = getLuxLevels(resources.getIntArray(
+                com.android.internal.R.array.config_autoBrightnessLevels));
+        int[] brightnessLevelsBacklight = resources.getIntArray(
+                com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+        float[] brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
+                com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
+
+        float[] nitsRange = getFloatArray(resources.obtainTypedArray(
+                com.android.internal.R.array.config_screenBrightnessNits));
+        int[] backlightRange = resources.getIntArray(
+                com.android.internal.R.array.config_screenBrightnessBacklight);
+
         if (isValidMapping(nitsRange, backlightRange)
                 && isValidMapping(luxLevels, brightnessLevelsNits)) {
+            int minimumBacklight = resources.getInteger(
+                    com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
+            int maximumBacklight = resources.getInteger(
+                    com.android.internal.R.integer.config_screenBrightnessSettingMaximum);
+            if (backlightRange[0] > minimumBacklight
+                    || backlightRange[backlightRange.length - 1] < maximumBacklight) {
+                Slog.w(TAG, "Screen brightness mapping does not cover whole range of available"
+                        + " backlight values, autobrightness functionality may be impaired.");
+            }
             BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
             builder.setCurve(luxLevels, brightnessLevelsNits);
             return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange);
@@ -56,6 +77,25 @@
         }
     }
 
+    private static float[] getLuxLevels(int[] lux) {
+        // The first control point is implicit and always at 0 lux.
+        float[] levels = new float[lux.length + 1];
+        for (int i = 0; i < lux.length; i++) {
+            levels[i + 1] = (float) lux[i];
+        }
+        return levels;
+    }
+
+    private static float[] getFloatArray(TypedArray array) {
+        final int N = array.length();
+        float[] vals = new float[N];
+        for (int i = 0; i < N; i++) {
+            vals[i] = array.getFloat(i, -1.0f);
+        }
+        array.recycle();
+        return vals;
+    }
+
     private static boolean isValidMapping(float[] x, float[] y) {
         if (x == null || y == null || x.length == 0 || y.length == 0) {
             return false;
@@ -124,10 +164,17 @@
      * brightness and 0 is the display at minimum brightness.
      *
      * @param lux The current ambient brightness in lux.
-     * @return The desired brightness of the display compressed to the range [0, 1.0].
+     * @return The desired brightness of the display normalized to the range [0, 1.0].
      */
     public abstract float getBrightness(float lux);
 
+    /**
+     * Gets the display's brightness in nits for the given backlight value.
+     *
+     * Returns -1.0f if there's no available mapping for the backlight to nits.
+     */
+    public abstract float getNits(int backlight);
+
     public abstract void dump(PrintWriter pw);
 
     private static float normalizeAbsoluteBrightness(int brightness) {
@@ -186,6 +233,11 @@
         }
 
         @Override
+        public float getNits(int backlight) {
+            return -1.0f;
+        }
+
+        @Override
         public void dump(PrintWriter pw) {
             pw.println("SimpleMappingStrategy");
             pw.println("  mSpline=" + mSpline);
@@ -209,7 +261,11 @@
 
         // A spline mapping from nits to the corresponding backlight value, normalized to the range
         // [0, 1.0].
-        private final Spline mBacklightSpline;
+        private final Spline mNitsToBacklightSpline;
+
+        // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to
+        // a brightness in nits.
+        private final Spline mBacklightToNitsSpline;
 
         // The default brightness configuration.
         private final BrightnessConfiguration mDefaultConfig;
@@ -227,19 +283,18 @@
 
             // Setup the backlight spline
             final int N = nits.length;
-            float[] x = new float[N];
-            float[] y = new float[N];
+            float[] normalizedBacklight = new float[N];
             for (int i = 0; i < N; i++) {
-                x[i] = nits[i];
-                y[i] = normalizeAbsoluteBrightness(backlight[i]);
+                normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
             }
 
-            mBacklightSpline = Spline.createSpline(x, y);
+            mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
+            mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
             if (DEBUG) {
-                Slog.d(TAG, "Backlight spline: " + mBacklightSpline);
+                Slog.d(TAG, "Backlight spline: " + mNitsToBacklightSpline);
                 for (float v = 1f; v < nits[nits.length - 1] * 1.25f; v *= 1.25f) {
                     Slog.d(TAG, String.format(
-                                "  %7.1f: %7.1f", v, mBacklightSpline.interpolate(v)));
+                                "  %7.1f: %7.1f", v, mNitsToBacklightSpline.interpolate(v)));
                 }
             }
 
@@ -275,7 +330,12 @@
 
         @Override
         public float getBrightness(float lux) {
-            return mBacklightSpline.interpolate(mBrightnessSpline.interpolate(lux));
+            return mNitsToBacklightSpline.interpolate(mBrightnessSpline.interpolate(lux));
+        }
+
+        @Override
+        public float getNits(int backlight) {
+            return mBacklightToNitsSpline.interpolate(normalizeAbsoluteBrightness(backlight));
         }
 
         @Override
@@ -283,7 +343,7 @@
             pw.println("PhysicalMappingStrategy");
             pw.println("  mConfig=" + mConfig);
             pw.println("  mBrightnessSpline=" + mBrightnessSpline);
-            pw.println("  mBacklightSpline=" + mBacklightSpline);
+            pw.println("  mNitsToBacklightSpline=" + mNitsToBacklightSpline);
         }
     }
 }
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 42247f9..cbb1c01 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -24,7 +24,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
-import android.database.ContentObserver;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -34,6 +33,8 @@
 import android.os.BatteryManager;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -88,7 +89,7 @@
 
     private static final String TAG_EVENTS = "events";
     private static final String TAG_EVENT = "event";
-    private static final String ATTR_BRIGHTNESS = "brightness";
+    private static final String ATTR_NITS = "nits";
     private static final String ATTR_TIMESTAMP = "timestamp";
     private static final String ATTR_PACKAGE_NAME = "packageName";
     private static final String ATTR_USER = "user";
@@ -97,7 +98,10 @@
     private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
     private static final String ATTR_NIGHT_MODE = "nightMode";
     private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
-    private static final String ATTR_LAST_BRIGHTNESS = "lastBrightness";
+    private static final String ATTR_LAST_NITS = "lastNits";
+
+    private static final int MSG_BACKGROUND_START = 0;
+    private static final int MSG_BRIGHTNESS_CHANGED = 1;
 
     // Lock held while accessing mEvents, is held while writing events to flash.
     private final Object mEventsLock = new Object();
@@ -113,9 +117,7 @@
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private Handler mBgHandler;
-    // mSettingsObserver, mBroadcastReceiver and mSensorListener should only be used on
-    // the mBgHandler thread.
-    private SettingsObserver mSettingsObserver;
+    // mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread.
     private BroadcastReceiver mBroadcastReceiver;
     private SensorListener mSensorListener;
 
@@ -126,9 +128,9 @@
     @GuardedBy("mDataCollectionLock")
     private float mLastBatteryLevel = Float.NaN;
     @GuardedBy("mDataCollectionLock")
-    private int mIgnoreBrightness = -1;
+    private float mLastBrightness = -1;
     @GuardedBy("mDataCollectionLock")
-    private int mLastBrightness = -1;
+    private boolean mStarted;
 
     private final Injector mInjector;
 
@@ -144,33 +146,31 @@
         }
     }
 
-    /** Start listening for brightness slider events */
-    public void start() {
+    /**
+     * Start listening for brightness slider events
+     *
+     * @param brightness the initial screen brightness
+     */
+    public void start(float initialBrightness) {
         if (DEBUG) {
             Slog.d(TAG, "Start");
         }
-        mBgHandler = mInjector.getBackgroundHandler();
+        mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
         mUserManager = mContext.getSystemService(UserManager.class);
 
-        mBgHandler.post(() -> backgroundStart());
+        mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
     }
 
-    private void backgroundStart() {
+    private void backgroundStart(float initialBrightness) {
         readEvents();
 
-        mLastBrightness = mInjector.getSystemIntForUser(mContentResolver,
-                Settings.System.SCREEN_BRIGHTNESS, -1,
-                UserHandle.USER_CURRENT);
-
         mSensorListener = new SensorListener();
 
+
         if (mInjector.isInteractive(mContext)) {
             mInjector.registerSensorListener(mContext, mSensorListener, mBgHandler);
         }
 
-        mSettingsObserver = new SettingsObserver(mBgHandler);
-        mInjector.registerBrightnessObserver(mContentResolver, mSettingsObserver);
-
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_SHUTDOWN);
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -180,6 +180,10 @@
         mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
 
         mInjector.scheduleIdleJob(mContext);
+        synchronized (mDataCollectionLock) {
+            mLastBrightness = initialBrightness;
+            mStarted = true;
+        }
     }
 
     /** Stop listening for events */
@@ -188,10 +192,14 @@
         if (DEBUG) {
             Slog.d(TAG, "Stop");
         }
+        mBgHandler.removeMessages(MSG_BACKGROUND_START);
         mInjector.unregisterSensorListener(mContext, mSensorListener);
         mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
-        mInjector.unregisterBrightnessObserver(mContext, mSettingsObserver);
         mInjector.cancelIdleJob(mContext);
+
+        synchronized (mDataCollectionLock) {
+            mStarted = false;
+        }
     }
 
     /**
@@ -220,40 +228,45 @@
         return new ParceledListSlice<>(out);
     }
 
-    /** Sets brightness without logging the brightness change event */
-    public void setBrightness(int brightness, int userId) {
-        synchronized (mDataCollectionLock) {
-            mIgnoreBrightness = brightness;
-        }
-        mInjector.putSystemIntForUser(mContentResolver, Settings.System.SCREEN_BRIGHTNESS,
-                brightness, userId);
-    }
-
     public void persistEvents() {
         scheduleWriteEvents();
     }
 
-    private void handleBrightnessChanged() {
+    /**
+     * Notify the BrightnessTracker that the user has changed the brightness of the display.
+     */
+    public void notifyBrightnessChanged(float brightness, boolean userInitiated) {
         if (DEBUG) {
-            Slog.d(TAG, "Brightness change");
+            Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
+                        brightness, userInitiated));
         }
-        final BrightnessChangeEvent event = new BrightnessChangeEvent();
-        event.timeStamp = mInjector.currentTimeMillis();
+        Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
+                userInitiated ? 1 : 0, 0 /*unused*/, (Float) brightness);
+        m.sendToTarget();
+    }
 
-        int brightness = mInjector.getSystemIntForUser(mContentResolver,
-                Settings.System.SCREEN_BRIGHTNESS, -1,
-                UserHandle.USER_CURRENT);
-
+    private void handleBrightnessChanged(float brightness, boolean userInitiated) {
+        final BrightnessChangeEvent event;
         synchronized (mDataCollectionLock) {
-            int previousBrightness = mLastBrightness;
-            mLastBrightness = brightness;
-
-            if (brightness == -1 || brightness == mIgnoreBrightness) {
-                // Notified of brightness change but no setting or self change so ignore.
-                mIgnoreBrightness = -1;
+            if (!mStarted) {
+                // Not currently gathering brightness change information
                 return;
             }
 
+            float previousBrightness = mLastBrightness;
+            mLastBrightness = brightness;
+
+            if (!userInitiated) {
+                // We want to record what current brightness is so that we know what the user
+                // changed it from, but if it wasn't user initiated then we don't want to record it
+                // as a BrightnessChangeEvent.
+                return;
+            }
+
+
+            event = new BrightnessChangeEvent();
+            event.timeStamp = mInjector.currentTimeMillis();
+
             final int readingCount = mLastSensorReadings.size();
             if (readingCount == 0) {
                 // No sensor data so ignore this.
@@ -386,7 +399,7 @@
             if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
                 mEvents.append(toWrite[i]);
                 out.startTag(null, TAG_EVENT);
-                out.attribute(null, ATTR_BRIGHTNESS, Integer.toString(toWrite[i].brightness));
+                out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness));
                 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
                 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
                 out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
@@ -394,8 +407,8 @@
                 out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
                 out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
                         toWrite[i].colorTemperature));
-                out.attribute(null, ATTR_LAST_BRIGHTNESS,
-                        Integer.toString(toWrite[i].lastBrightness));
+                out.attribute(null, ATTR_LAST_NITS,
+                        Float.toString(toWrite[i].lastBrightness));
                 StringBuilder luxValues = new StringBuilder();
                 StringBuilder luxTimestamps = new StringBuilder();
                 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
@@ -446,8 +459,8 @@
                 if (TAG_EVENT.equals(tag)) {
                     BrightnessChangeEvent event = new BrightnessChangeEvent();
 
-                    String brightness = parser.getAttributeValue(null, ATTR_BRIGHTNESS);
-                    event.brightness = Integer.parseInt(brightness);
+                    String brightness = parser.getAttributeValue(null, ATTR_NITS);
+                    event.brightness = Float.parseFloat(brightness);
                     String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
                     event.timeStamp = Long.parseLong(timestamp);
                     event.packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
@@ -460,8 +473,8 @@
                     String colorTemperature =
                             parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
                     event.colorTemperature = Integer.parseInt(colorTemperature);
-                    String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_BRIGHTNESS);
-                    event.lastBrightness = Integer.parseInt(lastBrightness);
+                    String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS);
+                    event.lastBrightness = Float.parseFloat(lastBrightness);
 
                     String luxValue = parser.getAttributeValue(null, ATTR_LUX);
                     String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
@@ -582,22 +595,6 @@
         }
     }
 
-    private final class SettingsObserver extends ContentObserver {
-        public SettingsObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (DEBUG) {
-                Slog.v(TAG, "settings change " + uri);
-            }
-            // Self change is based on observer passed to notifyObserver, SettingsProvider
-            // passes null so no changes are self changes.
-            handleBrightnessChanged();
-        }
-    }
-
     private final class Receiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -623,6 +620,24 @@
         }
     }
 
+    private final class TrackerHandler extends Handler {
+        public TrackerHandler(Looper looper) {
+            super(looper, null, true /*async*/);
+        }
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_BACKGROUND_START:
+                    backgroundStart((float)msg.obj /*initial brightness*/);
+                    break;
+                case MSG_BRIGHTNESS_CHANGED:
+                    float newBrightness = (float) msg.obj;
+                    boolean userInitiatedChange = (msg.arg1 == 1);
+                    handleBrightnessChanged(newBrightness, userInitiatedChange);
+                    break;
+            }
+        }
+    }
+
     @VisibleForTesting
     static class Injector {
         public void registerSensorListener(Context context,
@@ -638,18 +653,6 @@
             sensorManager.unregisterListener(sensorListener);
         }
 
-        public void registerBrightnessObserver(ContentResolver resolver,
-                ContentObserver settingsObserver) {
-            resolver.registerContentObserver(Settings.System.getUriFor(
-                    Settings.System.SCREEN_BRIGHTNESS),
-                    false, settingsObserver, UserHandle.USER_ALL);
-        }
-
-        public void unregisterBrightnessObserver(Context context,
-                ContentObserver settingsObserver) {
-            context.getContentResolver().unregisterContentObserver(settingsObserver);
-        }
-
         public void registerReceiver(Context context,
                 BroadcastReceiver receiver, IntentFilter filter) {
             context.registerReceiver(receiver, filter);
@@ -664,16 +667,6 @@
             return BackgroundThread.getHandler();
         }
 
-        public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue,
-                int userId) {
-            return Settings.System.getIntForUser(resolver, setting, defaultValue, userId);
-        }
-
-        public void putSystemIntForUser(ContentResolver resolver, String setting, int value,
-                int userId) {
-            Settings.System.putIntForUser(resolver, setting, value, userId);
-        }
-
         public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
                 int userId) {
             return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9b97934..02e4fe0 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -270,8 +270,6 @@
 
     private final Injector mInjector;
 
-    private final BrightnessTracker mBrightnessTracker;
-
     public DisplayManagerService(Context context) {
         this(context, new Injector());
     }
@@ -290,7 +288,6 @@
 
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
-        mBrightnessTracker = new BrightnessTracker(context, null);
         mCurrentUserId = UserHandle.USER_SYSTEM;
     }
 
@@ -1339,9 +1336,6 @@
 
             pw.println();
             mPersistentDataStore.dump(pw);
-
-            pw.println();
-            mBrightnessTracker.dump(pw);
         }
     }
 
@@ -1418,10 +1412,6 @@
                     break;
                 }
 
-                case MSG_REGISTER_BRIGHTNESS_TRACKER:
-                    mBrightnessTracker.start();
-                    break;
-
                 case MSG_LOAD_BRIGHTNESS_CONFIGURATION:
                     loadBrightnessConfiguration();
                     break;
@@ -1833,22 +1823,9 @@
             final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
-                return mBrightnessTracker.getEvents(userId, hasUsageStats);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override // Binder call
-        public void setBrightness(int brightness) {
-            // STOPSHIP - remove when adaptive brightness controller accepts curves.
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.BRIGHTNESS_SLIDER_USAGE,
-                    "Permission to set brightness.");
-            int userId = UserHandle.getUserId(Binder.getCallingUid());
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mBrightnessTracker.setBrightness(brightness, userId);
+                synchronized (mSyncRoot) {
+                    return mDisplayPowerController.getBrightnessEvents(userId, hasUsageStats);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2028,7 +2005,9 @@
 
         @Override
         public void persistBrightnessSliderEvents() {
-            mBrightnessTracker.persistEvents();
+            synchronized (mSyncRoot) {
+                mDisplayPowerController.persistBrightnessSliderEvents();
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a2d9548..e5a4b0a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -24,13 +24,16 @@
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -96,7 +99,6 @@
     private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
     private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
     private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
-    private static final int MSG_USER_SWITCH = 6;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -151,9 +153,6 @@
     // The dim screen brightness.
     private final int mScreenBrightnessDimConfig;
 
-    // The minimum screen brightness to use in a very dark room.
-    private final int mScreenBrightnessDarkConfig;
-
     // The minimum allowed brightness.
     private final int mScreenBrightnessRangeMinimum;
 
@@ -261,6 +260,13 @@
     private long mScreenOnBlockStartRealTime;
     private long mScreenOffBlockStartRealTime;
 
+    // The last brightness that was set by the user and not temporary. Set to -1 when a brightness
+    // has yet to be recorded.
+    private int mLastBrightness;
+    // The last auto brightness adjustment that was set by the user and not temporary. Set to
+    // Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
+    private float mLastAutoBrightnessAdjustment;
+
     // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_SCREEN_* fields.
     private int mReportedScreenStateToPolicy;
 
@@ -289,6 +295,10 @@
     // The controller for the automatic brightness level.
     private AutomaticBrightnessController mAutomaticBrightnessController;
 
+    // The mapper between ambient lux, display backlight values, and display brightness.
+    @Nullable
+    private BrightnessMappingStrategy mBrightnessMapper;
+
     // The default brightness configuration. Used for whenever we don't have a valid brightness
     // configuration set. This is typically seen with users that don't have a brightness
     // configuration that's different from the default.
@@ -302,6 +312,9 @@
     private ObjectAnimator mColorFadeOffAnimator;
     private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
 
+    // Tracker for brightness changes
+    private final BrightnessTracker mBrightnessTracker;
+
     /**
      * Creates the display power controller.
      */
@@ -309,6 +322,7 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker) {
         mHandler = new DisplayControllerHandler(handler.getLooper());
+        mBrightnessTracker = new BrightnessTracker(context, null);
         mCallbacks = callbacks;
 
         mBatteryStats = BatteryStatsService.getService();
@@ -327,23 +341,8 @@
         mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
                 com.android.internal.R.integer.config_screenBrightnessDim));
 
-        mScreenBrightnessDarkConfig = clampAbsoluteBrightness(resources.getInteger(
-                com.android.internal.R.integer.config_screenBrightnessDark));
-        if (mScreenBrightnessDarkConfig > mScreenBrightnessDimConfig) {
-            Slog.w(TAG, "Expected config_screenBrightnessDark ("
-                    + mScreenBrightnessDarkConfig + ") to be less than or equal to "
-                    + "config_screenBrightnessDim (" + mScreenBrightnessDimConfig + ").");
-        }
-        if (mScreenBrightnessDarkConfig > screenBrightnessSettingMinimum) {
-            Slog.w(TAG, "Expected config_screenBrightnessDark ("
-                    + mScreenBrightnessDarkConfig + ") to be less than or equal to "
-                    + "config_screenBrightnessSettingMinimum ("
-                    + screenBrightnessSettingMinimum + ").");
-        }
-
-        int screenBrightnessRangeMinimum = Math.min(Math.min(
-                screenBrightnessSettingMinimum, mScreenBrightnessDimConfig),
-                mScreenBrightnessDarkConfig);
+        mScreenBrightnessRangeMinimum =
+                Math.min(screenBrightnessSettingMinimum, mScreenBrightnessDimConfig);
 
         mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger(
                     com.android.internal.R.integer.config_screenBrightnessSettingMaximum));
@@ -362,18 +361,6 @@
                 com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
 
         if (mUseSoftwareAutoBrightnessConfig) {
-            float[] luxLevels = getLuxLevels(resources.getIntArray(
-                    com.android.internal.R.array.config_autoBrightnessLevels));
-            int[] backlightValues = resources.getIntArray(
-                    com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
-            float[] brightnessValuesNits = getFloatArray(resources.obtainTypedArray(
-                    com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
-
-            final float screenMinimumNits = resources.getFloat(
-                    com.android.internal.R.dimen.config_screenBrightnessMinimumNits);
-            final float screenMaximumNits = resources.getFloat(
-                    com.android.internal.R.dimen.config_screenBrightnessMaximumNits);
-
             final float dozeScaleFactor = resources.getFraction(
                     com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
                     1, 1);
@@ -413,31 +400,13 @@
                         + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
             }
 
-            if (backlightValues != null && backlightValues.length > 0) {
-                final int bottom = backlightValues[0];
-                if (mScreenBrightnessDarkConfig > bottom) {
-                    Slog.w(TAG, "config_screenBrightnessDark (" + mScreenBrightnessDarkConfig
-                            + ") should be less than or equal to the first value of "
-                            + "config_autoBrightnessLcdBacklightValues ("
-                            + bottom + ").");
-                }
-                if (bottom < screenBrightnessRangeMinimum) {
-                    screenBrightnessRangeMinimum = bottom;
-                }
-            }
-
-            float[] nitsRange = { screenMinimumNits, screenMaximumNits };
-            int[] backlightRange = { screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum };
-
-            BrightnessMappingStrategy mapper = BrightnessMappingStrategy.create(
-                    luxLevels, backlightValues, brightnessValuesNits,
-                    nitsRange, backlightRange);
-            if (mapper != null) {
+            mBrightnessMapper = BrightnessMappingStrategy.create(resources);
+            if (mBrightnessMapper != null) {
                 mAutomaticBrightnessController = new AutomaticBrightnessController(this,
-                        handler.getLooper(), sensorManager, mapper, lightSensorWarmUpTimeConfig,
-                        screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum,
-                        dozeScaleFactor, lightSensorRate, initialLightSensorRate,
-                        brighteningLightDebounce, darkeningLightDebounce,
+                        handler.getLooper(), sensorManager, mBrightnessMapper,
+                        lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
+                        mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
+                        initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientLightHorizon,
                         autoBrightnessAdjustmentMaxGamma, dynamicHysteresis);
             } else {
@@ -445,9 +414,6 @@
             }
         }
 
-        mScreenBrightnessRangeMinimum = screenBrightnessRangeMinimum;
-
-
         mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
         mColorFadeFadesConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_animateScreenLights);
@@ -466,25 +432,8 @@
             }
         }
 
-    }
-
-    private static float[] getLuxLevels(int[] lux) {
-        // The first control point is implicit and always at 0 lux.
-        float[] levels = new float[lux.length + 1];
-        for (int i = 0; i < lux.length; i++) {
-            levels[i + 1] = (float) lux[i];
-        }
-        return levels;
-    }
-
-    private static float[] getFloatArray(TypedArray array) {
-        final int N = array.length();
-        float[] vals = new float[N];
-        for (int i = 0; i < N; i++) {
-            vals[i] = array.getFloat(i, -1.0f);
-        }
-        array.recycle();
-        return vals;
+        mLastBrightness = -1;
+        mLastAutoBrightnessAdjustment = Float.NaN;
     }
 
     /**
@@ -495,6 +444,23 @@
     }
 
     /**
+     * Get the {@link BrightnessChangeEvent}s for the specified user.
+     * @param userId userId to fetch data for
+     * @param includePackage if false will null out the package name in events
+     */
+    public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
+            @UserIdInt int userId, boolean includePackage) {
+        return mBrightnessTracker.getEvents(userId, includePackage);
+    }
+
+    /**
+     * Persist the brightness slider events to disk.
+     */
+    public void persistBrightnessSliderEvents() {
+        mBrightnessTracker.persistEvents();
+    }
+
+    /**
      * Requests a new power state.
      * The controller makes a copy of the provided object and then
      * begins adjusting the power state to match what was requested.
@@ -588,6 +554,12 @@
         } catch (RemoteException ex) {
             // same process
         }
+
+        // Initialize all of the brightness tracking state
+        final float brightness = getNits(mPowerState.getScreenBrightness());
+        if (brightness >= 0.0f) {
+            mBrightnessTracker.start(brightness);
+        }
     }
 
     private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@@ -722,16 +694,32 @@
             brightness = PowerManager.BRIGHTNESS_OFF;
         }
 
-        // Configure auto-brightness.
-        boolean autoBrightnessEnabled = false;
-        if (mAutomaticBrightnessController != null) {
-            final boolean autoBrightnessEnabledInDoze =
-                    mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
-            autoBrightnessEnabled = mPowerRequest.useAutoBrightness
+
+        final boolean autoBrightnessEnabledInDoze =
+                mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
+        final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
                     && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
-                    && brightness < 0;
-            final boolean userInitiatedChange = autoBrightnessAdjustmentChanged
-                    && mPowerRequest.brightnessSetByUser;
+                    && brightness < 0
+                    && mAutomaticBrightnessController != null;
+        final boolean brightnessAdjustmentChanged =
+                !Float.isNaN(mLastAutoBrightnessAdjustment)
+                && mPowerRequest.screenAutoBrightnessAdjustment != mLastAutoBrightnessAdjustment;
+        final boolean brightnessChanged = mLastBrightness >= 0
+                && mPowerRequest.screenBrightness != mLastBrightness;
+
+        // Update the last set brightness values.
+        final boolean userInitiatedChange;
+        if (mPowerRequest.brightnessSetByUser && !mPowerRequest.brightnessIsTemporary) {
+            userInitiatedChange = autoBrightnessEnabled && brightnessAdjustmentChanged
+                    || !autoBrightnessEnabled && brightnessChanged;
+            mLastBrightness = mPowerRequest.screenBrightness;
+            mLastAutoBrightnessAdjustment = mPowerRequest.screenAutoBrightnessAdjustment;
+        } else {
+            userInitiatedChange = false;
+        }
+
+        // Configure auto-brightness.
+        if (mAutomaticBrightnessController != null) {
             mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                     mBrightnessConfiguration, mPowerRequest.screenAutoBrightnessAdjustment,
                     state != Display.STATE_ON, userInitiatedChange);
@@ -854,6 +842,13 @@
                 animateScreenBrightness(brightness,
                         slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
             }
+
+            final float brightnessInNits = getNits(brightness);
+            if (!mPowerRequest.brightnessIsTemporary && brightnessInNits >= 0.0f) {
+                // We only want to track changes made by the user and on devices that can actually
+                // map the display backlight values into a physical brightness unit.
+                mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiatedChange);
+            }
         }
 
         // Determine whether the display is ready for use in the newly requested state.
@@ -1312,6 +1307,14 @@
         mHandler.post(mOnStateChangedRunnable);
     }
 
+    private float getNits(int backlight) {
+        if (mBrightnessMapper != null) {
+            return mBrightnessMapper.getNits(backlight);
+        } else {
+            return -1.0f;
+        }
+    }
+
     private final Runnable mOnStateChangedRunnable = new Runnable() {
         @Override
         public void run() {
@@ -1362,7 +1365,6 @@
         pw.println("Display Power Controller Configuration:");
         pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
         pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
-        pw.println("  mScreenBrightnessDarkConfig=" + mScreenBrightnessDarkConfig);
         pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
         pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
         pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
@@ -1383,7 +1385,6 @@
         pw.println("Display Power Controller Thread State:");
         pw.println("  mPowerRequest=" + mPowerRequest);
         pw.println("  mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
-
         pw.println("  mProximitySensor=" + mProximitySensor);
         pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
         pw.println("  mProximityThreshold=" + mProximityThreshold);
@@ -1392,6 +1393,8 @@
         pw.println("  mPendingProximityDebounceTime="
                 + TimeUtils.formatUptime(mPendingProximityDebounceTime));
         pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
+        pw.println("  mLastBrightness=" + mLastBrightness);
+        pw.println("  mLastAutoBrightnessAdjustment=" + mLastAutoBrightnessAdjustment);
         pw.println("  mAppliedAutoBrightness=" + mAppliedAutoBrightness);
         pw.println("  mAppliedDimming=" + mAppliedDimming);
         pw.println("  mAppliedLowPower=" + mAppliedLowPower);
@@ -1420,6 +1423,10 @@
             mAutomaticBrightnessController.dump(pw);
         }
 
+        if (mBrightnessTracker != null) {
+            pw.println();
+            mBrightnessTracker.dump(pw);
+        }
     }
 
     private static String proximityToString(int state) {
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
index c356b63..033437a 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -221,7 +221,7 @@
             case Result.NOT_INIT:
                 return ContextHubTransaction.RESULT_FAILED_UNINITIALIZED;
             case Result.TRANSACTION_PENDING:
-                return ContextHubTransaction.RESULT_FAILED_PENDING;
+                return ContextHubTransaction.RESULT_FAILED_BUSY;
             case Result.TRANSACTION_FAILED:
             case Result.UNKNOWN_FAILURE:
             default: /* fall through */
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 482acef..1fa8da7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1956,81 +1956,78 @@
 
     @Override
     public void initRecoveryService(@NonNull String rootCertificateAlias,
-            @NonNull byte[] signedPublicKeyList, @UserIdInt int userId)
-            throws RemoteException {
+            @NonNull byte[] signedPublicKeyList) throws RemoteException {
         mRecoverableKeyStoreManager.initRecoveryService(rootCertificateAlias,
-                signedPublicKeyList, userId);
+                signedPublicKeyList);
     }
 
     @Override
-    public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, @UserIdInt int userId)
-            throws RemoteException {
-        return mRecoverableKeyStoreManager.getRecoveryData(account, userId);
+    public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account) throws RemoteException {
+        return mRecoverableKeyStoreManager.getRecoveryData(account);
     }
 
-    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent, int userId)
+    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
             throws RemoteException {
-        mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent, userId);
+        mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent);
     }
 
-    public Map getRecoverySnapshotVersions(int userId) throws RemoteException {
-        return mRecoverableKeyStoreManager.getRecoverySnapshotVersions(userId);
+    public Map getRecoverySnapshotVersions() throws RemoteException {
+        return mRecoverableKeyStoreManager.getRecoverySnapshotVersions();
     }
 
     @Override
-    public void setServerParameters(long serverParameters, @UserIdInt int userId)
-            throws RemoteException {
-        mRecoverableKeyStoreManager.setServerParameters(serverParameters, userId);
+    public void setServerParameters(long serverParameters) throws RemoteException {
+        mRecoverableKeyStoreManager.setServerParameters(serverParameters);
     }
 
     @Override
     public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
-            int status, @UserIdInt int userId) throws RemoteException {
-        mRecoverableKeyStoreManager.setRecoveryStatus(packageName, aliases, status, userId);
+            int status) throws RemoteException {
+        mRecoverableKeyStoreManager.setRecoveryStatus(packageName, aliases, status);
     }
 
-    public Map getRecoveryStatus(@Nullable String packageName, int userId) throws RemoteException {
-        return mRecoverableKeyStoreManager.getRecoveryStatus(packageName, userId);
+    public Map getRecoveryStatus(@Nullable String packageName) throws RemoteException {
+        return mRecoverableKeyStoreManager.getRecoveryStatus(packageName);
     }
 
     @Override
     public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
-            int[] secretTypes, @UserIdInt int userId) throws RemoteException {
-        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes, userId);
+            int[] secretTypes) throws RemoteException {
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
     }
 
     @Override
-    public int[] getRecoverySecretTypes(@UserIdInt int userId) throws RemoteException {
-        return mRecoverableKeyStoreManager.getRecoverySecretTypes(userId);
+    public int[] getRecoverySecretTypes() throws RemoteException {
+        return mRecoverableKeyStoreManager.getRecoverySecretTypes();
 
     }
 
     @Override
-    public int[] getPendingRecoverySecretTypes(@UserIdInt int userId) throws RemoteException {
+    public int[] getPendingRecoverySecretTypes() throws RemoteException {
         throw new SecurityException("Not implemented");
     }
 
     @Override
-    public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret,
-            @UserIdInt int userId) throws RemoteException {
-        mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret, userId);
+    public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
+            throws RemoteException {
+        mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
     }
 
     @Override
     public byte[] startRecoverySession(@NonNull String sessionId,
             @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
-            @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets,
-            @UserIdInt int userId) throws RemoteException {
+            @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets)
+            throws RemoteException {
         return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey,
-                vaultParams, vaultChallenge, secrets, userId);
+                vaultParams, vaultChallenge, secrets);
     }
 
     @Override
-    public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob,
-            @NonNull List<KeyEntryRecoveryData> applicationKeys, @UserIdInt int userId)
+    public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
+            @NonNull byte[] recoveryKeyBlob, @NonNull List<KeyEntryRecoveryData> applicationKeys)
             throws RemoteException {
         return mRecoverableKeyStoreManager.recoverKeys(
-                sessionId, recoveryKeyBlob, applicationKeys, userId);
+                sessionId, recoveryKeyBlob, applicationKeys);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/AndroidKeyStoreFactory.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/AndroidKeyStoreFactory.java
deleted file mode 100644
index 9a4d051..0000000
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/AndroidKeyStoreFactory.java
+++ /dev/null
@@ -1,34 +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 com.android.server.locksettings.recoverablekeystore;
-
-import android.security.keystore.AndroidKeyStoreProvider;
-
-import java.security.KeyStoreException;
-import java.security.NoSuchProviderException;
-
-public interface AndroidKeyStoreFactory {
-    KeyStoreProxy getKeyStoreForUid(int uid) throws KeyStoreException, NoSuchProviderException;
-
-    class Impl implements AndroidKeyStoreFactory {
-        @Override
-        public KeyStoreProxy getKeyStoreForUid(int uid)
-                throws KeyStoreException, NoSuchProviderException {
-            return new KeyStoreProxyImpl(AndroidKeyStoreProvider.getKeyStoreForUid(uid));
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index bc080be..e851d8c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -298,8 +298,8 @@
                 .order(ByteOrder.LITTLE_ENDIAN)
                 .put(SecureBox.encodePublicKey(thmPublicKey))
                 .putLong(counterId)
-                .putInt(maxAttempts)
                 .putLong(deviceId)
+                .putInt(maxAttempts)
                 .array();
     }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 95f5cb7..a8b8361 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -88,7 +88,8 @@
      *
      * @hide
      */
-    public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database, int userId)
+    public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database,
+            int userId)
             throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
         context = context.getApplicationContext();
         PlatformKeyManager keyManager = new PlatformKeyManager(
@@ -115,16 +116,12 @@
     /**
      * Returns the current generation ID of the platform key. This increments whenever a platform
      * key has to be replaced. (e.g., because the user has removed and then re-added their lock
-     * screen).
+     * screen). Returns -1 if no key has been generated yet.
      *
      * @hide
      */
     public int getGenerationId() {
-        int generationId = mDatabase.getPlatformKeyGenerationId(mUserId);
-        if (generationId == -1) {
-            return 1;
-        }
-        return generationId;
+        return mDatabase.getPlatformKeyGenerationId(mUserId);
     }
 
     /**
@@ -149,7 +146,6 @@
     public void regenerate() throws NoSuchAlgorithmException, KeyStoreException {
         int nextId = getGenerationId() + 1;
         generateAndLoadKey(nextId);
-        setGenerationId(nextId);
     }
 
     /**
@@ -207,13 +203,20 @@
                     Locale.US, "Platform key generation %d exists already.", generationId));
             return;
         }
-        if (generationId == 1) {
+        if (generationId == -1) {
             Log.i(TAG, "Generating initial platform ID.");
         } else {
             Log.w(TAG, String.format(Locale.US, "Platform generation ID was %d but no "
                     + "entry was present in AndroidKeyStore. Generating fresh key.", generationId));
         }
 
+        if (generationId == -1) {
+            generationId = 1;
+        } else {
+            // Had to generate a fresh key, bump the generation id
+            generationId++;
+        }
+
         generateAndLoadKey(generationId);
     }
 
@@ -296,6 +299,8 @@
                     .setBoundToSpecificSecureUserId(mUserId)
                     .build());
 
+        setGenerationId(generationId);
+
         try {
             secretKey.destroy();
         } catch (DestroyFailedException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index fe1cad4..c73f852 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -63,9 +63,11 @@
 public class RecoverableKeyStoreManager {
     private static final String TAG = "RecoverableKeyStoreMgr";
 
-    private static final int ERROR_INSECURE_USER = 1;
-    private static final int ERROR_KEYSTORE_INTERNAL_ERROR = 2;
-    private static final int ERROR_DATABASE_ERROR = 3;
+    // TODO: move error codes to RecoverableKeyStoreLoader.
+    private static int ERROR_INSECURE_USER = 1;
+    private static int ERROR_KEYSTORE_INTERNAL_ERROR = 2;
+    private static int ERROR_DATABASE_ERROR = 3;
+    private static int ERROR_RECOVERY_SESSION_NOT_FOUND = 4;
 
     private static RecoverableKeyStoreManager mInstance;
 
@@ -119,9 +121,10 @@
     }
 
     public void initRecoveryService(
-            @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList, int userId)
+            @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
             throws RemoteException {
         checkRecoverKeyStorePermission();
+        int userId = UserHandle.getCallingUserId();
         // TODO: open /system/etc/security/... cert file, and check the signature on the public keys
         PublicKey publicKey;
         try {
@@ -144,22 +147,22 @@
      * @return recovery data
      * @hide
      */
-    public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, int userId)
+    public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
             throws RemoteException {
         checkRecoverKeyStorePermission();
 
         KeyStoreRecoveryData snapshot = mSnapshotStorage.get(UserHandle.getCallingUserId());
         if (snapshot == null) {
-            throw new ServiceSpecificException(RecoverableKeyStoreLoader.NO_SNAPSHOT_PENDING_ERROR);
+            throw new ServiceSpecificException(RecoverableKeyStoreLoader.ERROR_NO_SNAPSHOT_PENDING);
         }
         return snapshot;
     }
 
-    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent, int userId)
+    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
             throws RemoteException {
         checkRecoverKeyStorePermission();
-        final int recoveryAgentUid = Binder.getCallingUid();
-        mListenersStorage.setSnapshotListener(recoveryAgentUid, intent);
+        int uid = Binder.getCallingUid();
+        mListenersStorage.setSnapshotListener(uid, intent);
     }
 
     /**
@@ -168,14 +171,15 @@
      *
      * @return Map from Recovery agent account to snapshot version.
      */
-    public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions(int userId)
+    public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
             throws RemoteException {
         checkRecoverKeyStorePermission();
         throw new UnsupportedOperationException();
     }
 
-    public void setServerParameters(long serverParameters, int userId) throws RemoteException {
+    public void setServerParameters(long serverParameters) throws RemoteException {
         checkRecoverKeyStorePermission();
+        int userId = UserHandle.getCallingUserId();
         mDatabase.setServerParameters(userId, Binder.getCallingUid(), serverParameters);
     }
 
@@ -187,7 +191,7 @@
      * @param status - new status
      */
     public void setRecoveryStatus(
-            @NonNull String packageName, @Nullable String[] aliases, int status, int userId)
+            @NonNull String packageName, @Nullable String[] aliases, int status)
             throws RemoteException {
         checkRecoverKeyStorePermission();
         int uid = Binder.getCallingUid();
@@ -211,12 +215,11 @@
      *
      * @return {@code Map} from KeyStore alias to recovery status.
      */
-    public @NonNull Map<String, Integer> getRecoveryStatus(@Nullable String packageName, int userId)
+    public @NonNull Map<String, Integer> getRecoveryStatus(@Nullable String packageName)
             throws RemoteException {
         // Any application should be able to check status for its own keys.
         // If caller is a recovery agent it can check statuses for other packages, but
         // only for recoverable keys it manages.
-        checkRecoverKeyStorePermission();
         return mDatabase.getStatusForAllKeys(Binder.getCallingUid());
     }
 
@@ -226,7 +229,7 @@
      * @hide
      */
     public void setRecoverySecretTypes(
-            @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes, int userId)
+            @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
             throws RemoteException {
         checkRecoverKeyStorePermission();
         mDatabase.setRecoverySecretTypes(UserHandle.getCallingUserId(), Binder.getCallingUid(),
@@ -239,7 +242,7 @@
      * @return secret types
      * @hide
      */
-    public @NonNull int[] getRecoverySecretTypes(int userId) throws RemoteException {
+    public @NonNull int[] getRecoverySecretTypes() throws RemoteException {
         checkRecoverKeyStorePermission();
         return mDatabase.getRecoverySecretTypes(UserHandle.getCallingUserId(),
             Binder.getCallingUid());
@@ -251,17 +254,17 @@
      * @return secret types
      * @hide
      */
-    public @NonNull int[] getPendingRecoverySecretTypes(int userId) throws RemoteException {
+    public @NonNull int[] getPendingRecoverySecretTypes() throws RemoteException {
         checkRecoverKeyStorePermission();
         throw new UnsupportedOperationException();
     }
 
     public void recoverySecretAvailable(
-            @NonNull KeyStoreRecoveryMetadata recoverySecret, int userId) throws RemoteException {
-        final int callingUid = Binder.getCallingUid(); // Recovery agent uid.
+            @NonNull KeyStoreRecoveryMetadata recoverySecret) throws RemoteException {
+        int uid = Binder.getCallingUid();
         if (recoverySecret.getLockScreenUiFormat() == KeyStoreRecoveryMetadata.TYPE_LOCKSCREEN) {
             throw new SecurityException(
-                    "Caller " + callingUid + "is not allowed to set lock screen secret");
+                    "Caller " + uid + " is not allowed to set lock screen secret");
         }
         checkRecoverKeyStorePermission();
         // TODO: add hook from LockSettingsService to set lock screen secret.
@@ -285,10 +288,10 @@
             @NonNull byte[] verifierPublicKey,
             @NonNull byte[] vaultParams,
             @NonNull byte[] vaultChallenge,
-            @NonNull List<KeyStoreRecoveryMetadata> secrets,
-            int userId)
+            @NonNull List<KeyStoreRecoveryMetadata> secrets)
             throws RemoteException {
         checkRecoverKeyStorePermission();
+        int uid = Binder.getCallingUid();
 
         if (secrets.size() != 1) {
             // TODO: support multiple secrets
@@ -298,7 +301,7 @@
         byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
         byte[] kfHash = secrets.get(0).getSecret();
         mRecoverySessionStorage.add(
-                userId,
+                uid,
                 new RecoverySessionStorage.Entry(sessionId, kfHash, keyClaimant, vaultParams));
 
         try {
@@ -336,22 +339,21 @@
      *     service.
      * @param applicationKeys The encrypted key blobs returned by the remote vault service. These
      *     were wrapped with the recovery key.
-     * @param uid The uid of the recovery agent.
      * @return Map from alias to raw key material.
      * @throws RemoteException if an error occurred recovering the keys.
      */
     public Map<String, byte[]> recoverKeys(
             @NonNull String sessionId,
             @NonNull byte[] encryptedRecoveryKey,
-            @NonNull List<KeyEntryRecoveryData> applicationKeys,
-            int uid)
+            @NonNull List<KeyEntryRecoveryData> applicationKeys)
             throws RemoteException {
         checkRecoverKeyStorePermission();
-
+        int uid = Binder.getCallingUid();
         RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
         if (sessionEntry == null) {
-            throw new RemoteException(String.format(Locale.US,
-                    "User %d does not have pending session '%s'", uid, sessionId));
+            throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR,
+                    String.format(Locale.US,
+                    "Application uid=%d does not have pending session '%s'", uid, sessionId));
         }
 
         try {
@@ -373,7 +375,7 @@
      */
     public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException {
         int uid = Binder.getCallingUid();
-        int userId = Binder.getCallingUserHandle().getIdentifier();
+        int userId = UserHandle.getCallingUserId();
 
         PlatformEncryptionKey encryptionKey;
 
@@ -401,7 +403,7 @@
 
     private byte[] decryptRecoveryKey(
             RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse)
-            throws RemoteException {
+            throws RemoteException, ServiceSpecificException {
         try {
             byte[] locallyEncryptedKey = KeySyncUtils.decryptRecoveryClaimResponse(
                     sessionEntry.getKeyClaimant(),
@@ -409,18 +411,12 @@
                     encryptedClaimResponse);
             return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey);
         } catch (InvalidKeyException | AEADBadTagException e) {
-            throw new RemoteException(
-                    "Failed to decrypt recovery key",
-                    e,
-                    /*enableSuppression=*/ true,
-                    /*writeableStackTrace=*/ true);
+            throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR,
+                    "Failed to decrypt recovery key " + e.getMessage());
+
         } catch (NoSuchAlgorithmException e) {
             // Should never happen: all the algorithms used are required by AOSP implementations
-            throw new RemoteException(
-                    "Missing required algorithm",
-                    e,
-                    /*enableSuppression=*/ true,
-                    /*writeableStackTrace=*/ true);
+            throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index 838311e..5ca5da4 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -590,6 +590,7 @@
      *
      * @hide
      */
+    @Nullable
     public Long getServerParameters(int userId, int uid) {
         SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index fdfe241..3c47e85 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2799,6 +2799,17 @@
     }
 
     @Override
+    public String getSubscriptionPlansOwner(int subId) {
+        if (UserHandle.getCallingAppId() != android.os.Process.SYSTEM_UID) {
+            throw new SecurityException();
+        }
+
+        synchronized (mNetworkPoliciesSecondLock) {
+            return mSubscriptionPlansOwner.get(subId);
+        }
+    }
+
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
 
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 171703a..f35e6ec 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -33,6 +33,7 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.net.INetworkWatchlistManager;
@@ -92,6 +93,7 @@
         }
     }
 
+    @GuardedBy("mLoggingSwitchLock")
     private volatile boolean mIsLoggingEnabled = false;
     private final Object mLoggingSwitchLock = new Object();
 
@@ -220,36 +222,11 @@
         }
     }
 
-    /**
-     * Set a new network watchlist.
-     * This method should be called by ConfigUpdater only.
-     *
-     * @return True if network watchlist is updated.
-     */
-    public boolean setNetworkSecurityWatchlist(List<byte[]> domainsCrc32Digests,
-            List<byte[]> domainsSha256Digests,
-            List<byte[]> ipAddressesCrc32Digests,
-            List<byte[]> ipAddressesSha256Digests) {
-        Slog.i(TAG, "Setting network watchlist");
-        if (domainsCrc32Digests == null || domainsSha256Digests == null
-                || ipAddressesCrc32Digests == null || ipAddressesSha256Digests == null) {
-            Slog.e(TAG, "Parameters cannot be null");
-            return false;
-        }
-        if (domainsCrc32Digests.size() != domainsSha256Digests.size()
-                || ipAddressesCrc32Digests.size() != ipAddressesSha256Digests.size()) {
-            Slog.e(TAG, "Must need to have the same number of CRC32 and SHA256 digests");
-            return false;
-        }
-        if (domainsSha256Digests.size() + ipAddressesSha256Digests.size()
-                > MAX_NUM_OF_WATCHLIST_DIGESTS) {
-            Slog.e(TAG, "Total watchlist size cannot exceed " + MAX_NUM_OF_WATCHLIST_DIGESTS);
-            return false;
-        }
-        mSettings.writeSettingsToDisk(domainsCrc32Digests, domainsSha256Digests,
-                ipAddressesCrc32Digests, ipAddressesSha256Digests);
-        Slog.i(TAG, "Set network watchlist: Success");
-        return true;
+    @Override
+    public void reloadWatchlist() throws RemoteException {
+        enforceWatchlistLoggingPermission();
+        Slog.i(TAG, "Reloading watchlist");
+        mSettings.reloadSettings();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
index f48463f..838aa53 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -21,10 +21,12 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Environment;
 import android.util.Pair;
 
 import com.android.internal.util.HexDump;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
@@ -83,9 +85,12 @@
         HashMap<String, String> appDigestCNCList;
     }
 
+    static File getSystemWatchlistDbFile() {
+        return new File(Environment.getDataSystemDirectory(), NAME);
+    }
+
     private WatchlistReportDbHelper(Context context) {
-        super(context, WatchlistSettings.getSystemWatchlistFile(NAME).getAbsolutePath(),
-                null, VERSION);
+        super(context, getSystemWatchlistDbFile().getAbsolutePath(), null, VERSION);
         // Memory optimization - close idle connections after 30s of inactivity
         setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
     }
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
index c50f0d5..70002ea 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -19,8 +19,10 @@
 import android.os.Environment;
 import android.util.AtomicFile;
 import android.util.Log;
+import android.util.Slog;
 import android.util.Xml;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.HexDump;
@@ -51,10 +53,9 @@
 class WatchlistSettings {
     private static final String TAG = "WatchlistSettings";
 
-    // Settings xml will be stored in /data/system/network_watchlist/watchlist_settings.xml
-    static final String SYSTEM_WATCHLIST_DIR = "network_watchlist";
-
-    private static final String WATCHLIST_XML_FILE = "watchlist_settings.xml";
+    // Watchlist config that pushed by ConfigUpdater.
+    private static final String NETWORK_WATCHLIST_DB_PATH =
+            "/data/misc/network_watchlist/network_watchlist.xml";
 
     private static class XmlTags {
         private static final String WATCHLIST_SETTINGS = "watchlist-settings";
@@ -65,86 +66,74 @@
         private static final String HASH = "hash";
     }
 
-    private static WatchlistSettings sInstance = new WatchlistSettings();
-    private final AtomicFile mXmlFile;
-    private final Object mLock = new Object();
-    private HarmfulDigests mCrc32DomainDigests = new HarmfulDigests(new ArrayList<>());
-    private HarmfulDigests mSha256DomainDigests = new HarmfulDigests(new ArrayList<>());
-    private HarmfulDigests mCrc32IpDigests = new HarmfulDigests(new ArrayList<>());
-    private HarmfulDigests mSha256IpDigests = new HarmfulDigests(new ArrayList<>());
+    private static class CrcShaDigests {
+        final HarmfulDigests crc32Digests;
+        final HarmfulDigests sha256Digests;
 
-    public static synchronized WatchlistSettings getInstance() {
+        public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) {
+            this.crc32Digests = crc32Digests;
+            this.sha256Digests = sha256Digests;
+        }
+    }
+
+    private final static WatchlistSettings sInstance = new WatchlistSettings();
+    private final AtomicFile mXmlFile;
+
+    private volatile CrcShaDigests mDomainDigests;
+    private volatile CrcShaDigests mIpDigests;
+
+    public static WatchlistSettings getInstance() {
         return sInstance;
     }
 
     private WatchlistSettings() {
-        this(getSystemWatchlistFile(WATCHLIST_XML_FILE));
+        this(new File(NETWORK_WATCHLIST_DB_PATH));
     }
 
     @VisibleForTesting
     protected WatchlistSettings(File xmlFile) {
         mXmlFile = new AtomicFile(xmlFile);
-        readSettingsLocked();
+        reloadSettings();
     }
 
-    static File getSystemWatchlistFile(String filename) {
-        final File dataSystemDir = Environment.getDataSystemDirectory();
-        final File systemWatchlistDir = new File(dataSystemDir, SYSTEM_WATCHLIST_DIR);
-        systemWatchlistDir.mkdirs();
-        return new File(systemWatchlistDir, filename);
-    }
-
-    private void readSettingsLocked() {
-        synchronized (mLock) {
-            FileInputStream stream;
-            try {
-                stream = mXmlFile.openRead();
-            } catch (FileNotFoundException e) {
-                Log.i(TAG, "No watchlist settings: " + mXmlFile.getBaseFile().getAbsolutePath());
-                return;
-            }
+    public void reloadSettings() {
+        try (FileInputStream stream = mXmlFile.openRead()){
 
             final List<byte[]> crc32DomainList = new ArrayList<>();
             final List<byte[]> sha256DomainList = new ArrayList<>();
             final List<byte[]> crc32IpList = new ArrayList<>();
             final List<byte[]> sha256IpList = new ArrayList<>();
 
-            try {
-                XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(stream, StandardCharsets.UTF_8.name());
-                parser.nextTag();
-                parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS);
-                while (parser.nextTag() == XmlPullParser.START_TAG) {
-                    String tagName = parser.getName();
-                    switch (tagName) {
-                        case XmlTags.CRC32_DOMAIN:
-                            parseHash(parser, tagName, crc32DomainList);
-                            break;
-                        case XmlTags.CRC32_IP:
-                            parseHash(parser, tagName, crc32IpList);
-                            break;
-                        case XmlTags.SHA256_DOMAIN:
-                            parseHash(parser, tagName, sha256DomainList);
-                            break;
-                        case XmlTags.SHA256_IP:
-                            parseHash(parser, tagName, sha256IpList);
-                            break;
-                        default:
-                            Log.w(TAG, "Unknown element: " + parser.getName());
-                            XmlUtils.skipCurrentTag(parser);
-                    }
-                }
-                parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS);
-                writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList);
-            } catch (IllegalStateException | NullPointerException | NumberFormatException |
-                    XmlPullParserException | IOException | IndexOutOfBoundsException e) {
-                Log.w(TAG, "Failed parsing " + e);
-            } finally {
-                try {
-                    stream.close();
-                } catch (IOException e) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+            parser.nextTag();
+            parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+            while (parser.nextTag() == XmlPullParser.START_TAG) {
+                String tagName = parser.getName();
+                switch (tagName) {
+                    case XmlTags.CRC32_DOMAIN:
+                        parseHash(parser, tagName, crc32DomainList);
+                        break;
+                    case XmlTags.CRC32_IP:
+                        parseHash(parser, tagName, crc32IpList);
+                        break;
+                    case XmlTags.SHA256_DOMAIN:
+                        parseHash(parser, tagName, sha256DomainList);
+                        break;
+                    case XmlTags.SHA256_IP:
+                        parseHash(parser, tagName, sha256IpList);
+                        break;
+                    default:
+                        Log.w(TAG, "Unknown element: " + parser.getName());
+                        XmlUtils.skipCurrentTag(parser);
                 }
             }
+            parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+            writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList);
+            Log.i(TAG, "Reload watchlist done");
+        } catch (IllegalStateException | NullPointerException | NumberFormatException |
+                XmlPullParserException | IOException | IndexOutOfBoundsException e) {
+            Slog.e(TAG, "Failed parsing xml", e);
         }
     }
 
@@ -161,101 +150,61 @@
     }
 
     /**
-     * Write network watchlist settings to disk.
-     * Adb should not use it, should use writeSettingsToMemory directly instead.
-     */
-    public void writeSettingsToDisk(List<byte[]> newCrc32DomainList,
-            List<byte[]> newSha256DomainList,
-            List<byte[]> newCrc32IpList,
-            List<byte[]> newSha256IpList) {
-        synchronized (mLock) {
-            FileOutputStream stream;
-            try {
-                stream = mXmlFile.startWrite();
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to write display settings: " + e);
-                return;
-            }
-
-            try {
-                XmlSerializer out = new FastXmlSerializer();
-                out.setOutput(stream, StandardCharsets.UTF_8.name());
-                out.startDocument(null, true);
-                out.startTag(null, XmlTags.WATCHLIST_SETTINGS);
-
-                writeHashSetToXml(out, XmlTags.SHA256_DOMAIN, newSha256DomainList);
-                writeHashSetToXml(out, XmlTags.SHA256_IP, newSha256IpList);
-                writeHashSetToXml(out, XmlTags.CRC32_DOMAIN, newCrc32DomainList);
-                writeHashSetToXml(out, XmlTags.CRC32_IP, newCrc32IpList);
-
-                out.endTag(null, XmlTags.WATCHLIST_SETTINGS);
-                out.endDocument();
-                mXmlFile.finishWrite(stream);
-                writeSettingsToMemory(newCrc32DomainList, newSha256DomainList, newCrc32IpList,
-                        newSha256IpList);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to write display settings, restoring backup.", e);
-                mXmlFile.failWrite(stream);
-            }
-        }
-    }
-
-    /**
      * Write network watchlist settings to memory.
      */
     public void writeSettingsToMemory(List<byte[]> newCrc32DomainList,
             List<byte[]> newSha256DomainList,
             List<byte[]> newCrc32IpList,
             List<byte[]> newSha256IpList) {
-        synchronized (mLock) {
-            mCrc32DomainDigests = new HarmfulDigests(newCrc32DomainList);
-            mCrc32IpDigests = new HarmfulDigests(newCrc32IpList);
-            mSha256DomainDigests = new HarmfulDigests(newSha256DomainList);
-            mSha256IpDigests = new HarmfulDigests(newSha256IpList);
-        }
-    }
-
-    private static void writeHashSetToXml(XmlSerializer out, String tagName, List<byte[]> hashSet)
-            throws IOException {
-        out.startTag(null, tagName);
-        for (byte[] hash : hashSet) {
-            out.startTag(null, XmlTags.HASH);
-            out.text(HexDump.toHexString(hash));
-            out.endTag(null, XmlTags.HASH);
-        }
-        out.endTag(null, tagName);
+        mDomainDigests = new CrcShaDigests(new HarmfulDigests(newCrc32DomainList),
+                new HarmfulDigests(newSha256DomainList));
+        mIpDigests = new CrcShaDigests(new HarmfulDigests(newCrc32IpList),
+                new HarmfulDigests(newSha256IpList));
     }
 
     public boolean containsDomain(String domain) {
+        final CrcShaDigests domainDigests = mDomainDigests;
+        if (domainDigests == null) {
+            Slog.wtf(TAG, "domainDigests should not be null");
+            return false;
+        }
         // First it does a quick CRC32 check.
         final byte[] crc32 = getCrc32(domain);
-        if (!mCrc32DomainDigests.contains(crc32)) {
+        if (!domainDigests.crc32Digests.contains(crc32)) {
             return false;
         }
         // Now we do a slow SHA256 check.
         final byte[] sha256 = getSha256(domain);
-        return mSha256DomainDigests.contains(sha256);
+        return domainDigests.sha256Digests.contains(sha256);
     }
 
     public boolean containsIp(String ip) {
+        final CrcShaDigests ipDigests = mIpDigests;
+        if (ipDigests == null) {
+            Slog.wtf(TAG, "ipDigests should not be null");
+            return false;
+        }
         // First it does a quick CRC32 check.
         final byte[] crc32 = getCrc32(ip);
-        if (!mCrc32IpDigests.contains(crc32)) {
+        if (!ipDigests.crc32Digests.contains(crc32)) {
             return false;
         }
         // Now we do a slow SHA256 check.
         final byte[] sha256 = getSha256(ip);
-        return mSha256IpDigests.contains(sha256);
+        return ipDigests.sha256Digests.contains(sha256);
     }
 
 
-    /** Get CRC32 of a string */
+    /** Get CRC32 of a string
+     *
+     * TODO: Review if we should use CRC32 or other algorithms
+     */
     private byte[] getCrc32(String str) {
         final CRC32 crc = new CRC32();
         crc.update(str.getBytes());
         final long tmp = crc.getValue();
-        return new byte[]{(byte)(tmp >> 24 & 255), (byte)(tmp >> 16 & 255),
-                (byte)(tmp >> 8 & 255), (byte)(tmp & 255)};
+        return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255),
+                (byte) (tmp >> 8 & 255), (byte) (tmp & 255)};
     }
 
     /** Get SHA256 of a string */
@@ -273,12 +222,12 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Domain CRC32 digest list:");
-        mCrc32DomainDigests.dump(fd, pw, args);
+        mDomainDigests.crc32Digests.dump(fd, pw, args);
         pw.println("Domain SHA256 digest list:");
-        mSha256DomainDigests.dump(fd, pw, args);
+        mDomainDigests.sha256Digests.dump(fd, pw, args);
         pw.println("Ip CRC32 digest list:");
-        mCrc32IpDigests.dump(fd, pw, args);
+        mIpDigests.crc32Digests.dump(fd, pw, args);
         pw.println("Ip SHA256 digest list:");
-        mSha256IpDigests.dump(fd, pw, args);
+        mIpDigests.sha256Digests.dump(fd, pw, args);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 14128a7..16fae99 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -415,7 +415,12 @@
             params.installFlags |= PackageManager.INSTALL_FROM_ADB;
 
         } else {
-            mAppOps.checkPackage(callingUid, installerPackageName);
+            // Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
+            // caller.
+            if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) !=
+                    PackageManager.PERMISSION_GRANTED) {
+                mAppOps.checkPackage(callingUid, installerPackageName);
+            }
 
             params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
             params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5cf08dc..5577de8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -346,13 +346,14 @@
                 || (isSelfUpdatePermissionGranted
                     && mPm.getPackageUid(mPackageName, 0, userId) == mInstallerUid);
         final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
+        final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
         final boolean forcePermissionPrompt =
                 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
 
         // Device owners and affiliated profile owners  are allowed to silently install packages, so
         // the permission check is waived if the installer is the device owner.
         return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
-                || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
+                || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
     }
 
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1157af4..44aad44 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13141,80 +13141,6 @@
         }
     }
 
-    @Override
-    public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
-            int installFlags, String installerPackageName, int userId) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
-
-        final int callingUid = Binder.getCallingUid();
-        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");
-
-        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
-            try {
-                if (observer != null) {
-                    observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
-                }
-            } catch (RemoteException re) {
-            }
-            return;
-        }
-
-        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
-            installFlags |= PackageManager.INSTALL_FROM_ADB;
-
-        } else {
-            // Caller holds INSTALL_PACKAGES permission, so we're less strict
-            // about installerPackageName.
-
-            installFlags &= ~PackageManager.INSTALL_FROM_ADB;
-            installFlags &= ~PackageManager.INSTALL_ALL_USERS;
-        }
-
-        UserHandle user;
-        if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
-            user = UserHandle.ALL;
-        } else {
-            user = new UserHandle(userId);
-        }
-
-        // Only system components can circumvent runtime permissions when installing.
-        if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
-                && mContext.checkCallingOrSelfPermission(Manifest.permission
-                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
-            throw new SecurityException("You need the "
-                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
-                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
-        }
-
-        if ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
-                || (installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            throw new IllegalArgumentException(
-                    "New installs into ASEC containers no longer supported");
-        }
-
-        final File originFile = new File(originPath);
-        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
-
-        final Message msg = mHandler.obtainMessage(INIT_COPY);
-        final VerificationInfo verificationInfo = new VerificationInfo(
-                null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
-        final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
-                installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
-                null /*packageAbiOverride*/, null /*grantedPermissions*/,
-                null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
-        params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
-        msg.obj = params;
-
-        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
-                System.identityHashCode(msg.obj));
-        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
-                System.identityHashCode(msg.obj));
-
-        mHandler.sendMessage(msg);
-    }
-
-
     /**
      * Ensure that the install reason matches what we know about the package installer (e.g. whether
      * it is acting on behalf on an enterprise or the user).
@@ -23049,6 +22975,11 @@
         }
 
         @Override
+        public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
+            mDefaultPermissionPolicy.setUseOpenWifiAppPackagesProvider(provider);
+        }
+
+        @Override
         public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) {
             mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider);
         }
@@ -23073,6 +23004,12 @@
         }
 
         @Override
+        public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
+            mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp(
+                    packageName, userId);
+        }
+
+        @Override
         public void setKeepUninstalledPackages(final List<String> packageList) {
             Preconditions.checkNotNull(packageList);
             List<String> removedFromList = null;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 648f847..4cf1814 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3122,7 +3122,7 @@
                             ATTR_VOLUME_UUID);
                     final VersionInfo ver = findOrCreateVersion(volumeUuid);
                     ver.sdkVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);
-                    ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);
+                    ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_DATABASE_VERSION);
                     ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
                 } else {
                     Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 768eb8f..c3dce31 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -27,7 +27,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
-import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
@@ -795,12 +794,7 @@
                     "target should only be specified when we are disabling quiet mode.");
         }
 
-        if (!isAllowedToSetWorkMode(callingPackage, Binder.getCallingUid())) {
-            throw new SecurityException("Not allowed to call trySetQuietModeEnabled, "
-                    + "caller is foreground default launcher "
-                    + "nor with MANAGE_USERS/MODIFY_QUIET_MODE permission");
-        }
-
+        ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), target != null);
         final long identity = Binder.clearCallingIdentity();
         try {
             if (enableQuietMode) {
@@ -824,35 +818,44 @@
     }
 
     /**
-     * An app can modify quiet mode if the caller meets one of the condition:
+     * The caller can modify quiet mode if it meets one of these conditions:
      * <ul>
      *     <li>Has system UID or root UID</li>
      *     <li>Has {@link Manifest.permission#MODIFY_QUIET_MODE}</li>
      *     <li>Has {@link Manifest.permission#MANAGE_USERS}</li>
      * </ul>
+     * <p>
+     * If caller wants to start an intent after disabling the quiet mode, it must has
+     * {@link Manifest.permission#MANAGE_USERS}.
      */
-    private boolean isAllowedToSetWorkMode(String callingPackage, int callingUid) {
+    private void ensureCanModifyQuietMode(String callingPackage, int callingUid,
+            boolean startIntent) {
         if (hasManageUsersPermission()) {
-            return true;
+            return;
         }
-
+        if (startIntent) {
+            throw new SecurityException("MANAGE_USERS permission is required to start intent "
+                    + "after disabling quiet mode.");
+        }
         final boolean hasModifyQuietModePermission = ActivityManager.checkComponentPermission(
                 Manifest.permission.MODIFY_QUIET_MODE,
                 callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
         if (hasModifyQuietModePermission) {
-            return true;
+            return;
         }
 
+        verifyCallingPackage(callingPackage, callingUid);
         final ShortcutServiceInternal shortcutInternal =
                 LocalServices.getService(ShortcutServiceInternal.class);
         if (shortcutInternal != null) {
             boolean isForegroundLauncher =
                     shortcutInternal.isForegroundDefaultLauncher(callingPackage, callingUid);
             if (isForegroundLauncher) {
-                return true;
+                return;
             }
         }
-        return false;
+        throw new SecurityException("Can't modify quiet mode, caller is neither foreground "
+                + "default launcher nor has MANAGE_USERS/MODIFY_QUIET_MODE permission");
     }
 
     private void setQuietModeEnabled(
@@ -3932,4 +3935,16 @@
             return false;
         }
     }
+
+    /**
+     * Check if the calling package name matches with the calling UID, throw
+     * {@link SecurityException} if not.
+     */
+    private void verifyCallingPackage(String callingPackage, int callingUid) {
+        int packageUid = mPm.getPackageUid(callingPackage, 0,  UserHandle.getUserId(callingUid));
+        if (packageUid != callingUid) {
+            throw new SecurityException("Specified package " + callingPackage
+                    + " does not match the calling uid " + callingUid);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 9240843..bfba700 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -460,7 +460,8 @@
                         // DISALLOW_DATA_ROAMING user restriction is set.
 
                         // Multi sim device.
-                        SubscriptionManager subscriptionManager = new SubscriptionManager(context);
+                        SubscriptionManager subscriptionManager = context
+                                .getSystemService(SubscriptionManager.class);
                         final List<SubscriptionInfo> subscriptionInfoList =
                             subscriptionManager.getActiveSubscriptionInfoList();
                         if (subscriptionInfoList != null) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index d38dc9a..34c3ce3 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -135,6 +135,11 @@
         LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
     }
 
+    private static final Set<String> COARSE_LOCATION_PERMISSIONS = new ArraySet<>();
+    static {
+        COARSE_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+    }
+
     private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>();
     static {
         CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR);
@@ -183,6 +188,7 @@
     private PackagesProvider mSmsAppPackagesProvider;
     private PackagesProvider mDialerAppPackagesProvider;
     private PackagesProvider mSimCallManagerPackagesProvider;
+    private PackagesProvider mUseOpenWifiAppPackagesProvider;
     private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
 
     private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
@@ -247,6 +253,12 @@
         }
     }
 
+    public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mUseOpenWifiAppPackagesProvider = provider;
+        }
+    }
+
     public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
         synchronized (mLock) {
             mSyncAdapterPackagesProvider = provider;
@@ -320,6 +332,7 @@
         final PackagesProvider smsAppPackagesProvider;
         final PackagesProvider dialerAppPackagesProvider;
         final PackagesProvider simCallManagerPackagesProvider;
+        final PackagesProvider useOpenWifiAppPackagesProvider;
         final SyncAdapterPackagesProvider syncAdapterPackagesProvider;
 
         synchronized (mLock) {
@@ -328,6 +341,7 @@
             smsAppPackagesProvider = mSmsAppPackagesProvider;
             dialerAppPackagesProvider = mDialerAppPackagesProvider;
             simCallManagerPackagesProvider = mSimCallManagerPackagesProvider;
+            useOpenWifiAppPackagesProvider = mUseOpenWifiAppPackagesProvider;
             syncAdapterPackagesProvider = mSyncAdapterPackagesProvider;
         }
 
@@ -341,6 +355,8 @@
                 ? dialerAppPackagesProvider.getPackages(userId) : null;
         String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null)
                 ? simCallManagerPackagesProvider.getPackages(userId) : null;
+        String[] useOpenWifiAppPackageNames = (useOpenWifiAppPackagesProvider != null)
+                ? useOpenWifiAppPackagesProvider.getPackages(userId) : null;
         String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
                 syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null;
         String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
@@ -458,6 +474,18 @@
             }
         }
 
+        // Use Open Wifi
+        if (useOpenWifiAppPackageNames != null) {
+            for (String useOpenWifiPackageName : useOpenWifiAppPackageNames) {
+                PackageParser.Package useOpenWifiPackage =
+                        getSystemPackage(useOpenWifiPackageName);
+                if (useOpenWifiPackage != null) {
+                    grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(useOpenWifiPackage,
+                            userId);
+                }
+            }
+        }
+
         // SMS
         if (smsAppPackageNames == null) {
             Intent smsIntent = new Intent(Intent.ACTION_MAIN);
@@ -827,6 +855,13 @@
         }
     }
 
+    private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
+            PackageParser.Package useOpenWifiPackage, int userId) {
+        if (doesPackageSupportRuntimePermissions(useOpenWifiPackage)) {
+            grantRuntimePermissions(useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, userId);
+        }
+    }
+
     public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
         if (packageName == null) {
@@ -859,6 +894,19 @@
         }
     }
 
+    public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
+        Log.i(TAG, "Granting permissions to default Use Open WiFi app for user:" + userId);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package useOpenWifiPackage = getPackage(packageName);
+        if (useOpenWifiPackage != null
+                && doesPackageSupportRuntimePermissions(useOpenWifiPackage)) {
+            grantRuntimePermissions(
+                    useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, false, true, userId);
+        }
+    }
+
     private void grantDefaultPermissionsToDefaultSimCallManager(
             PackageParser.Package simCallManagerPackage, int userId) {
         Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
@@ -1014,7 +1062,7 @@
     }
 
     private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
-            boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
+            boolean systemFixed, boolean ignoreSystemPackage, int userId) {
         if (pkg.requestedPermissions.isEmpty()) {
             return;
         }
@@ -1022,13 +1070,13 @@
         List<String> requestedPermissions = pkg.requestedPermissions;
         Set<String> grantablePermissions = null;
 
-        // If this is the default Phone or SMS app we grant permissions regardless
-        // whether the version on the system image declares the permission as used since
-        // selecting the app as the default Phone or SMS the user makes a deliberate
+        // In some cases, like for the Phone or SMS app, we grant permissions regardless
+        // of if the version on the system image declares the permission as used since
+        // selecting the app as the default for that function the user makes a deliberate
         // choice to grant this app the permissions needed to function. For all other
         // apps, (default grants on first boot and user creation) we don't grant default
         // permissions if the version on the system image does not declare them.
-        if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {
+        if (!ignoreSystemPackage && pkg.isUpdatedSystemApp()) {
             final PackageParser.Package disabledPkg =
                     mServiceInternal.getDisabledPackage(pkg.packageName);
             if (disabledPkg != null) {
@@ -1062,7 +1110,7 @@
                 // Unless the caller wants to override user choices. The override is
                 // to make sure we can grant the needed permission to the default
                 // sms and phone apps after the user chooses this in the UI.
-                if (flags == 0 || isDefaultPhoneOrSms) {
+                if (flags == 0 || ignoreSystemPackage) {
                     // Never clobber policy or system.
                     final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                             | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 02c8f68..86b22bb 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -57,6 +57,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
+import android.os.WorkSource.WorkChain;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
@@ -1976,6 +1977,16 @@
                     return true;
                 }
             }
+
+            final ArrayList<WorkChain> workChains = wakeLock.mWorkSource.getWorkChains();
+            if (workChains != null) {
+                for (int k = 0; k < workChains.size(); k++) {
+                    final int uid = workChains.get(k).getAttributionUid();
+                    if (userId == UserHandle.getUserId(uid)) {
+                        return true;
+                    }
+                }
+            }
         }
         return userId == UserHandle.getUserId(wakeLock.mOwnerUid);
     }
@@ -2441,6 +2452,7 @@
             float screenAutoBrightnessAdjustment = 0.0f;
             boolean autoBrightness = (mScreenBrightnessModeSetting ==
                     Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+            boolean brightnessIsTemporary = false;
             if (!mBootCompleted) {
                 // Keep the brightness steady during boot. This requires the
                 // bootloader brightness and the default brightness to be identical.
@@ -2455,6 +2467,7 @@
                 brightnessSetByUser = false;
             } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
                 screenBrightness = mTemporaryScreenBrightnessSettingOverride;
+                brightnessIsTemporary = true;
             } else if (isValidBrightness(mScreenBrightnessSetting)) {
                 screenBrightness = mScreenBrightnessSetting;
             }
@@ -2464,6 +2477,7 @@
                         mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
                     screenAutoBrightnessAdjustment =
                             mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
+                    brightnessIsTemporary = true;
                 } else if (isValidAutoBrightnessAdjustment(
                         mScreenAutoBrightnessAdjustmentSetting)) {
                     screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
@@ -2479,6 +2493,7 @@
             mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                     screenAutoBrightnessAdjustment;
             mDisplayPowerRequest.brightnessSetByUser = brightnessSetByUser;
+            mDisplayPowerRequest.brightnessIsTemporary = brightnessIsTemporary;
             mDisplayPowerRequest.useAutoBrightness = autoBrightness;
             mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
             mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
diff --git a/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java b/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java
new file mode 100644
index 0000000..3b7ddc2
--- /dev/null
+++ b/services/core/java/com/android/server/updates/NetworkWatchlistInstallReceiver.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.android.server.updates;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkWatchlistManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+public class NetworkWatchlistInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    public NetworkWatchlistInstallReceiver() {
+        super("/data/misc/network_watchlist/", "network_watchlist.xml", "metadata/", "version");
+    }
+
+    @Override
+    protected void postInstall(Context context, Intent intent) {
+        try {
+            context.getSystemService(NetworkWatchlistManager.class).reloadWatchlist();
+        } catch (Exception e) {
+            // Network Watchlist is not available
+            Slog.wtf("NetworkWatchlistInstallReceiver", "Unable to reload watchlist");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8e916ad..844aafb 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -956,7 +956,7 @@
                 }
                 if (mInfo != null && mInfo.getSupportsAmbientMode()) {
                     try {
-                        mEngine.setInAmbientMode(mInAmbientMode);
+                        mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
@@ -1751,7 +1751,7 @@
         }
     }
 
-    public void setInAmbientMode(boolean inAmbienMode) {
+    public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
         final IWallpaperEngine engine;
         synchronized (mLock) {
             mInAmbientMode = inAmbienMode;
@@ -1766,7 +1766,7 @@
 
         if (engine != null) {
             try {
-                engine.setInAmbientMode(inAmbienMode);
+                engine.setInAmbientMode(inAmbienMode, animated);
             } catch (RemoteException e) {
                 // Cannot talk to wallpaper engine.
             }
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index b86cd50..c16a531 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -139,7 +139,7 @@
 
     @Override
     public Builder makeAnimationLeash() {
-        return mAppToken.makeSurface().setParent(mAppToken.getAppAnimationLayer());
+        return mAppToken.makeSurface();
     }
 
     @Override
@@ -148,6 +148,11 @@
     }
 
     @Override
+    public SurfaceControl getAnimationLeashParent() {
+        return mAppToken.getAppAnimationLayer();
+    }
+
+    @Override
     public SurfaceControl getParentSurfaceControl() {
         return mAppToken.getParentSurfaceControl();
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 44d7948..dcd88dd 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -53,6 +56,7 @@
 
 import android.annotation.CallSuper;
 import android.app.Activity;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.res.Configuration;
 import android.graphics.GraphicBuffer;
 import android.graphics.Point;
@@ -1211,6 +1215,30 @@
     }
 
     @Override
+    public void onConfigurationChanged(Configuration newParentConfig) {
+        final int prevWinMode = getWindowingMode();
+        super.onConfigurationChanged(newParentConfig);
+        final int winMode = getWindowingMode();
+
+        if (prevWinMode == winMode) {
+            return;
+        }
+
+        if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) {
+            // Entering PiP from fullscreen, reset the snap fraction
+            mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
+        } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED) {
+            // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
+            // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
+            final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+            if (pinnedStack != null) {
+                mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(this,
+                        pinnedStack.mPreAnimationBounds);
+            }
+        }
+    }
+
+    @Override
     void checkAppWindowsReadyToShow() {
         if (allDrawn == mLastAllDrawn) {
             return;
@@ -1512,9 +1540,8 @@
     }
 
     @Override
-    public SurfaceControl.Builder makeAnimationLeash() {
-        return super.makeAnimationLeash()
-                .setParent(getAppAnimationLayer());
+    public SurfaceControl getAnimationLeashParent() {
+        return getAppAnimationLayer();
     }
 
     boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
@@ -1541,6 +1568,7 @@
                 if (stack != null) {
                     stack.getRelativePosition(mTmpPoint);
                     stack.getBounds(mTmpRect);
+                    mTmpRect.offsetTo(0, 0);
                 }
                 final AnimationAdapter adapter = new LocalAnimationAdapter(
                         new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
@@ -1837,6 +1865,11 @@
     @Override
     void setHidden(boolean hidden) {
         super.setHidden(hidden);
+
+        if (hidden) {
+            // Once the app window is hidden, reset the last saved PiP snap fraction
+            mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
+        }
         scheduleAnimation();
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d053015..63dfbc2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1923,6 +1923,8 @@
                     mService.unregisterPointerEventListener(mService.mMousePositionTracker);
                 }
             }
+            mService.mAnimator.removeDisplayLocked(mDisplayId);
+
             // The pending transaction won't be applied so we should
             // just clean up any surfaces pending destruction.
             onPendingTransactionApplied();
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index d8726bf..69cbe46 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -48,6 +48,7 @@
 import com.android.server.UiThread;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -71,6 +72,7 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM;
 
+    public static final float INVALID_SNAP_FRACTION = -1f;
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private final Handler mHandler = UiThread.getHandler();
@@ -101,6 +103,8 @@
     private float mDefaultAspectRatio;
     private Point mScreenEdgeInsets;
     private int mCurrentMinSize;
+    private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
+    private WeakReference<AppWindowToken> mLastPipActivity = null;
 
     // The aspect ratio bounds of the PIP.
     private float mMinAspectRatio;
@@ -113,6 +117,7 @@
     private final Rect mTmpAnimatingBoundsRect = new Rect();
     private final Point mTmpDisplaySize = new Point();
 
+
     /**
      * The callback object passed to listeners for them to notify the controller of state changes.
      */
@@ -250,9 +255,35 @@
     }
 
     /**
+     * Saves the current snap fraction for re-entry of the current activity into PiP.
+     */
+    void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) {
+        mReentrySnapFraction = getSnapFraction(stackBounds);
+        mLastPipActivity = new WeakReference<>(token);
+    }
+
+    /**
+     * Resets the last saved snap fraction so that the default bounds will be returned.
+     */
+    void resetReentrySnapFraction(AppWindowToken token) {
+        if (mLastPipActivity != null && mLastPipActivity.get() == token) {
+            mReentrySnapFraction = INVALID_SNAP_FRACTION;
+            mLastPipActivity = null;
+        }
+    }
+
+    /**
      * @return the default bounds to show the PIP when there is no active PIP.
      */
-    Rect getDefaultBounds() {
+    Rect getDefaultOrLastSavedBounds() {
+        return getDefaultBounds(mReentrySnapFraction);
+    }
+
+    /**
+     * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it
+     * will apply the default bounds to the provided snap fraction.
+     */
+    Rect getDefaultBounds(float snapFraction) {
         synchronized (mService.mWindowMap) {
             final Rect insetBounds = new Rect();
             getInsetBounds(insetBounds);
@@ -260,8 +291,14 @@
             final Rect defaultBounds = new Rect();
             final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
                     mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-            Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
-                    0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
+            if (snapFraction != INVALID_SNAP_FRACTION) {
+                defaultBounds.set(0, 0, size.getWidth(), size.getHeight());
+                final Rect movementBounds = getMovementBounds(defaultBounds);
+                mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
+            } else {
+                Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
+                        0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
+            }
             return defaultBounds;
         }
     }
@@ -299,9 +336,7 @@
             final Rect postChangeStackBounds = mTmpRect;
 
             // Calculate the snap fraction of the current stack along the old movement bounds
-            final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds);
-            final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
-                    preChangeMovementBounds);
+            final float snapFraction = getSnapFraction(postChangeStackBounds);
             mDisplayInfo.copyFrom(displayInfo);
 
             // Calculate the stack bounds in the new orientation to the same same fraction along the
@@ -414,7 +449,7 @@
             try {
                 final Rect insetBounds = new Rect();
                 getInsetBounds(insetBounds);
-                final Rect normalBounds = getDefaultBounds();
+                final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION);
                 if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
                     transformBoundsToAspectRatio(normalBounds, mAspectRatio,
                             false /* useCurrentMinEdgeSize */);
@@ -486,6 +521,14 @@
     }
 
     /**
+     * @return the default snap fraction to apply instead of the default gravity when calculating
+     *         the default stack bounds when first entering PiP.
+     */
+    private float getSnapFraction(Rect stackBounds) {
+        return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds));
+    }
+
+    /**
      * @return the pixels for a given dp value.
      */
     private int dpToPx(float dpValue, DisplayMetrics dm) {
@@ -494,7 +537,8 @@
 
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "PinnedStackController");
-        pw.print(prefix + "  defaultBounds="); getDefaultBounds().printShortString(pw);
+        pw.print(prefix + "  defaultBounds=");
+        getDefaultBounds(INVALID_SNAP_FRACTION).printShortString(pw);
         pw.println();
         mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
         pw.print(prefix + "  movementBounds="); getMovementBounds(mTmpRect).printShortString(pw);
@@ -516,7 +560,7 @@
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS);
+        getDefaultBounds(INVALID_SNAP_FRACTION).writeToProto(proto, DEFAULT_BOUNDS);
         mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
         getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS);
         proto.end(token);
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index b021a72..02fbfba 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -61,7 +61,7 @@
                     displayContent.getPinnedStackController();
             if (stackBounds == null) {
                 // Calculate the aspect ratio bounds from the default bounds
-                stackBounds = pinnedStackController.getDefaultBounds();
+                stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
             }
 
             if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
@@ -173,7 +173,7 @@
      * from fullscreen to non-fullscreen bounds.
      */
     public boolean deferScheduleMultiWindowModeChanged() {
-        synchronized(mWindowMap) {
+        synchronized (mWindowMap) {
             return mContainer.deferScheduleMultiWindowModeChanged();
         }
     }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index bda5bc9..a32e711 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -72,23 +72,29 @@
                     target.mInnerAnimationFinishedCallback.onAnimationFinished(anim);
                     return;
                 }
-                if (anim != mAnimation) {
-                    // Callback was from another animation - ignore.
-                    return;
-                }
 
-                final Transaction t = new Transaction();
-                SurfaceControl.openTransaction();
-                try {
-                    reset(t, true /* destroyLeash */);
-                    animationFinishedCallback.run();
-                } finally {
-                    // TODO: This should use pendingTransaction eventually, but right now things
-                    // happening on the animation finished callback are happening on the global
-                    // transaction.
-                    SurfaceControl.mergeToGlobalTransaction(t);
-                    SurfaceControl.closeTransaction();
-                }
+                // TODO: This should use pendingTransaction eventually, but right now things
+                // happening on the animation finished callback are happening on the global
+                // transaction.
+                // For now we need to run this after it's guaranteed that the transaction that
+                // reparents the surface onto the leash is executed already. Otherwise this may be
+                // executed first, leading to surface loss, as the reparent operations wouldn't
+                // be in order.
+                mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+                    if (anim != mAnimation) {
+                        // Callback was from another animation - ignore.
+                        return;
+                    }
+                    final Transaction t = new Transaction();
+                    SurfaceControl.openTransaction();
+                    try {
+                        reset(t, true /* destroyLeash */);
+                        animationFinishedCallback.run();
+                    } finally {
+                        SurfaceControl.mergeToGlobalTransaction(t);
+                        SurfaceControl.closeTransaction();
+                    }
+                });
             }
         };
     }
@@ -213,7 +219,7 @@
             return;
         }
         final SurfaceControl surface = mAnimatable.getSurfaceControl();
-        final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
+        final SurfaceControl parent = mAnimatable.getAnimationLeashParent();
         if (surface == null || parent == null) {
             Slog.w(TAG, "Unable to transfer animation, surface or parent is null");
             cancelAnimation();
@@ -287,6 +293,7 @@
             int height, boolean hidden) {
         if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
         final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
+                .setParent(mAnimatable.getAnimationLeashParent())
                 .setName(surface + " - animation-leash")
                 .setSize(width, height);
         final SurfaceControl leash = builder.build();
@@ -355,6 +362,11 @@
         SurfaceControl.Builder makeAnimationLeash();
 
         /**
+         * @return The parent that should be used for the animation leash.
+         */
+        @Nullable SurfaceControl getAnimationLeashParent();
+
+        /**
          * @return The surface of the object to be animated.
          */
         @Nullable SurfaceControl getSurfaceControl();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 3ffc7fa..eb8eae1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -44,6 +44,7 @@
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.RemoteException;
@@ -145,6 +146,7 @@
      * For {@link #prepareSurfaces}.
      */
     final Rect mTmpDimBoundsRect = new Rect();
+    private final Point mLastSurfaceSize = new Point();
 
     TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
         super(service);
@@ -743,7 +745,13 @@
         }
 
         final Rect stackBounds = getBounds();
-        transaction.setSize(mSurfaceControl, stackBounds.width(), stackBounds.height());
+        final int width = stackBounds.width();
+        final int height = stackBounds.height();
+        if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
+            return;
+        }
+        transaction.setSize(mSurfaceControl, width, height);
+        mLastSurfaceSize.set(width, height);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 9865293..031b57b 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -92,8 +92,8 @@
             t.setFinalCrop(leash, mStackBounds);
             t.setWindowCrop(leash, tmp.transformation.getClipRect());
         } else {
-            mTmpRect.set(tmp.transformation.getClipRect());
-            mTmpRect.intersect(mStackBounds);
+            mTmpRect.set(mStackBounds);
+            mTmpRect.intersect(tmp.transformation.getClipRect());
             t.setWindowCrop(leash, mTmpRect);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 8bceb64..7295873 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -35,6 +35,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 
 /**
  * Singleton class that carries out the animations and Surface operations in a separate task
@@ -87,6 +88,12 @@
      */
     private boolean mAnimationFrameCallbackScheduled;
 
+    /**
+     * A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is
+     * executed and the corresponding transaction is closed and applied.
+     */
+    private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
+
     WindowAnimator(final WindowManagerService service) {
         mService = service;
         mContext = service.mContext;
@@ -262,6 +269,7 @@
             mService.destroyPreservedSurfaceLocked();
             mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
+            executeAfterPrepareSurfacesRunnables();
 
             if (DEBUG_WINDOW_TRACE) {
                 Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
@@ -425,4 +433,23 @@
     void orAnimating(boolean animating) {
         mAnimating |= animating;
     }
+
+    /**
+     * Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
+     * the corresponding transaction is closed and applied.
+     */
+    void addAfterPrepareSurfacesRunnable(Runnable r) {
+        mAfterPrepareSurfacesRunnables.add(r);
+        scheduleAnimation();
+    }
+
+    private void executeAfterPrepareSurfacesRunnables() {
+
+        // Traverse in order they were added.
+        final int size = mAfterPrepareSurfacesRunnables.size();
+        for (int i = 0; i < size; i++) {
+            mAfterPrepareSurfacesRunnables.get(i).run();
+        }
+        mAfterPrepareSurfacesRunnables.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b251b53..af31410 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -95,6 +95,7 @@
     protected final WindowManagerService mService;
 
     private final Point mTmpPos = new Point();
+    protected final Point mLastSurfacePosition = new Point();
 
     /** Total number of elements in this subtree, including our own hierarchy element. */
     private int mTreeWeight = 1;
@@ -1088,6 +1089,11 @@
         return makeSurface();
     }
 
+    @Override
+    public SurfaceControl getAnimationLeashParent() {
+        return getParentSurfaceControl();
+    }
+
     /**
      * @return The layer on which all app animations are happening.
      */
@@ -1172,7 +1178,12 @@
         }
 
         getRelativePosition(mTmpPos);
+        if (mTmpPos.equals(mLastSurfacePosition)) {
+            return;
+        }
+
         transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
+        mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             mChildren.get(i).updateSurfacePosition(transaction);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dfb385b..fcbc802 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -48,8 +48,8 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 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_MEDIA_OVERLAY;
 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;
@@ -112,15 +112,36 @@
 import static com.android.server.wm.proto.WindowStateProto.CONTAINING_FRAME;
 import static com.android.server.wm.proto.WindowStateProto.CONTENT_FRAME;
 import static com.android.server.wm.proto.WindowStateProto.CONTENT_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.CUTOUT;
+import static com.android.server.wm.proto.WindowStateProto.DECOR_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.DESTROYING;
+import static com.android.server.wm.proto.WindowStateProto.DISPLAY_FRAME;
 import static com.android.server.wm.proto.WindowStateProto.DISPLAY_ID;
 import static com.android.server.wm.proto.WindowStateProto.FRAME;
 import static com.android.server.wm.proto.WindowStateProto.GIVEN_CONTENT_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.HAS_SURFACE;
 import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER;
+import static com.android.server.wm.proto.WindowStateProto.IS_ON_SCREEN;
+import static com.android.server.wm.proto.WindowStateProto.IS_READY_FOR_DISPLAY;
+import static com.android.server.wm.proto.WindowStateProto.IS_VISIBLE;
+import static com.android.server.wm.proto.WindowStateProto.OUTSETS;
+import static com.android.server.wm.proto.WindowStateProto.OUTSET_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.OVERSCAN_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.OVERSCAN_INSETS;
 import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME;
-import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
+import static com.android.server.wm.proto.WindowStateProto.REMOVED;
+import static com.android.server.wm.proto.WindowStateProto.REMOVE_ON_EXIT;
+import static com.android.server.wm.proto.WindowStateProto.REQUESTED_HEIGHT;
+import static com.android.server.wm.proto.WindowStateProto.REQUESTED_WIDTH;
 import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION;
+import static com.android.server.wm.proto.WindowStateProto.STABLE_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
 import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
 import static com.android.server.wm.proto.WindowStateProto.SURFACE_POSITION;
+import static com.android.server.wm.proto.WindowStateProto.SYSTEM_UI_VISIBILITY;
+import static com.android.server.wm.proto.WindowStateProto.VIEW_VISIBILITY;
+import static com.android.server.wm.proto.WindowStateProto.VISIBLE_FRAME;
+import static com.android.server.wm.proto.WindowStateProto.VISIBLE_INSETS;
 import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
@@ -142,6 +163,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.util.Slog;
@@ -2222,12 +2244,13 @@
                         mWinAnimator + ": " + mPolicyVisibilityAfterAnim);
             }
             mPolicyVisibility = mPolicyVisibilityAfterAnim;
-            setDisplayLayoutNeeded();
             if (!mPolicyVisibility) {
+                mWinAnimator.hide("checkPolicyVisibilityChange");
                 if (mService.mCurrentFocus == this) {
                     if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                             "setAnimationLocked: setting mFocusMayChange true");
                     mService.mFocusMayChange = true;
+                    setDisplayLayoutNeeded();
                 }
                 // Window is no longer visible -- make sure if we were waiting
                 // for it to be displayed before enabling the display, that
@@ -3083,6 +3106,27 @@
         for (int i = 0; i < mChildren.size(); i++) {
             mChildren.get(i).writeToProto(proto, CHILD_WINDOWS, trim);
         }
+        proto.write(REQUESTED_WIDTH, mRequestedWidth);
+        proto.write(REQUESTED_HEIGHT, mRequestedHeight);
+        proto.write(VIEW_VISIBILITY, mViewVisibility);
+        proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
+        proto.write(HAS_SURFACE, mHasSurface);
+        proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
+        mDisplayFrame.writeToProto(proto, DISPLAY_FRAME);
+        mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME);
+        mVisibleFrame.writeToProto(proto, VISIBLE_FRAME);
+        mDecorFrame.writeToProto(proto, DECOR_FRAME);
+        mOutsetFrame.writeToProto(proto, OUTSET_FRAME);
+        mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
+        mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
+        mStableInsets.writeToProto(proto, STABLE_INSETS);
+        mOutsets.writeToProto(proto, OUTSETS);
+        mDisplayCutout.writeToProto(proto, CUTOUT);
+        proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
+        proto.write(DESTROYING, mDestroying);
+        proto.write(REMOVED, mRemoved);
+        proto.write(IS_ON_SCREEN, isOnScreen());
+        proto.write(IS_VISIBLE, isVisible());
         proto.end(token);
     }
 
@@ -3675,6 +3719,13 @@
             windowInfo.activityToken = mAppToken.appToken.asBinder();
         }
         windowInfo.title = mAttrs.accessibilityTitle;
+        // Panel windows have no public way to set the a11y title directly. Use the
+        // regular title as a fallback.
+        if (TextUtils.isEmpty(windowInfo.title)
+                && (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW)
+                && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW)) {
+            windowInfo.title = mAttrs.getTitle();
+        }
         windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
         windowInfo.focused = isFocused();
         Task task = getTask();
@@ -4291,8 +4342,22 @@
         float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
         float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
         float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
-        float9[Matrix.MTRANS_X] = mSurfacePosition.x + mShownPosition.x;
-        float9[Matrix.MTRANS_Y] = mSurfacePosition.y + mShownPosition.y;
+        int x = mSurfacePosition.x + mShownPosition.x;
+        int y = mSurfacePosition.y + mShownPosition.y;
+
+        // If changed, also adjust transformFrameToSurfacePosition
+        final WindowContainer parent = getParent();
+        if (isChildWindow()) {
+            final WindowState parentWindow = getParentWindow();
+            x += parentWindow.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
+            y += parentWindow.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
+        } else if (parent != null) {
+            final Rect parentBounds = parent.getBounds();
+            x += parentBounds.left;
+            y += parentBounds.top;
+        }
+        float9[Matrix.MTRANS_X] = x;
+        float9[Matrix.MTRANS_Y] = y;
         float9[Matrix.MPERSP_0] = 0;
         float9[Matrix.MPERSP_1] = 0;
         float9[Matrix.MPERSP_2] = 1;
@@ -4417,6 +4482,7 @@
 
         // Leash is now responsible for position, so set our position to 0.
         t.setPosition(mSurfaceControl, 0, 0);
+        mLastSurfacePosition.set(0, 0);
     }
 
     @Override
@@ -4432,13 +4498,16 @@
         }
 
         transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
-        if (!mSurfaceAnimator.hasLeash()) {
+        if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
             t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+            mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
         }
     }
 
     private void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
         outPoint.set(left, top);
+
+        // If changed, also adjust getTransformationMatrix
         final WindowContainer parentWindowContainer = getParent();
         if (isChildWindow()) {
             // TODO: This probably falls apart at some point and we should
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d2247ac..96b0bd5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -42,8 +42,10 @@
 import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
+import static com.android.server.wm.proto.WindowStateAnimatorProto.DRAW_STATE;
 import static com.android.server.wm.proto.WindowStateAnimatorProto.LAST_CLIP_RECT;
 import static com.android.server.wm.proto.WindowStateAnimatorProto.SURFACE;
+import static com.android.server.wm.proto.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
 
 import android.content.Context;
 import android.graphics.Matrix;
@@ -1381,6 +1383,8 @@
         if (mSurfaceController != null) {
             mSurfaceController.writeToProto(proto, SURFACE);
         }
+        proto.write(DRAW_STATE, mDrawState);
+        mSystemDecorRect.writeToProto(proto, SYSTEM_DECOR_RECT);
         proto.end(token);
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index e55d4ea..c1e95eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -67,7 +67,8 @@
     public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
 
     public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
-            ParcelableKeyGenParameterSpec keySpec, KeymasterCertificateChain attestationChain) {
+            ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags,
+            KeymasterCertificateChain attestationChain) {
         return false;
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e5351b4..11fce4d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -45,6 +45,10 @@
 import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES;
 import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
 import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
 import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -217,8 +221,10 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -5052,17 +5058,82 @@
         return false;
     }
 
+    private void enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(
+            ComponentName who, String callerPackage, int callerUid) throws SecurityException {
+        if (who == null) {
+            if (!mOwners.hasDeviceOwner()) {
+                throw new SecurityException("Not in Device Owner mode.");
+            }
+            if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
+                throw new SecurityException("Caller not from device owner user");
+            }
+            if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
+                throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid() +
+                        "has no permission to generate keys.");
+            }
+        } else {
+            // Caller provided - check it is the device owner.
+            synchronized (this) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public static int[] translateIdAttestationFlags(
+            int idAttestationFlags) {
+        Map<Integer, Integer> idTypeToAttestationFlag = new HashMap();
+        idTypeToAttestationFlag.put(ID_TYPE_SERIAL, AttestationUtils.ID_TYPE_SERIAL);
+        idTypeToAttestationFlag.put(ID_TYPE_IMEI, AttestationUtils.ID_TYPE_IMEI);
+        idTypeToAttestationFlag.put(ID_TYPE_MEID, AttestationUtils.ID_TYPE_MEID);
+
+        int numFlagsSet = Integer.bitCount(idAttestationFlags);
+        // No flags are set - return null to indicate no device ID attestation information should
+        // be included in the attestation record.
+        if (numFlagsSet == 0) {
+            return null;
+        }
+
+        // If the ID_TYPE_BASE_INFO is set, make sure that a non-null array is returned, even if
+        // no other flag is set. That will lead to inclusion of general device make data in the
+        // attestation record, but no specific device identifiers.
+        if ((idAttestationFlags & ID_TYPE_BASE_INFO) != 0) {
+            numFlagsSet -= 1;
+            idAttestationFlags = idAttestationFlags & (~ID_TYPE_BASE_INFO);
+        }
+
+        int[] attestationUtilsFlags = new int[numFlagsSet];
+        int i = 0;
+        for (Integer idType: idTypeToAttestationFlag.keySet()) {
+            if ((idType & idAttestationFlags) != 0) {
+                attestationUtilsFlags[i++] = idTypeToAttestationFlag.get(idType);
+            }
+        }
+
+        return attestationUtilsFlags;
+    }
+
     @Override
     public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
             ParcelableKeyGenParameterSpec parcelableKeySpec,
+            int idAttestationFlags,
             KeymasterCertificateChain attestationChain) {
-        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
-                DELEGATION_CERT_INSTALL);
+        // Get attestation flags, if any.
+        final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags);
+        final boolean deviceIdAttestationRequired = attestationUtilsFlags != null;
+        final int callingUid = mInjector.binderGetCallingUid();
+
+        if (deviceIdAttestationRequired && attestationUtilsFlags.length > 0) {
+            enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(who, callerPackage, callingUid);
+        } else {
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_CERT_INSTALL);
+        }
         final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
-        if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) {
+        final String alias = keySpec.getKeystoreAlias();
+        if (TextUtils.isEmpty(alias)) {
             throw new IllegalArgumentException("Empty alias provided.");
         }
-        final String alias = keySpec.getKeystoreAlias();
         // As the caller will be granted access to the key, ensure no UID was specified, as
         // it will not have the desired effect.
         if (keySpec.getUid() != KeyStore.UID_SELF) {
@@ -5070,7 +5141,10 @@
             return false;
         }
 
-        final int callingUid = mInjector.binderGetCallingUid();
+        if (deviceIdAttestationRequired && (keySpec.getAttestationChallenge() == null)) {
+            throw new IllegalArgumentException(
+                    "Requested Device ID attestation but challenge is empty.");
+        }
 
         final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
         final long id = mInjector.binderClearCallingIdentity();
@@ -5103,7 +5177,7 @@
                 final byte[] attestationChallenge = keySpec.getAttestationChallenge();
                 if (attestationChallenge != null) {
                     final boolean attestationResult = keyChain.attestKey(
-                            alias, attestationChallenge, attestationChain);
+                            alias, attestationChallenge, attestationUtilsFlags, attestationChain);
                     if (!attestationResult) {
                         Log.e(LOG_TAG, String.format(
                                 "Attestation for %s failed, deleting key.", alias));
@@ -11802,10 +11876,14 @@
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
-            //STOPSHIP add support for COMP, DO, edge cases when device is rebooted/work mode off,
+            //STOPSHIP add support for COMP, edge cases when device is rebooted/work mode off,
             //transfer callbacks and broadcast
-            if (isProfileOwner(admin, callingUserId)) {
-                transferProfileOwner(admin, target, callingUserId);
+            synchronized (this) {
+                if (isProfileOwner(admin, callingUserId)) {
+                    transferProfileOwnerLocked(admin, target, callingUserId);
+                } else if (isDeviceOwner(admin, callingUserId)) {
+                    transferDeviceOwnerLocked(admin, target, callingUserId);
+                }
             }
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -11815,15 +11893,25 @@
     /**
      * Transfers the profile owner for user with id profileOwnerUserId from admin to target.
      */
-    private void transferProfileOwner(ComponentName admin, ComponentName target,
+    private void transferProfileOwnerLocked(ComponentName admin, ComponentName target,
             int profileOwnerUserId) {
-        synchronized (this) {
-            transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
-            mOwners.transferProfileOwner(target, profileOwnerUserId);
-            Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
-            mOwners.writeProfileOwner(profileOwnerUserId);
-            mDeviceAdminServiceController.startServiceForOwner(
-                    target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
-        }
+        transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
+        mOwners.transferProfileOwner(target, profileOwnerUserId);
+        Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
+        mOwners.writeProfileOwner(profileOwnerUserId);
+        mDeviceAdminServiceController.startServiceForOwner(
+                target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
+    }
+
+    /**
+     * Transfers the device owner for user with id userId from admin to target.
+     */
+    private void transferDeviceOwnerLocked(ComponentName admin, ComponentName target, int userId) {
+        transferActiveAdminUncheckedLocked(target, admin, userId);
+        mOwners.transferDeviceOwner(target);
+        Slog.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
+        mOwners.writeDeviceOwner();
+        mDeviceAdminServiceController.startServiceForOwner(
+                target.getPackageName(), userId, "transfer-device-owner");
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 9042a8d..2a23888 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -288,6 +288,17 @@
         }
     }
 
+    void transferDeviceOwner(ComponentName target) {
+        synchronized (mLock) {
+            // We don't set a name because it's not used anyway.
+            // See DevicePolicyManagerService#getDeviceOwnerName
+            mDeviceOwner = new OwnerInfo(null, target,
+                    mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri,
+                    mDeviceOwner.remoteBugreportHash);
+            pushToPackageManagerLocked();
+        }
+    }
+
     ComponentName getProfileOwnerComponent(int userId) {
         synchronized (mLock) {
             OwnerInfo profileOwner = mProfileOwners.get(userId);
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
index c20c376..a473bc6 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
@@ -16,19 +16,17 @@
 
 package com.android.server.backup;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.AlarmManager;
 import android.content.Context;
 import android.os.Handler;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 
-import com.android.server.backup.testing.ShadowBackupTransportStub;
-import com.android.server.backup.testing.ShadowContextImplForBackup;
-import com.android.server.backup.testing.ShadowPackageManagerForBackup;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
 import com.android.server.testing.SystemLoaderClasses;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,19 +34,9 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-import static com.google.common.truth.Truth.assertThat;
-
 @RunWith(FrameworkRobolectricTestRunner.class)
-@Config(
-        manifest = Config.NONE,
-        sdk = 26,
-        shadows = {
-                ShadowContextImplForBackup.class,
-                ShadowBackupTransportStub.class,
-                ShadowPackageManagerForBackup.class
-        }
-)
-@SystemLoaderClasses({TransportManager.class})
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderClasses({BackupManagerConstants.class})
 @Presubmit
 public class BackupManagerConstantsTest {
     private static final String PACKAGE_NAME = "some.package.name";
@@ -59,17 +47,13 @@
         MockitoAnnotations.initMocks(this);
     }
 
-    @After
-    public void tearDown() throws Exception {
-    }
-
     @Test
     public void testDefaultValues() throws Exception {
         final Context context = RuntimeEnvironment.application.getApplicationContext();
         final Handler handler = new Handler();
 
-        Settings.Secure.putString(context.getContentResolver(),
-                Settings.Secure.BACKUP_MANAGER_CONSTANTS, null);
+        Settings.Secure.putString(
+                context.getContentResolver(), Settings.Secure.BACKUP_MANAGER_CONSTANTS, null);
 
         final BackupManagerConstants constants =
                 new BackupManagerConstants(handler, context.getContentResolver());
@@ -93,17 +77,21 @@
         final Context context = RuntimeEnvironment.application.getApplicationContext();
         final Handler handler = new Handler();
 
-        final String recievers_setting = "backup_finished_notification_receivers=" +
-                PACKAGE_NAME + ':' + ANOTHER_PACKAGE_NAME;
-        Settings.Secure.putString(context.getContentResolver(),
-                Settings.Secure.BACKUP_MANAGER_CONSTANTS, recievers_setting);
+        final String recieversSetting =
+                "backup_finished_notification_receivers="
+                        + PACKAGE_NAME
+                        + ':'
+                        + ANOTHER_PACKAGE_NAME;
+        Settings.Secure.putString(
+                context.getContentResolver(),
+                Settings.Secure.BACKUP_MANAGER_CONSTANTS,
+                recieversSetting);
 
         final BackupManagerConstants constants =
                 new BackupManagerConstants(handler, context.getContentResolver());
         constants.start();
 
-        assertThat(constants.getBackupFinishedNotificationReceivers()).isEqualTo(new String[] {
-                PACKAGE_NAME,
-                ANOTHER_PACKAGE_NAME});
+        assertThat(constants.getBackupFinishedNotificationReceivers())
+                .isEqualTo(new String[] {PACKAGE_NAME, ANOTHER_PACKAGE_NAME});
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 3b7db9f..4176d2a 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -24,12 +24,15 @@
 import static junit.framework.Assert.fail;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyListOf;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -42,7 +45,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.net.INetworkRecommendationProvider;
 import android.net.INetworkScoreCache;
 import android.net.NetworkKey;
@@ -63,6 +68,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -128,7 +134,9 @@
     @Mock private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter;
     @Mock private WifiInfo mWifiInfo;
     @Mock private NetworkScoreService.ScoringServiceConnection mServiceConnection;
+    @Mock private PackageManagerInternal mPackageManagerInternal;
     @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
+    @Captor private ArgumentCaptor<PackageManagerInternal.PackagesProvider> mPackagesProviderCaptor;
 
     private ContentResolver mContentResolver;
     private NetworkScoreService mNetworkScoreService;
@@ -156,6 +164,7 @@
         when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
         mHandlerThread = new HandlerThread("NetworkScoreServiceTest");
         mHandlerThread.start();
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
         mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager,
                 networkScorerAppData -> mServiceConnection, mHandlerThread.getLooper());
         WifiConfiguration configuration = new WifiConfiguration();
@@ -181,6 +190,29 @@
     @After
     public void tearDown() throws Exception {
         mHandlerThread.quitSafely();
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+    }
+
+    @Test
+    public void testConstructor_setsUseOpenWifiPackagesProvider() {
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.USE_OPEN_WIFI_PACKAGE, "com.some.app");
+
+        verify(mPackageManagerInternal)
+                .setUseOpenWifiAppPackagesProvider(mPackagesProviderCaptor.capture());
+
+        String[] packages = mPackagesProviderCaptor.getValue().getPackages(0);
+        assertEquals(1, packages.length);
+        assertEquals("com.some.app", packages[0]);
+    }
+
+    @Test
+    public void testConstructor_registersUseOpenWifiPackageContentObserver() {
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.USE_OPEN_WIFI_PACKAGE, "com.some.other.app");
+
+        verify(mPackageManagerInternal, timeout(500))
+                .grantDefaultPermissionsToDefaultUseOpenWifiApp("com.some.other.app", 0);
     }
 
     @Test
@@ -947,8 +979,8 @@
     }
 
     private static class CountDownHandler extends Handler {
-        CountDownLatch latch = new CountDownLatch(1);
-        int receivedWhat;
+        final CountDownLatch latch = new CountDownLatch(1);
+        volatile int receivedWhat;
 
         CountDownHandler(Looper looper) {
             super(looper);
@@ -956,8 +988,8 @@
 
         @Override
         public void handleMessage(Message msg) {
-            latch.countDown();
             receivedWhat = msg.what;
+            latch.countDown();
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 8a54c4e..8d5556e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -32,6 +32,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Message;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.DebugUtils;
@@ -48,6 +49,34 @@
 import java.util.function.IntConsumer;
 
 
+/**
+ * Tests the state transitions of {@link MagnificationGestureHandler}
+ *
+ * Here's a dot graph describing the transitions being tested:
+ * {@code
+ *      digraph {
+ *          IDLE -> SHORTCUT_TRIGGERED [label="a11y\nbtn"]
+ *          SHORTCUT_TRIGGERED -> IDLE [label="a11y\nbtn"]
+ *          IDLE -> DOUBLE_TAP [label="2tap"]
+ *          DOUBLE_TAP -> IDLE [label="timeout"]
+ *          DOUBLE_TAP -> TRIPLE_TAP_AND_HOLD [label="down"]
+ *          SHORTCUT_TRIGGERED -> TRIPLE_TAP_AND_HOLD [label="down"]
+ *          TRIPLE_TAP_AND_HOLD -> ZOOMED [label="up"]
+ *          TRIPLE_TAP_AND_HOLD -> DRAGGING_TMP [label="hold/\nswipe"]
+ *          DRAGGING_TMP -> IDLE [label="release"]
+ *          ZOOMED -> ZOOMED_DOUBLE_TAP [label="2tap"]
+ *          ZOOMED_DOUBLE_TAP -> ZOOMED [label="timeout"]
+ *          ZOOMED_DOUBLE_TAP -> DRAGGING [label="hold"]
+ *          ZOOMED_DOUBLE_TAP -> IDLE [label="tap"]
+ *          DRAGGING -> ZOOMED [label="release"]
+ *          ZOOMED -> IDLE [label="a11y\nbtn"]
+ *          ZOOMED -> PANNING [label="2hold"]
+ *          PANNING -> PANNING_SCALING [label="pinch"]
+ *          PANNING_SCALING -> ZOOMED [label="release"]
+ *          PANNING -> ZOOMED [label="release"]
+ *      }
+ * }
+ */
 @RunWith(AndroidJUnit4.class)
 public class MagnificationGestureHandlerTest {
 
@@ -76,6 +105,8 @@
     private MagnificationGestureHandler mMgh;
     private TestHandler mHandler;
 
+    private long mLastDownTime = Integer.MIN_VALUE;
+
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
@@ -104,7 +135,13 @@
         MagnificationGestureHandler h = new MagnificationGestureHandler(
                 mContext, mMagnificationController,
                 detectTripleTap, detectShortcutTrigger);
-        mHandler = new TestHandler(h.mDetectingState, mClock);
+        mHandler = new TestHandler(h.mDetectingState, mClock) {
+            @Override
+            protected String messageToString(Message m) {
+                return DebugUtils.valueToString(
+                        MagnificationGestureHandler.DetectingState.class, "MESSAGE_", m.what);
+            }
+        };
         h.mDetectingState.mHandler = mHandler;
         h.setNext(strictMock(EventStreamTransformation.class));
         return h;
@@ -184,11 +221,11 @@
             fastForward1sec();
         }, STATE_ZOOMED);
 
-        // tap+tap+swipe gets delegated
-        assertTransition(STATE_2TAPS, () -> {
-            allowEventDelegation();
-            swipe();
-        }, STATE_IDLE);
+        // tap+tap+swipe doesn't get delegated
+        assertTransition(STATE_2TAPS, () -> swipe(), STATE_IDLE);
+
+        // tap+tap+swipe initiates viewport dragging immediately
+        assertTransition(STATE_2TAPS, () -> swipeAndHold(), STATE_DRAGGING_TMP);
     }
 
     @Test
@@ -439,23 +476,24 @@
     }
 
     private void tap() {
-        MotionEvent downEvent = downEvent();
-        send(downEvent);
-        send(upEvent(downEvent.getDownTime()));
+        send(downEvent());
+        send(upEvent());
     }
 
     private void swipe() {
-        MotionEvent downEvent = downEvent();
-        send(downEvent);
+        swipeAndHold();
+        send(upEvent());
+    }
+
+    private void swipeAndHold() {
+        send(downEvent());
         send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2));
-        send(upEvent(downEvent.getDownTime()));
     }
 
     private void longTap() {
-        MotionEvent downEvent = downEvent();
-        send(downEvent);
+        send(downEvent());
         fastForward(2000);
-        send(upEvent(downEvent.getDownTime()));
+        send(upEvent());
     }
 
     private void triggerShortcut() {
@@ -473,16 +511,17 @@
     }
 
     private MotionEvent moveEvent(float x, float y) {
-        return MotionEvent.obtain(defaultDownTime(), mClock.now(), ACTION_MOVE, x, y, 0);
+        return MotionEvent.obtain(mLastDownTime, mClock.now(), ACTION_MOVE, x, y, 0);
     }
 
     private MotionEvent downEvent() {
-        return MotionEvent.obtain(mClock.now(), mClock.now(),
+        mLastDownTime = mClock.now();
+        return MotionEvent.obtain(mLastDownTime, mLastDownTime,
                 ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0);
     }
 
     private MotionEvent upEvent() {
-        return upEvent(defaultDownTime());
+        return upEvent(mLastDownTime);
     }
 
     private MotionEvent upEvent(long downTime) {
@@ -490,11 +529,6 @@
                 MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0);
     }
 
-    private long defaultDownTime() {
-        MotionEvent lastDown = mMgh.mDetectingState.mLastDown;
-        return lastDown == null ? mClock.now() - 1 : lastDown.getDownTime();
-    }
-
     private MotionEvent pointerEvent(int action, float x, float y) {
         MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties();
         defPointerProperties.id = 0;
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index c7fd551..9cac536 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -1,6 +1,5 @@
 package com.android.server.backup.testutils;
 
-import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -32,7 +31,6 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
@@ -622,12 +620,6 @@
     }
 
     @Override
-    public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags,
-            String installerPackageName) {
-
-    }
-
-    @Override
     public int installExistingPackage(String packageName)
             throws NameNotFoundException {
         return 0;
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 60783db..58ac7d2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -17,6 +17,10 @@
 
 import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
 import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
@@ -71,6 +75,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.security.KeyChain;
+import android.security.keystore.AttestationUtils;
 import android.telephony.TelephonyManager;
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -4459,6 +4464,47 @@
         });
     }
 
+    private void assertAttestationFlags(int attestationFlags, int[] expectedFlags) {
+        int[] gotFlags = DevicePolicyManagerService.translateIdAttestationFlags(attestationFlags);
+        Arrays.sort(gotFlags);
+        Arrays.sort(expectedFlags);
+        assertTrue(Arrays.equals(expectedFlags, gotFlags));
+    }
+
+    public void testTranslationOfIdAttestationFlag() {
+        int[] allIdTypes = new int[]{ID_TYPE_SERIAL, ID_TYPE_IMEI, ID_TYPE_MEID};
+        int[] correspondingAttUtilsTypes = new int[]{
+            AttestationUtils.ID_TYPE_SERIAL, AttestationUtils.ID_TYPE_IMEI,
+            AttestationUtils.ID_TYPE_MEID};
+
+        // Test translation of zero flags
+        assertNull(DevicePolicyManagerService.translateIdAttestationFlags(0));
+
+        // Test translation of the ID_TYPE_BASE_INFO flag, which should yield an empty, but
+        // non-null array
+        assertAttestationFlags(ID_TYPE_BASE_INFO, new int[] {});
+
+        // Test translation of a single flag
+        assertAttestationFlags(ID_TYPE_BASE_INFO | ID_TYPE_SERIAL,
+                new int[] {AttestationUtils.ID_TYPE_SERIAL});
+        assertAttestationFlags(ID_TYPE_SERIAL, new int[] {AttestationUtils.ID_TYPE_SERIAL});
+
+        // Test translation of two flags
+        assertAttestationFlags(ID_TYPE_SERIAL | ID_TYPE_IMEI,
+                new int[] {AttestationUtils.ID_TYPE_IMEI, AttestationUtils.ID_TYPE_SERIAL});
+        assertAttestationFlags(ID_TYPE_BASE_INFO | ID_TYPE_MEID | ID_TYPE_SERIAL,
+                new int[] {AttestationUtils.ID_TYPE_MEID, AttestationUtils.ID_TYPE_SERIAL});
+
+        // Test translation of all three flags
+        assertAttestationFlags(ID_TYPE_SERIAL | ID_TYPE_IMEI | ID_TYPE_MEID,
+                new int[] {AttestationUtils.ID_TYPE_IMEI, AttestationUtils.ID_TYPE_SERIAL,
+                    AttestationUtils.ID_TYPE_MEID});
+        // Test translation of all three flags
+        assertAttestationFlags(ID_TYPE_SERIAL | ID_TYPE_IMEI | ID_TYPE_MEID | ID_TYPE_BASE_INFO,
+                new int[] {AttestationUtils.ID_TYPE_IMEI, AttestationUtils.ID_TYPE_SERIAL,
+                    AttestationUtils.ID_TYPE_MEID});
+    }
+
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(getServices().settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 2629b12..5105f4e 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -20,7 +20,13 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.os.PowerManager;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -35,18 +41,18 @@
 @RunWith(AndroidJUnit4.class)
 public class BrightnessMappingStrategyTest {
 
-    private static final float[] LUX_LEVELS = {
-        0f,
-        5f,
-        20f,
-        40f,
-        100f,
-        325f,
-        600f,
-        1250f,
-        2200f,
-        4000f,
-        5000f
+    private static final int[] LUX_LEVELS = {
+        0,
+        5,
+        20,
+        40,
+        100,
+        325,
+        600,
+        1250,
+        2200,
+        4000,
+        5000
     };
 
     private static final float[] DISPLAY_LEVELS_NITS = {
@@ -80,11 +86,13 @@
     private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
     private static final int[] BACKLIGHT_RANGE = { 1, 255 };
 
+    private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
+    private static final int[] EMPTY_INT_ARRAY = new int[0];
+
     @Test
     public void testSimpleStrategyMappingAtControlPoints() {
-        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(
-                LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
-                null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
+        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
         assertNotNull("BrightnessMappingStrategy should not be null", simple);
         for (int i = 0; i < LUX_LEVELS.length; i++) {
             final float expectedLevel =
@@ -96,9 +104,8 @@
 
     @Test
     public void testSimpleStrategyMappingBetweenControlPoints() {
-        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(
-                LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
-                null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
+        BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res);
         assertNotNull("BrightnessMappingStrategy should not be null", simple);
         for (int i = 1; i < LUX_LEVELS.length; i++) {
             final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
@@ -111,9 +118,9 @@
 
     @Test
     public void testPhysicalStrategyMappingAtControlPoints() {
-        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
-                LUX_LEVELS, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
+                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
         assertNotNull("BrightnessMappingStrategy should not be null", physical);
         for (int i = 0; i < LUX_LEVELS.length; i++) {
             final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1];
@@ -124,9 +131,9 @@
 
     @Test
     public void testPhysicalStrategyMappingBetweenControlPoints() {
-        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
-                LUX_LEVELS, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS,
+                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
         assertNotNull("BrightnessMappingStrategy should not be null", physical);
         Spline backlightToBrightness =
                 Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS);
@@ -141,82 +148,79 @@
 
     @Test
     public void testDefaultStrategyIsPhysical() {
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
-                LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
+        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
                 DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
         assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
     }
 
     @Test
     public void testNonStrictlyIncreasingLuxLevelsFails() {
-        final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
+        final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
         final int idx = lux.length / 2;
-        float tmp = lux[idx];
+        int tmp = lux[idx];
         lux[idx] = lux[idx+1];
         lux[idx+1] = tmp;
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
-                lux, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
+                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
         assertNull(strategy);
 
         // And make sure we get the same result even if it's monotone but not increasing.
         lux[idx] = lux[idx+1];
-        strategy = BrightnessMappingStrategy.create(
-                lux, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        strategy = BrightnessMappingStrategy.create(res);
         assertNull(strategy);
     }
 
     @Test
     public void testDifferentNumberOfControlPointValuesFails() {
         //Extra lux level
-        final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1);
+        final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1);
         // Make sure it's strictly increasing so that the only failure is the differing array
         // lengths
         lux[lux.length - 1] = lux[lux.length - 2] + 1;
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
-                lux, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        Resources res = createResources(lux, DISPLAY_LEVELS_NITS,
+                DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
         assertNull(strategy);
 
-        strategy = BrightnessMappingStrategy.create(
-                lux, DISPLAY_LEVELS_BACKLIGHT,
-                null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+        res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT);
+        strategy = BrightnessMappingStrategy.create(res);
         assertNull(strategy);
 
         // Extra backlight level
         final int[] backlight = Arrays.copyOf(
                 DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
         backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
-        strategy = BrightnessMappingStrategy.create(
-                LUX_LEVELS, backlight,
-                null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+        res = createResources(LUX_LEVELS, backlight);
+        strategy = BrightnessMappingStrategy.create(res);
         assertNull(strategy);
 
         // Extra nits level
         final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
         nits[nits.length - 1] = nits[nits.length - 2] + 1;
-        strategy = BrightnessMappingStrategy.create(
-                LUX_LEVELS, null /*brightnessLevelsBacklight*/,
-                nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+        strategy = BrightnessMappingStrategy.create(res);
         assertNull(strategy);
     }
 
     @Test
     public void testPhysicalStrategyRequiresNitsMapping() {
-        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
-                LUX_LEVELS, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, null, BACKLIGHT_RANGE);
+        Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
+                DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE);
+        BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res);
         assertNull(physical);
 
-        physical = BrightnessMappingStrategy.create(
-                LUX_LEVELS, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, null);
+        res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
+                DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/);
+        physical = BrightnessMappingStrategy.create(res);
         assertNull(physical);
 
-        physical = BrightnessMappingStrategy.create(
-                LUX_LEVELS, null /*brightnessLevelsBacklight*/,
-                DISPLAY_LEVELS_NITS, null, null);
+        res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
+                DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/,
+                EMPTY_INT_ARRAY /*backlightRange*/);
+        physical = BrightnessMappingStrategy.create(res);
         assertNull(physical);
     }
 
@@ -227,4 +231,73 @@
         }
         return newVals;
     }
+
+    private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) {
+        return createResources(luxLevels, brightnessLevelsBacklight,
+                EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/,
+                EMPTY_INT_ARRAY /*backlightRange*/);
+    }
+
+    private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits,
+            float[] nitsRange, int[] backlightRange) {
+        return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
+                brightnessLevelsNits, nitsRange, backlightRange);
+    }
+
+    private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
+            float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) {
+        Resources mockResources = mock(Resources.class);
+        // For historical reasons, the lux levels resource implicitly defines the first point as 0,
+        // so we need to chop it off of the array the mock resource object returns.
+        int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length);
+        when(mockResources.getIntArray(com.android.internal.R.array.config_autoBrightnessLevels))
+                .thenReturn(luxLevelsResource);
+
+        when(mockResources.getIntArray(
+                com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
+                .thenReturn(brightnessLevelsBacklight);
+
+        TypedArray mockBrightnessLevelNits = createFloatTypedArray(brightnessLevelsNits);
+        when(mockResources.obtainTypedArray(
+                com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
+                .thenReturn(mockBrightnessLevelNits);
+
+        TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
+        when(mockResources.obtainTypedArray(
+                com.android.internal.R.array.config_screenBrightnessNits))
+                .thenReturn(mockNitsRange);
+
+        when(mockResources.getIntArray(
+                com.android.internal.R.array.config_screenBrightnessBacklight))
+                .thenReturn(backlightRange);
+
+        when(mockResources.getInteger(
+                com.android.internal.R.integer.config_screenBrightnessSettingMinimum))
+                .thenReturn(1);
+        when(mockResources.getInteger(
+                com.android.internal.R.integer.config_screenBrightnessSettingMaximum))
+                .thenReturn(255);
+        return mockResources;
+    }
+
+    private TypedArray createFloatTypedArray(float[] vals) {
+        TypedArray mockArray = mock(TypedArray.class);
+        when(mockArray.length()).thenAnswer(invocation -> {
+            return vals.length;
+        });
+        when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> {
+            final float def = (float) invocation.getArguments()[1];
+            if (vals == null) {
+                return def;
+            }
+            int idx = (int) invocation.getArguments()[0];
+            if (idx >= 0 && idx < vals.length) {
+                return vals[idx];
+            } else {
+                return def;
+            }
+        });
+        return mockArray;
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 926009e..08edd52 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -66,6 +66,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BrightnessTrackerTest {
+    private static final float DEFAULT_INITIAL_BRIGHTNESS = 2.5f;
+    private static final float FLOAT_DELTA = 0.01f;
 
     private BrightnessTracker mTracker;
     private TestInjector mInjector;
@@ -98,7 +100,6 @@
         mInjector.mInteractive = false;
         startTracker(mTracker);
         assertNull(mInjector.mSensorListener);
-        assertNotNull(mInjector.mSettingsObserver);
         assertNotNull(mInjector.mBroadcastReceiver);
         assertTrue(mInjector.mIdleScheduled);
         Intent onIntent = new Intent();
@@ -119,7 +120,6 @@
 
         mTracker.stop();
         assertNull(mInjector.mSensorListener);
-        assertNull(mInjector.mSettingsObserver);
         assertNull(mInjector.mBroadcastReceiver);
         assertFalse(mInjector.mIdleScheduled);
     }
@@ -128,12 +128,10 @@
     public void testBrightnessEvent() {
         final int brightness = 20;
 
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
         startTracker(mTracker);
         mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, brightness);
         List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
@@ -141,10 +139,11 @@
         BrightnessChangeEvent event = events.get(0);
         assertEquals(mInjector.currentTimeMillis(), event.timeStamp);
         assertEquals(1, event.luxValues.length);
-        assertEquals(1.0f, event.luxValues[0], 0.1f);
+        assertEquals(1.0f, event.luxValues[0], FLOAT_DELTA);
         assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2),
                 event.luxTimestamps[0]);
-        assertEquals(brightness, event.brightness);
+        assertEquals(brightness, event.brightness, FLOAT_DELTA);
+        assertEquals(DEFAULT_INITIAL_BRIGHTNESS, event.lastBrightness, FLOAT_DELTA);
 
         // System had no data so these should all be at defaults.
         assertEquals(Float.NaN, event.batteryLevel, 0.0);
@@ -154,21 +153,18 @@
 
     @Test
     public void testBrightnessFullPopulatedEvent() {
-        final int lastBrightness = 230;
+        final int initialBrightness = 230;
         final int brightness = 130;
 
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, lastBrightness);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3333);
 
-        startTracker(mTracker);
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
+        startTracker(mTracker, initialBrightness);
         mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
                 batteryChangeEvent(30, 60));
         mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
         final long sensorTime = mInjector.currentTimeMillis();
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, brightness);
         List<BrightnessChangeEvent> eventsNoPackage
                 = mTracker.getEvents(0, false).getList();
         List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
@@ -179,9 +175,9 @@
         assertEquals(event.timeStamp, mInjector.currentTimeMillis());
         assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f);
         assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps);
-        assertEquals(brightness, event.brightness);
-        assertEquals(lastBrightness, event.lastBrightness);
-        assertEquals(0.5, event.batteryLevel, 0.01);
+        assertEquals(brightness, event.brightness, FLOAT_DELTA);
+        assertEquals(initialBrightness, event.lastBrightness, FLOAT_DELTA);
+        assertEquals(0.5, event.batteryLevel, FLOAT_DELTA);
         assertTrue(event.nightMode);
         assertEquals(3333, event.colorTemperature);
         assertEquals("a.package", event.packageName);
@@ -192,45 +188,34 @@
     }
 
     @Test
-    public void testIgnoreSelfChange() {
+    public void testIgnoreAutomaticBrightnessChange() {
         final int initialBrightness = 30;
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, initialBrightness);
-        startTracker(mTracker);
+        startTracker(mTracker, initialBrightness);
         mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
 
         final int systemUpdatedBrightness = 20;
-        mTracker.setBrightness(systemUpdatedBrightness, 0);
-        assertEquals(systemUpdatedBrightness,
-                (int) mInjector.mSystemIntSettings.get(Settings.System.SCREEN_BRIGHTNESS));
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/);
         List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         // No events because we filtered out our change.
         assertEquals(0, events.size());
 
         final int firstUserUpdateBrightness = 20;
         // Then change comes from somewhere else so we shouldn't filter.
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS,
-                firstUserUpdateBrightness);
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, firstUserUpdateBrightness);
 
         // and with a different brightness value.
         final int secondUserUpdateBrightness = 34;
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS,
-                secondUserUpdateBrightness);
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, secondUserUpdateBrightness);
         events = mTracker.getEvents(0, true).getList();
 
         assertEquals(2, events.size());
         // First event is change from system update (20) to first user update (20)
-        assertEquals(systemUpdatedBrightness, events.get(0).lastBrightness);
-        assertEquals(firstUserUpdateBrightness, events.get(0).brightness);
+        assertEquals(systemUpdatedBrightness, events.get(0).lastBrightness, FLOAT_DELTA);
+        assertEquals(firstUserUpdateBrightness, events.get(0).brightness, FLOAT_DELTA);
         // Second event is from first to second user update.
-        assertEquals(firstUserUpdateBrightness, events.get(1).lastBrightness);
-        assertEquals(secondUserUpdateBrightness, events.get(1).brightness);
+        assertEquals(firstUserUpdateBrightness, events.get(1).lastBrightness, FLOAT_DELTA);
+        assertEquals(secondUserUpdateBrightness, events.get(1).brightness, FLOAT_DELTA);
 
         mTracker.stop();
     }
@@ -243,9 +228,7 @@
         for (int brightness = 0; brightness <= 255; ++brightness) {
             mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
             mInjector.incrementTime(TimeUnit.SECONDS.toNanos(1));
-            mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
-            mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                    Settings.System.SCREEN_BRIGHTNESS));
+            notifyBrightnessChanged(mTracker, brightness);
         }
         List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
@@ -254,14 +237,13 @@
         assertEquals(100, events.size());
         for (int i = 0; i < events.size(); i++) {
             BrightnessChangeEvent event = events.get(i);
-            assertEquals(156 + i, event.brightness);
+            assertEquals(156 + i, event.brightness, FLOAT_DELTA);
         }
     }
 
     @Test
     public void testLimitedSensorEvents() {
         final int brightness = 20;
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
 
         startTracker(mTracker);
         // 20 Sensor events 1 second apart.
@@ -269,8 +251,7 @@
             mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
             mInjector.mSensorListener.onSensorChanged(createSensorEvent(i + 1.0f));
         }
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, 20);
         List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
@@ -284,7 +265,7 @@
             assertEquals(event.luxTimestamps[11 - i],
                     mInjector.currentTimeMillis() - i * TimeUnit.SECONDS.toMillis(1));
         }
-        assertEquals(brightness, event.brightness);
+        assertEquals(brightness, event.brightness, FLOAT_DELTA);
     }
 
     @Test
@@ -298,25 +279,25 @@
         String eventFile =
                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
                 + "<events>\n"
-                + "<event brightness=\"194\" timestamp=\""
+                + "<event nits=\"194.2\" timestamp=\""
                 + Long.toString(someTimeAgo) + "\" packageName=\""
                 + "com.example.app\" user=\"10\" "
-                + "lastBrightness=\"32\" "
+                + "lastNits=\"32.333\" "
                 + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n"
                 + "lux=\"32.2,31.1\" luxTimestamps=\""
                 + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
-                + "<event brightness=\"71\" timestamp=\""
+                + "<event nits=\"71\" timestamp=\""
                 + Long.toString(someTimeAgo) + "\" packageName=\""
                 + "com.android.anapp\" user=\"11\" "
-                + "lastBrightness=\"32\" "
+                + "lastNits=\"32\" "
                 + "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\"\n"
                 + "lux=\"132.2,131.1\" luxTimestamps=\""
                 + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
                 // Event that is too old so shouldn't show up.
-                + "<event brightness=\"142\" timestamp=\""
+                + "<event nits=\"142\" timestamp=\""
                 + Long.toString(twoMonthsAgo) + "\" packageName=\""
                 + "com.example.app\" user=\"10\" "
-                + "lastBrightness=\"32\" "
+                + "lastNits=\"32\" "
                 + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n"
                 + "lux=\"32.2,31.1\" luxTimestamps=\""
                 + Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>"
@@ -326,27 +307,27 @@
         assertEquals(1, events.size());
         BrightnessChangeEvent event = events.get(0);
         assertEquals(someTimeAgo, event.timeStamp);
-        assertEquals(194, event.brightness);
-        assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, 0.01f);
+        assertEquals(194.2, event.brightness, FLOAT_DELTA);
+        assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, FLOAT_DELTA);
         assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
-        assertEquals(32, event.lastBrightness);
+        assertEquals(32.333, event.lastBrightness, FLOAT_DELTA);
         assertEquals(0, event.userId);
         assertFalse(event.nightMode);
-        assertEquals(1.0f, event.batteryLevel, 0.01);
+        assertEquals(1.0f, event.batteryLevel, FLOAT_DELTA);
         assertEquals("com.example.app", event.packageName);
 
         events = tracker.getEvents(1, true).getList();
         assertEquals(1, events.size());
         event = events.get(0);
         assertEquals(someTimeAgo, event.timeStamp);
-        assertEquals(71, event.brightness);
-        assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, 0.01f);
+        assertEquals(71, event.brightness, FLOAT_DELTA);
+        assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, FLOAT_DELTA);
         assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
-        assertEquals(32, event.lastBrightness);
+        assertEquals(32, event.lastBrightness, FLOAT_DELTA);
         assertEquals(1, event.userId);
         assertTrue(event.nightMode);
         assertEquals(3235, event.colorTemperature);
-        assertEquals(0.5f, event.batteryLevel, 0.01);
+        assertEquals(0.5f, event.batteryLevel, FLOAT_DELTA);
         assertEquals("com.android.anapp", event.packageName);
     }
 
@@ -370,7 +351,7 @@
         eventFile =
                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
                         + "<events>\n"
-                        + "<event brightness=\"194\" timestamp=\"" + someTimeAgo + "\" packageName=\""
+                        + "<event nits=\"194\" timestamp=\"" + someTimeAgo + "\" packageName=\""
                         + "com.example.app\" user=\"10\" "
                         + "batteryLevel=\"0.7\" nightMode=\"false\" colorTemperature=\"0\" />\n"
                         + "</events>";
@@ -386,7 +367,6 @@
     public void testWriteThenRead() throws Exception {
         final int brightness = 20;
 
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
 
@@ -399,8 +379,7 @@
         mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
         final long secondSensorTime = mInjector.currentTimeMillis();
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, brightness);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         mTracker.writeEventsLocked(baos);
         mTracker.stop();
@@ -414,10 +393,10 @@
 
         assertEquals(1, events.size());
         BrightnessChangeEvent event = events.get(0);
-        assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, 0.01f);
+        assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA);
         assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps);
-        assertEquals(brightness, event.brightness);
-        assertEquals(0.3, event.batteryLevel, 0.01f);
+        assertEquals(brightness, event.brightness, FLOAT_DELTA);
+        assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
         assertTrue(event.nightMode);
         assertEquals(3339, event.colorTemperature);
     }
@@ -426,7 +405,6 @@
     public void testWritePrunesOldEvents() throws Exception {
         final int brightness = 20;
 
-        mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
         mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
 
@@ -437,14 +415,12 @@
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
         mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f));
         final long sensorTime = mInjector.currentTimeMillis();
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, brightness);
 
         // 31 days later
         mInjector.incrementTime(TimeUnit.DAYS.toMillis(31));
         mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
-        mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
-                Settings.System.SCREEN_BRIGHTNESS));
+        notifyBrightnessChanged(mTracker, brightness);
         final long eventTime = mInjector.currentTimeMillis();
 
         List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
@@ -460,10 +436,10 @@
         assertEquals(eventTime, event.timeStamp);
 
         // We will keep one of the old sensor events because we keep 1 event outside the window.
-        assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, 0.01f);
+        assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA);
         assertArrayEquals(new long[] {sensorTime, eventTime}, event.luxTimestamps);
-        assertEquals(brightness, event.brightness);
-        assertEquals(0.3, event.batteryLevel, 0.01f);
+        assertEquals(brightness, event.brightness, FLOAT_DELTA);
+        assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
         assertTrue(event.nightMode);
         assertEquals(3339, event.colorTemperature);
     }
@@ -472,7 +448,7 @@
     public void testParcelUnParcel() {
         Parcel parcel = Parcel.obtain();
         BrightnessChangeEvent event = new BrightnessChangeEvent();
-        event.brightness = 23;
+        event.brightness = 23f;
         event.timeStamp = 345L;
         event.packageName = "com.example";
         event.userId = 12;
@@ -485,7 +461,7 @@
         event.batteryLevel = 0.7f;
         event.nightMode = false;
         event.colorTemperature = 345;
-        event.lastBrightness = 50;
+        event.lastBrightness = 50f;
 
         event.writeToParcel(parcel, 0);
         byte[] parceled = parcel.marshall();
@@ -497,16 +473,16 @@
 
         BrightnessChangeEvent event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel);
         parcel.recycle();
-        assertEquals(event.brightness, event2.brightness);
+        assertEquals(event.brightness, event2.brightness, FLOAT_DELTA);
         assertEquals(event.timeStamp, event2.timeStamp);
         assertEquals(event.packageName, event2.packageName);
         assertEquals(event.userId, event2.userId);
-        assertArrayEquals(event.luxValues, event2.luxValues, 0.01f);
+        assertArrayEquals(event.luxValues, event2.luxValues, FLOAT_DELTA);
         assertArrayEquals(event.luxTimestamps, event2.luxTimestamps);
-        assertEquals(event.batteryLevel, event2.batteryLevel, 0.01f);
+        assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
         assertEquals(event.nightMode, event2.nightMode);
         assertEquals(event.colorTemperature, event2.colorTemperature);
-        assertEquals(event.lastBrightness, event2.lastBrightness);
+        assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
 
         parcel = Parcel.obtain();
         event.batteryLevel = Float.NaN;
@@ -518,7 +494,7 @@
         parcel.unmarshall(parceled, 0, parceled.length);
         parcel.setDataPosition(0);
         event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel);
-        assertEquals(event.batteryLevel, event2.batteryLevel, 0.01f);
+        assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
     }
 
     private InputStream getInputStream(String data) {
@@ -550,7 +526,21 @@
     }
 
     private void startTracker(BrightnessTracker tracker) {
-        tracker.start();
+        startTracker(tracker, DEFAULT_INITIAL_BRIGHTNESS);
+    }
+
+    private void startTracker(BrightnessTracker tracker, float initialBrightness) {
+        tracker.start(initialBrightness);
+        mInjector.waitForHandler();
+    }
+
+    private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) {
+        notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/);
+    }
+
+    private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
+            boolean userInitiated) {
+        tracker.notifyBrightnessChanged(brightness, userInitiated);
         mInjector.waitForHandler();
     }
 
@@ -578,7 +568,6 @@
 
     private class TestInjector extends BrightnessTracker.Injector {
         SensorEventListener mSensorListener;
-        ContentObserver mSettingsObserver;
         BroadcastReceiver mBroadcastReceiver;
         Map<String, Integer> mSystemIntSettings = new HashMap<>();
         Map<String, Integer> mSecureIntSettings = new HashMap<>();
@@ -610,18 +599,6 @@
         }
 
         @Override
-        public void registerBrightnessObserver(ContentResolver resolver,
-                ContentObserver settingsObserver) {
-            mSettingsObserver = settingsObserver;
-        }
-
-        @Override
-        public void unregisterBrightnessObserver(Context context,
-                ContentObserver settingsObserver) {
-            mSettingsObserver = null;
-        }
-
-        @Override
         public void registerReceiver(Context context,
                 BroadcastReceiver shutdownReceiver, IntentFilter shutdownFilter) {
             mBroadcastReceiver = shutdownReceiver;
@@ -647,23 +624,6 @@
         }
 
         @Override
-        public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue,
-                int userId) {
-            Integer value = mSystemIntSettings.get(setting);
-            if (value == null) {
-                return defaultValue;
-            } else {
-                return value;
-            }
-        }
-
-        @Override
-        public void putSystemIntForUser(ContentResolver resolver, String setting, int value,
-                int userId) {
-            mSystemIntSettings.put(setting, value);
-        }
-
-        @Override
         public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
                 int userId) {
             Integer value = mSecureIntSettings.get(setting);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index ba40c67..114da1a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -386,23 +386,7 @@
     }
 
     @Test
-    public void packVaultParams_encodesMaxAttemptsAsThirdParam() throws Exception {
-        int maxAttempts = 10;
-
-        byte[] packedForm = KeySyncUtils.packVaultParams(
-                SecureBox.genKeyPair().getPublic(),
-                /*counterId=*/ 1001L,
-                maxAttempts,
-                /*deviceId=*/ 1L);
-
-        ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
-                .order(ByteOrder.LITTLE_ENDIAN);
-        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES);
-        assertEquals(maxAttempts, byteBuffer.getInt());
-    }
-
-    @Test
-    public void packVaultParams_encodesDeviceIdAsLastParam() throws Exception {
+    public void packVaultParams_encodesDeviceIdAsThirdParam() throws Exception {
         long deviceId = 102942158152L;
 
         byte[] packedForm = KeySyncUtils.packVaultParams(
@@ -413,10 +397,27 @@
 
         ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
                 .order(ByteOrder.LITTLE_ENDIAN);
-        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
+        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES);
         assertEquals(deviceId, byteBuffer.getLong());
     }
 
+    @Test
+    public void packVaultParams_encodesMaxAttemptsAsLastParam() throws Exception {
+        int maxAttempts = 10;
+
+        byte[] packedForm = KeySyncUtils.packVaultParams(
+                SecureBox.genKeyPair().getPublic(),
+                /*counterId=*/ 1001L,
+                maxAttempts,
+                /*deviceId=*/ 1L);
+
+        ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
+                .order(ByteOrder.LITTLE_ENDIAN);
+        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + 2 * Long.BYTES);
+        assertEquals(maxAttempts, byteBuffer.getInt());
+    }
+
+
     private static byte[] randomBytes(int n) {
         byte[] bytes = new byte[n];
         new Random().nextBytes(bytes);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 6f13a98..97fbca2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -205,6 +205,14 @@
     }
 
     @Test
+    public void init_savesGenerationIdToDatabase() throws Exception {
+        mPlatformKeyManager.init();
+
+        assertEquals(1,
+                mRecoverableKeyStoreDb.getPlatformKeyGenerationId(USER_ID_FIXTURE));
+    }
+
+    @Test
     public void init_setsGenerationIdTo1() throws Exception {
         mPlatformKeyManager.init();
 
@@ -212,7 +220,38 @@
     }
 
     @Test
+    public void init_incrementsGenerationIdIfKeyIsUnavailable() throws Exception {
+        mPlatformKeyManager.init();
+
+        mPlatformKeyManager.init();
+
+        assertEquals(2, mPlatformKeyManager.getGenerationId());
+    }
+
+    @Test
+    public void init_doesNotIncrementGenerationIdIfKeyAvailable() throws Exception {
+        mPlatformKeyManager.init();
+        when(mKeyStoreProxy
+                .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+                        + "platform/42/1/decrypt")).thenReturn(true);
+        when(mKeyStoreProxy
+                .containsAlias("com.android.server.locksettings.recoverablekeystore/"
+                        + "platform/42/1/encrypt")).thenReturn(true);
+
+        mPlatformKeyManager.init();
+
+        assertEquals(1, mPlatformKeyManager.getGenerationId());
+    }
+
+    @Test
+    public void getGenerationId_returnsMinusOneIfNotInitialized() throws Exception {
+        assertEquals(-1, mPlatformKeyManager.getGenerationId());
+    }
+
+    @Test
     public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
+        mPlatformKeyManager.init();
+
         mPlatformKeyManager.getDecryptKey();
 
         verify(mKeyStoreProxy).getKey(
@@ -222,6 +261,8 @@
 
     @Test
     public void getEncryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
+        mPlatformKeyManager.init();
+
         mPlatformKeyManager.getEncryptKey();
 
         verify(mKeyStoreProxy).getKey(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 88df62b..445fbde 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -37,6 +37,7 @@
 import android.content.Intent;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.security.recoverablekeystore.KeyDerivationParameters;
 import android.security.recoverablekeystore.KeyEntryRecoveryData;
@@ -96,7 +97,6 @@
     private static final byte[] TEST_SECRET = getUtf8Bytes("password1234");
     private static final byte[] TEST_VAULT_CHALLENGE = getUtf8Bytes("vault_challenge");
     private static final byte[] TEST_VAULT_PARAMS = getUtf8Bytes("vault_params");
-    private static final int TEST_USER_ID = 10009;
     private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
     private static final byte[] RECOVERY_RESPONSE_HEADER =
             "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
@@ -174,8 +174,7 @@
                                 TYPE_LOCKSCREEN,
                                 TYPE_PASSWORD,
                                 KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
-                                TEST_SECRET)),
-                TEST_USER_ID);
+                                TEST_SECRET)));
 
         verify(mMockContext, times(1))
                 .enforceCallingOrSelfPermission(
@@ -194,12 +193,11 @@
                                 TYPE_LOCKSCREEN,
                                 TYPE_PASSWORD,
                                 KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
-                                TEST_SECRET)),
-                TEST_USER_ID);
+                                TEST_SECRET)));
 
         assertEquals(1, mRecoverySessionStorage.size());
         RecoverySessionStorage.Entry entry =
-                mRecoverySessionStorage.get(TEST_USER_ID, TEST_SESSION_ID);
+                mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID);
         assertArrayEquals(TEST_SECRET, entry.getLskfHash());
         assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
     }
@@ -212,8 +210,7 @@
                     TEST_PUBLIC_KEY,
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
-                    ImmutableList.of(),
-                    TEST_USER_ID);
+                    ImmutableList.of());
             fail("should have thrown");
         } catch (RemoteException e) {
             assertEquals("Only a single KeyStoreRecoveryMetadata is supported", e.getMessage());
@@ -233,8 +230,7 @@
                                     TYPE_LOCKSCREEN,
                                     TYPE_PASSWORD,
                                     KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
-                                    TEST_SECRET)),
-                    TEST_USER_ID);
+                                    TEST_SECRET)));
             fail("should have thrown");
         } catch (RemoteException e) {
             assertEquals("Not a valid X509 key", e.getMessage());
@@ -249,12 +245,10 @@
                     /*recoveryKeyBlob=*/ randomBytes(32),
                     /*applicationKeys=*/ ImmutableList.of(
                             new KeyEntryRecoveryData(getUtf8Bytes("alias"), randomBytes(32))
-                    ),
-                    TEST_USER_ID);
+                    ));
             fail("should have thrown");
-        } catch (RemoteException e) {
-            assertEquals("User 10009 does not have pending session 'karlin'",
-                    e.getMessage());
+        } catch (ServiceSpecificException e) {
+            // expected
         }
     }
 
@@ -269,18 +263,17 @@
                         TYPE_LOCKSCREEN,
                         TYPE_PASSWORD,
                         KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
-                        TEST_SECRET)),
-                TEST_USER_ID);
+                        TEST_SECRET)));
 
         try {
             mRecoverableKeyStoreManager.recoverKeys(
                     TEST_SESSION_ID,
                     /*encryptedRecoveryKey=*/ randomBytes(60),
-                    /*applicationKeys=*/ ImmutableList.of(),
-                    /*uid=*/ TEST_USER_ID);
+                    /*applicationKeys=*/ ImmutableList.of());
             fail("should have thrown");
-        } catch (RemoteException e) {
-            assertEquals("Failed to decrypt recovery key", e.getMessage());
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key");
+            //assertEquals("Failed to decrypt recovery key", e.getMessage());
         }
     }
 
@@ -295,9 +288,8 @@
                         TYPE_LOCKSCREEN,
                         TYPE_PASSWORD,
                         KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
-                        TEST_SECRET)),
-                TEST_USER_ID);
-        byte[] keyClaimant = mRecoverySessionStorage.get(TEST_USER_ID, TEST_SESSION_ID)
+                        TEST_SECRET)));
+        byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
                 .getKeyClaimant();
         SecretKey recoveryKey = randomRecoveryKey();
         byte[] encryptedClaimResponse = encryptClaimResponse(
@@ -310,8 +302,7 @@
             mRecoverableKeyStoreManager.recoverKeys(
                     TEST_SESSION_ID,
                     /*encryptedRecoveryKey=*/ encryptedClaimResponse,
-                    /*applicationKeys=*/ ImmutableList.of(badApplicationKey),
-                    /*uid=*/ TEST_USER_ID);
+                    /*applicationKeys=*/ ImmutableList.of(badApplicationKey));
             fail("should have thrown");
         } catch (RemoteException e) {
             assertEquals("Failed to recover key with alias 'nick'", e.getMessage());
@@ -329,9 +320,8 @@
                         TYPE_LOCKSCREEN,
                         TYPE_PASSWORD,
                         KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
-                        TEST_SECRET)),
-                TEST_USER_ID);
-        byte[] keyClaimant = mRecoverySessionStorage.get(TEST_USER_ID, TEST_SESSION_ID)
+                        TEST_SECRET)));
+        byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
                 .getKeyClaimant();
         SecretKey recoveryKey = randomRecoveryKey();
         byte[] encryptedClaimResponse = encryptClaimResponse(
@@ -344,8 +334,7 @@
         Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
                 TEST_SESSION_ID,
                 encryptedClaimResponse,
-                ImmutableList.of(applicationKey),
-                TEST_USER_ID);
+                ImmutableList.of(applicationKey));
 
         assertThat(recoveredKeys).hasSize(1);
         assertThat(recoveredKeys.get(TEST_ALIAS)).isEqualTo(applicationKeyBytes);
@@ -357,27 +346,26 @@
         PendingIntent intent = PendingIntent.getBroadcast(
                 InstrumentationRegistry.getTargetContext(), /*requestCode=*/1,
                 new Intent(), /*flags=*/ 0);
-        mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent, /*userId=*/ 0);
+        mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent);
         verify(mMockListenersStorage).setSnapshotListener(eq(uid), any(PendingIntent.class));
     }
 
     @Test
     public void setRecoverySecretTypes() throws Exception {
-        int userId = UserHandle.getCallingUserId();
         int[] types1 = new int[]{11, 2000};
         int[] types2 = new int[]{1, 2, 3};
         int[] types3 = new int[]{};
 
-        mRecoverableKeyStoreManager.setRecoverySecretTypes(types1, userId);
-        assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes(userId)).isEqualTo(
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(types1);
+        assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
                 types1);
 
-        mRecoverableKeyStoreManager.setRecoverySecretTypes(types2, userId);
-        assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes(userId)).isEqualTo(
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(types2);
+        assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
                 types2);
 
-        mRecoverableKeyStoreManager.setRecoverySecretTypes(types3, userId);
-        assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes(userId)).isEqualTo(
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(types3);
+        assertThat(mRecoverableKeyStoreManager.getRecoverySecretTypes()).isEqualTo(
                 types3);
     }
 
@@ -391,13 +379,13 @@
         WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
         Map<String, Integer> statuses =
-                mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+                mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
         assertThat(statuses).hasSize(1);
         assertThat(statuses).containsEntry(alias, status);
 
         mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, new String[] {alias}, status2, userId);
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+                /*packageName=*/ null, new String[] {alias}, status2);
+        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
         assertThat(statuses).hasSize(1);
         assertThat(statuses).containsEntry(alias, status2); // updated
     }
@@ -415,30 +403,30 @@
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey);
         Map<String, Integer> statuses =
-                mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+                mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
         assertThat(statuses).hasSize(2);
         assertThat(statuses).containsEntry(alias, status);
         assertThat(statuses).containsEntry(alias2, status);
 
         mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, /*aliases=*/ null, status2, userId);
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+                /*packageName=*/ null, /*aliases=*/ null, status2);
+        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
         assertThat(statuses).hasSize(2);
         assertThat(statuses).containsEntry(alias, status2); // updated
         assertThat(statuses).containsEntry(alias2, status2); // updated
 
         mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, new String[] {alias2}, status3, userId);
+                /*packageName=*/ null, new String[] {alias2}, status3);
 
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
         assertThat(statuses).hasSize(2);
         assertThat(statuses).containsEntry(alias, status2);
         assertThat(statuses).containsEntry(alias2, status3); // updated
 
         mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, new String[] {alias, alias2}, status, userId);
+                /*packageName=*/ null, new String[] {alias, alias2}, status);
 
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
         assertThat(statuses).hasSize(2);
         assertThat(statuses).containsEntry(alias, status); // updated
         assertThat(statuses).containsEntry(alias2, status); // updated
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
index f3cb980..212d25d 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
@@ -95,41 +95,6 @@
     }
 
     @Test
-    public void testWatchlistSettings_writeSettingsToDisk() throws Exception {
-        copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
-        WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
-        settings.writeSettingsToDisk(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
-                Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
-                Arrays.asList(TEST_NEW_CC_IP_SHA256));
-        // Ensure old watchlist is not in memory
-        assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
-        assertFalse(settings.containsIp(TEST_CC_IP));
-        assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
-        assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
-        assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
-        assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
-        assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
-        assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
-        // Ensure new watchlist is in memory
-        assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
-        assertTrue(settings.containsIp(TEST_NEW_CC_IP));
-        // Reload settings from disk and test again
-        settings = new WatchlistSettings(mTestXmlFile);
-        // Ensure old watchlist is not in memory
-        assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
-        assertFalse(settings.containsIp(TEST_CC_IP));
-        assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
-        assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
-        assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
-        assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
-        assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
-        assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
-        // Ensure new watchlist is in memory
-        assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
-        assertTrue(settings.containsIp(TEST_NEW_CC_IP));
-    }
-
-    @Test
     public void testWatchlistSettings_writeSettingsToMemory() throws Exception {
         copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
         WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
index 2d4bc0f..029d9f1 100644
--- a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
+++ b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
@@ -104,6 +104,15 @@
         return new PriorityQueue<>(mMessages);
     }
 
+    /**
+     * Optionally-overridable to allow deciphering message types
+     *
+     * @see android.util.DebugUtils#valueToString - a handy utility to use when overriding this
+     */
+    protected String messageToString(Message message) {
+        return message.toString();
+    }
+
     private void dispatch(MsgInfo msg) {
         int msgId = msg.message.what;
 
@@ -148,7 +157,7 @@
         @Override
         public String toString() {
             return "MsgInfo{" +
-                    "message=" + message +
+                    "message=" + messageToString(message) +
                     ", sendTime=" + sendTime +
                     '}';
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 96ff461..6e57f47 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -45,6 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * Test class for {@link SurfaceAnimatorTest}.
@@ -82,6 +83,7 @@
         verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
 
         callbackCaptor.getValue().onAnimationFinished(mSpec);
+        waitUntilPrepareSurfaces();
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
         assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
@@ -104,11 +106,13 @@
 
         // First animation was finished, but this shouldn't cancel the second animation
         callbackCaptor.getValue().onAnimationFinished(mSpec);
+        waitUntilPrepareSurfaces();
         assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
 
         // Second animation was finished
         verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
         callbackCaptor.getValue().onAnimationFinished(mSpec2);
+        waitUntilPrepareSurfaces();
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
     }
@@ -160,6 +164,7 @@
         assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
         assertFalse(mAnimatable.mPendingDestroySurfaces.contains(leash));
         callbackCaptor.getValue().onAnimationFinished(mSpec);
+        waitUntilPrepareSurfaces();
         assertNotAnimating(mAnimatable2);
         assertTrue(mAnimatable2.mFinishedCallbackCalled);
         assertTrue(mAnimatable2.mPendingDestroySurfaces.contains(leash));
@@ -175,6 +180,14 @@
         assertNull(animatable.mSurfaceAnimator.getAnimation());
     }
 
+    private void waitUntilPrepareSurfaces() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        synchronized (sWm.mWindowMap) {
+            sWm.mAnimator.addAfterPrepareSurfacesRunnable(latch::countDown);
+        }
+        latch.await();
+    }
+
     private class MyAnimatable implements Animatable {
 
         final SurfaceControl mParent;
@@ -233,6 +246,11 @@
         }
 
         @Override
+        public SurfaceControl getAnimationLeashParent() {
+            return mParent;
+        }
+
+        @Override
         public SurfaceControl getSurfaceControl() {
             return mSurface;
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
new file mode 100644
index 0000000..9cdef16
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
+
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.ClipRectAnimation;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link WindowAnimationSpec} class.
+ *
+ *  atest FrameworksServicesTests:com.android.server.wm.WindowAnimationSpecTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WindowAnimationSpecTest {
+    private final SurfaceControl mSurfaceControl = mock(SurfaceControl.class);
+    private final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+    private final Animation mAnimation = mock(Animation.class);
+    private final Rect mStackBounds = new Rect(0, 0, 10, 10);
+
+    @Test
+    public void testApply_clipNone() {
+        Rect windowCrop = new Rect(0, 0, 20, 20);
+        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(windowCrop)));
+    }
+
+    @Test
+    public void testApply_clipAfter() {
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
+        verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(mStackBounds)));
+    }
+
+    @Test
+    public void testApply_clipBeforeNoAnimationBounds() {
+        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0)
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(mStackBounds)));
+    }
+
+    @Test
+    public void testApply_clipBeforeNoStackBounds() {
+        // Stack bounds is (0, 0, 0, 0) animation clip is (0, 0, 20, 20)
+        Rect windowCrop = new Rect(0, 0, 20, 20);
+        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
+    }
+
+    @Test
+    public void testApply_clipBeforeSmallerAnimationClip() {
+        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5)
+        Rect windowCrop = new Rect(0, 0, 5, 5);
+        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(windowCrop)));
+    }
+
+    @Test
+    public void testApply_clipBeforeSmallerStackClip() {
+        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 20, 20)
+        Rect windowCrop = new Rect(0, 0, 20, 20);
+        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.equals(mStackBounds)));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index c699a94..ff840f3 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -103,6 +103,7 @@
 
         context.getDisplay().getDisplayInfo(mDisplayInfo);
         mDisplayContent = createNewDisplay();
+        sWm.mAnimator.mInitialized = true;
         sWm.mDisplayEnabled = true;
         sWm.mDisplayReady = true;
 
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/provider/Telephony.java
similarity index 100%
rename from telephony/java/android/telephony/Telephony.java
rename to telephony/java/android/provider/Telephony.java
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1e6abf2..a494799 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -17,12 +17,14 @@
 package android.telephony;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.net.INetworkPolicyManager;
@@ -32,11 +34,14 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.DisplayMetrics;
+
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.PhoneConstants;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -430,6 +435,26 @@
             = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
 
     /**
+     * Activity Action: Display UI for managing the billing relationship plans
+     * between a carrier and a specific subscriber.
+     * <p>
+     * Carrier apps are encouraged to implement this activity, and the OS will
+     * provide an affordance to quickly enter this activity, typically via
+     * Settings. This affordance will only be shown when the carrier app is
+     * actively providing subscription plan information via
+     * {@link #setSubscriptionPlans(int, List)}.
+     * <p>
+     * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
+     * the user is interested in.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS
+            = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
+
+    /**
      * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and
      * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription
      * which has changed.
@@ -437,6 +462,7 @@
     public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
 
     private final Context mContext;
+    private final INetworkPolicyManager mNetworkPolicy;
 
     /**
      * A listener class for monitoring changes to {@link SubscriptionInfo} records.
@@ -509,22 +535,20 @@
     }
 
     /** @hide */
-    public SubscriptionManager(Context context) {
+    public SubscriptionManager(Context context) throws ServiceNotFoundException {
         if (DBG) logd("SubscriptionManager created");
         mContext = context;
+        mNetworkPolicy = INetworkPolicyManager.Stub
+                .asInterface(ServiceManager.getServiceOrThrow(Context.NETWORK_POLICY_SERVICE));
     }
 
     /**
-     * Get an instance of the SubscriptionManager from the Context.
-     * This invokes {@link android.content.Context#getSystemService
-     * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}.
-     *
-     * @param context to use.
-     * @return SubscriptionManager instance
+     * @deprecated developers should always obtain references directly from
+     *             {@link Context#getSystemService(Class)}.
      */
+    @Deprecated
     public static SubscriptionManager from(Context context) {
-        return (SubscriptionManager) context.getSystemService(
-                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        return context.getSystemService(SubscriptionManager.class);
     }
 
     /**
@@ -1622,11 +1646,9 @@
      */
     @SystemApi
     public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
-        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
-                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
         try {
             SubscriptionPlan[] subscriptionPlans =
-                    npm.getSubscriptionPlans(subId, mContext.getOpPackageName());
+                    mNetworkPolicy.getSubscriptionPlans(subId, mContext.getOpPackageName());
             return subscriptionPlans == null
                     ? Collections.emptyList() : Arrays.asList(subscriptionPlans);
         } catch (RemoteException e) {
@@ -1654,13 +1676,52 @@
      */
     @SystemApi
     public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
-        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
-                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
         try {
-            npm.setSubscriptionPlans(subId, plans.toArray(new SubscriptionPlan[plans.size()]),
-                    mContext.getOpPackageName());
+            mNetworkPolicy.setSubscriptionPlans(subId,
+                    plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /** @hide */
+    private String getSubscriptionPlansOwner(int subId) {
+        try {
+            return mNetworkPolicy.getSubscriptionPlansOwner(subId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Create an {@link Intent} that will launch towards the carrier app that is
+     * currently defining the billing relationship plan through
+     * {@link #setSubscriptionPlans(int, List)}.
+     *
+     * @return ready to launch Intent targeted towards the carrier app, or
+     *         {@code null} if no carrier app is defined, or if the defined
+     *         carrier app provides no management activity.
+     * @hide
+     */
+    public @Nullable Intent createManageSubscriptionIntent(int subId) {
+        // Bail if no owner
+        final String owner = getSubscriptionPlansOwner(subId);
+        if (owner == null) return null;
+
+        // Bail if no plans
+        final List<SubscriptionPlan> plans = getSubscriptionPlans(subId);
+        if (plans.isEmpty()) return null;
+
+        final Intent intent = new Intent(ACTION_MANAGE_SUBSCRIPTION_PLANS);
+        intent.setPackage(owner);
+        intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
+
+        // Bail if not implemented
+        if (mContext.getPackageManager().queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
+            return null;
+        }
+
+        return intent;
+    }
 }
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index ce8019f..7207ebc 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1090,15 +1090,6 @@
      * @hide
      */
     @Override
-    public void installPackage(Uri packageURI, PackageInstallObserver observer,
-            int flags, String installerPackageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @hide
-     */
-    @Override
     public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId,
             int flags) {
         throw new UnsupportedOperationException();
diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DexLoggerIntegrationTests/Android.mk
new file mode 100644
index 0000000..7187a37
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/Android.mk
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# Build a tiny library that the test app can dynamically load
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE := DexLoggerTestLibrary
+LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl)
+
+include $(BUILD_JAVA_LIBRARY)
+
+dexloggertest_jar := $(LOCAL_BUILT_MODULE)
+
+
+# Build the test app itself
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    truth-prebuilt \
+
+# This gets us the javalib.jar built by DexLoggerTestLibrary above.
+LOCAL_JAVA_RESOURCE_FILES := $(dexloggertest_jar)
+
+include $(BUILD_PACKAGE)
diff --git a/tests/DexLoggerIntegrationTests/AndroidManifest.xml b/tests/DexLoggerIntegrationTests/AndroidManifest.xml
new file mode 100644
index 0000000..a847e8f
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.dexloggertest">
+
+    <!-- Tests feature introduced in P (27) -->
+    <uses-sdk
+        android:minSdkVersion="27"
+        android:targetSdkVersion="27" />
+
+    <uses-permission android:name="android.permission.READ_LOGS" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.dexloggertest"
+        android:label="Integration test for DexLogger" />
+</manifest>
diff --git a/tests/DexLoggerIntegrationTests/AndroidTest.xml b/tests/DexLoggerIntegrationTests/AndroidTest.xml
new file mode 100644
index 0000000..8ed19f8
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 DexLogger Integration Tests">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="DexLoggerIntegrationTests.apk"/>
+        <option name="cleanup-apks" value="true"/>
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="DexLoggerIntegrationTests"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.frameworks.dexloggertest"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
diff --git a/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java b/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java
new file mode 100644
index 0000000..e995a26
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java
@@ -0,0 +1,22 @@
+/*
+ * 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 com.android.dcl;
+
+/** Dummy class which is built into a jar purely so we can pass it to DexClassLoader. */
+public final class Simple {
+    public Simple() {}
+}
diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java
new file mode 100644
index 0000000..d9f34d5
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/DexLoggerIntegrationTests.java
@@ -0,0 +1,151 @@
+/*
+ * 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 com.android.server.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.util.EventLog;
+
+import dalvik.system.DexClassLoader;
+
+import org.junit.After;
+import org.junit.AfterClass;
+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.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.List;
+
+/**
+ * Integration tests for {@link com.android.server.pm.dex.DexLogger}.
+ *
+ * The setup for the test dynamically loads code in a jar extracted
+ * from our assets (a secondary dex file).
+ *
+ * We then use adb to trigger secondary dex file reconcilation (and
+ * wait for it to complete). As a side-effect of this DexLogger should
+ * be notified of the file and should log the hash of the file's name
+ * and content.  We verify that this message appears in the event log.
+ *
+ * Run with "atest DexLoggerIntegrationTests".
+ */
+@RunWith(JUnit4.class)
+public final class DexLoggerIntegrationTests {
+
+    private static final String TAG = DexLoggerIntegrationTests.class.getSimpleName();
+
+    private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest";
+
+    private static final int SNET_TAG = 0x534e4554;
+    private static final String DCL_SUBTAG = "dcl";
+
+    // Obtained via "echo -n copied.jar | sha256sum"
+    private static final String EXPECTED_NAME_HASH =
+            "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
+
+    private static String expectedContentHash;
+
+    @BeforeClass
+    public static void setUpAll() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        MessageDigest hasher = MessageDigest.getInstance("SHA-256");
+
+        // Copy the jar from our Java resources to a private data directory
+        File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar");
+        try (InputStream input = DexLoggerIntegrationTests.class.getResourceAsStream("/javalib.jar");
+                OutputStream output = new FileOutputStream(privateCopy)) {
+            byte[] buffer = new byte[1024];
+            while (true) {
+                int numRead = input.read(buffer);
+                if (numRead < 0) {
+                    break;
+                }
+                output.write(buffer, 0, numRead);
+                hasher.update(buffer, 0, numRead);
+            }
+        }
+
+        // Remember the SHA-256 of the file content to check that it is the same as
+        // the value we see logged.
+        Formatter formatter = new Formatter();
+        for (byte b : hasher.digest()) {
+            formatter.format("%02X", b);
+        }
+        expectedContentHash = formatter.toString();
+
+        // Feed the jar to a class loader and make sure it contains what we expect.
+        ClassLoader loader =
+                new DexClassLoader(
+                    privateCopy.toString(), null, null, context.getClass().getClassLoader());
+        loader.loadClass("com.android.dcl.Simple");
+    }
+
+    @Test
+    public void testDexLoggerReconcileGeneratesEvents() throws Exception {
+        int[] tagList = new int[] { SNET_TAG };
+        List<EventLog.Event> events = new ArrayList<>();
+
+        // There may already be events in the event log - figure out the most recent one
+        EventLog.readEvents(tagList, events);
+        long previousEventNanos = events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
+        events.clear();
+
+        Process process = Runtime.getRuntime().exec(
+            "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME);
+        int exitCode = process.waitFor();
+        assertThat(exitCode).isEqualTo(0);
+
+        int myUid = android.os.Process.myUid();
+        String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash;
+
+        EventLog.readEvents(tagList, events);
+        boolean found = false;
+        for (EventLog.Event event : events) {
+            if (event.getTimeNanos() <= previousEventNanos) {
+                continue;
+            }
+            Object[] data = (Object[]) event.getData();
+
+            // We only care about DCL events that we generated.
+            String subTag = (String) data[0];
+            if (!DCL_SUBTAG.equals(subTag)) {
+                continue;
+            }
+            int uid = (int) data[1];
+            if (uid != myUid) {
+                continue;
+            }
+
+            String message = (String) data[2];
+            assertThat(message).isEqualTo(expectedMessage);
+            found = true;
+        }
+
+        assertThat(found).isTrue();
+    }
+}
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index f7ce2c7..8d8fc84 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -38,7 +38,7 @@
             public Engine onCreateEngine() {
                 return new Engine() {
                     @Override
-                    public void onAmbientModeChanged(boolean inAmbientMode) {
+                    public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
                         ambientModeChangedCount[0]++;
                     }
                 };
@@ -47,12 +47,12 @@
         WallpaperService.Engine engine = service.onCreateEngine();
         engine.setCreated(true);
 
-        engine.doAmbientModeChanged(false);
+        engine.doAmbientModeChanged(false, false);
         assertFalse("ambient mode should be false", engine.isInAmbientMode());
         assertEquals("onAmbientModeChanged should have been called",
                 ambientModeChangedCount[0], 1);
 
-        engine.doAmbientModeChanged(true);
+        engine.doAmbientModeChanged(true, false);
         assertTrue("ambient mode should be false", engine.isInAmbientMode());
         assertEquals("onAmbientModeChanged should have been called",
                 ambientModeChangedCount[0], 2);
diff --git a/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java
index 93aa555..b39db61 100644
--- a/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/PmPermissionsTests.java
@@ -71,18 +71,6 @@
     private class TestInstallObserver extends PackageInstallObserver {
     }
 
-    @SmallTest
-    public void testInstallPackage() {
-        TestInstallObserver observer = new TestInstallObserver();
-        try {
-            mPm.installPackage(null, observer, 0, null);
-            fail("PackageManager.installPackage" +
-                    "did not throw SecurityException as expected");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
     /*
      * This test verifies that PackageManger.freeStorage
      * enforces permission android.permission.CLEAR_APP_CACHE
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 7650795..102bbf9 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -20,6 +20,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <sstream>
 
 using namespace android;
 using namespace android::os;
@@ -482,9 +483,44 @@
 }
 
 // ================================================================================
+static string replace_string(const string& str, const char replace, const char with)
+{
+    string result(str);
+    const int N = result.size();
+    for (int i=0; i<N; i++) {
+        if (result[i] == replace) {
+            result[i] = with;
+        }
+    }
+    return result;
+}
+
+static void generateCsv(Descriptor const* descriptor, const string& indent, set<string>* parents) {
+    DebugStringOptions options;
+    options.include_comments = true;
+    for (int i=0; i<descriptor->field_count(); i++) {
+        const FieldDescriptor* field = descriptor->field(i);
+        stringstream text;
+        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
+            text << field->message_type()->name();
+        } else {
+            text << field->type_name();
+        }
+        text << " " << field->name();
+        printf("%s%s,\n", indent.c_str(), replace_string(text.str(), '\n', ' ').c_str());
+        if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
+            parents->find(field->message_type()->full_name()) == parents->end()) {
+            parents->insert(field->message_type()->full_name());
+            generateCsv(field->message_type(), indent + ",", parents);
+            parents->erase(field->message_type()->full_name());
+        }
+    }
+}
+
+// ================================================================================
 int main(int argc, char const *argv[])
 {
-    if (argc != 2) return 1;
+    if (argc < 2) return 1;
     const char* module = argv[1];
 
     Descriptor const* descriptor = IncidentProto::descriptor();
@@ -495,7 +531,23 @@
     if (strcmp(module, "incidentd") == 0 ) {
         return !generateSectionListCpp(descriptor);
     }
-
-    // return failure if not called by the whitelisted modules
+    // Generates Csv Format of proto definition for each section.
+    if (strcmp(module, "csv") == 0 && argc > 2) {
+        int sectionId = atoi(argv[2]);
+        for (int i=0; i<descriptor->field_count(); i++) {
+            const FieldDescriptor* field = descriptor->field(i);
+            if (strcmp(field->name().c_str(), argv[2]) == 0
+                || field->number() == sectionId) {
+                set<string> parents;
+                printf("%s\n", field->name().c_str());
+                generateCsv(field->message_type(), "", &parents);
+                break;
+            }
+        }
+        // Returns failure if csv is enabled to prevent Android building with it.
+        // It doesn't matter if this command runs manually.
+        return 1;
+    }
+    // Returns failure if not called by the whitelisted modules
     return 1;
 }
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
new file mode 100644
index 0000000..00fb8aa
--- /dev/null
+++ b/tools/sdkparcelables/Android.bp
@@ -0,0 +1,22 @@
+java_binary_host {
+    name: "sdkparcelables",
+    manifest: "manifest.txt",
+    srcs: [
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "asm-6.0",
+    ],
+}
+
+java_library_host {
+    name: "sdkparcelables_test",
+    manifest: "manifest.txt",
+    srcs: [
+        "tests/**/*.kt",
+    ],
+    static_libs: [
+        "sdkparcelables",
+        "junit",
+    ],
+}
diff --git a/tools/sdkparcelables/manifest.txt b/tools/sdkparcelables/manifest.txt
new file mode 100644
index 0000000..cd5420c
--- /dev/null
+++ b/tools/sdkparcelables/manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.sdkparcelables.MainKt
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
new file mode 100644
index 0000000..f278aec
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/AncestorCollector.kt
@@ -0,0 +1,28 @@
+package com.android.sdkparcelables
+
+import org.objectweb.asm.ClassVisitor
+import java.util.*
+
+data class Ancestors(val superName: String?, val interfaces: List<String>?)
+
+/** A class that implements an ASM ClassVisitor that collects super class and
+ * implemented interfaces for each class that it visits.
+ */
+class AncestorCollector(api: Int, dest: ClassVisitor?) : ClassVisitor(api, dest) {
+    private val _ancestors = LinkedHashMap<String, Ancestors>()
+
+    val ancestors: Map<String, Ancestors>
+        get() = _ancestors
+
+    override fun visit(version: Int, access: Int, name: String?, signature: String?,
+                       superName: String?, interfaces: Array<out String>?) {
+        name!!
+
+        val old = _ancestors.put(name, Ancestors(superName, interfaces?.toList()))
+        if (old != null) {
+            throw RuntimeException("class $name already found")
+        }
+
+        super.visit(version, access, name, signature, superName, interfaces)
+    }
+}
\ No newline at end of file
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
new file mode 100644
index 0000000..3e9d92c
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -0,0 +1,56 @@
+package com.android.sdkparcelables
+
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.Opcodes
+import java.io.File
+import java.io.IOException
+import java.util.zip.ZipFile
+
+fun main(args: Array<String>) {
+    if (args.size != 2) {
+        usage()
+    }
+
+    val zipFileName = args[0]
+    val aidlFileName = args[1]
+
+    val zipFile: ZipFile
+
+    try {
+        zipFile = ZipFile(zipFileName)
+    } catch (e: IOException) {
+        System.err.println("error reading input jar: ${e.message}")
+        kotlin.system.exitProcess(2)
+    }
+
+    val ancestorCollector = AncestorCollector(Opcodes.ASM6, null)
+
+    for (entry in zipFile.entries()) {
+        if (entry.name.endsWith(".class")) {
+            val reader = ClassReader(zipFile.getInputStream(entry))
+            reader.accept(ancestorCollector,
+                    ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
+        }
+    }
+
+    val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorCollector.ancestors)
+
+    try {
+        val outFile = File(aidlFileName)
+        val outWriter = outFile.bufferedWriter()
+        for (parcelable in parcelables) {
+            outWriter.write("parcelable ")
+            outWriter.write(parcelable.replace('/', '.').replace('$', '.'))
+            outWriter.write(";\n")
+        }
+        outWriter.flush()
+    } catch (e: IOException) {
+        System.err.println("error writing output aidl: ${e.message}")
+        kotlin.system.exitProcess(2)
+    }
+}
+
+fun usage() {
+    System.err.println("Usage: <input jar> <output aidl>")
+    kotlin.system.exitProcess(1)
+}
\ No newline at end of file
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
new file mode 100644
index 0000000..620f798
--- /dev/null
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/ParcelableDetector.kt
@@ -0,0 +1,52 @@
+package com.android.sdkparcelables
+
+/** A class that uses an ancestor map to find all classes that
+ * implement android.os.Parcelable, including indirectly through
+ * super classes or super interfaces.
+ */
+class ParcelableDetector {
+    companion object {
+        fun ancestorsToParcelables(ancestors: Map<String, Ancestors>): List<String> {
+            val impl = Impl(ancestors)
+            impl.build()
+            return impl.parcelables
+        }
+    }
+
+    private class Impl(val ancestors: Map<String, Ancestors>) {
+        val isParcelableCache = HashMap<String, Boolean>()
+        val parcelables = ArrayList<String>()
+
+        fun build() {
+            val classList = ancestors.keys
+            classList.filterTo(parcelables, this::isParcelable)
+            parcelables.sort()
+        }
+
+        private fun isParcelable(c: String?): Boolean {
+            if (c == null) {
+                return false
+            }
+
+            if (c == "android/os/Parcelable") {
+                return true
+            }
+
+            val old = isParcelableCache[c]
+            if (old != null) {
+                return old
+            }
+
+            val cAncestors = ancestors[c] ?:
+                    throw RuntimeException("class $c missing ancestor information")
+
+            val seq = (cAncestors.interfaces?.asSequence() ?: emptySequence()) +
+                    cAncestors.superName
+
+            val ancestorIsParcelable = seq.any(this::isParcelable)
+
+            isParcelableCache[c] = ancestorIsParcelable
+            return ancestorIsParcelable
+        }
+    }
+}
diff --git a/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
new file mode 100644
index 0000000..edfc825
--- /dev/null
+++ b/tools/sdkparcelables/tests/com/android/sdkparcelables/ParcelableDetectorTest.kt
@@ -0,0 +1,57 @@
+package com.android.sdkparcelables
+
+import junit.framework.TestCase.assertEquals
+import org.junit.Test
+
+class ParcelableDetectorTest {
+    @Test
+    fun `detect implements`() {
+        val ancestorMap = mapOf(
+                testAncestors("android/test/Parcelable",null, "android/os/Parcelable"),
+                testAncestors("android/os/Parcelable", null))
+
+        val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+        assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/Parcelable"))
+    }
+
+    @Test
+    fun `detect implements in reverse order`() {
+        val ancestorMap = mapOf(
+                testAncestors("android/os/Parcelable", null),
+                testAncestors("android/test/Parcelable",null, "android/os/Parcelable"))
+
+        val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+        assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/Parcelable"))
+    }
+
+    @Test
+    fun `detect super implements`() {
+        val ancestorMap = mapOf(
+                testAncestors("android/test/SuperParcelable",null, "android/os/Parcelable"),
+                testAncestors("android/test/Parcelable","android/test/SuperParcelable"),
+                testAncestors("android/os/Parcelable", null))
+
+        val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+        assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/Parcelable", "android/test/SuperParcelable"))
+    }
+
+    @Test
+    fun `detect super interface`() {
+        val ancestorMap = mapOf(
+                testAncestors("android/test/IParcelable",null, "android/os/Parcelable"),
+                testAncestors("android/test/Parcelable",null, "android/test/IParcelable"),
+                testAncestors("android/os/Parcelable", null))
+
+        val parcelables = ParcelableDetector.ancestorsToParcelables(ancestorMap)
+
+        assertEquals(parcelables, listOf("android/os/Parcelable", "android/test/IParcelable", "android/test/Parcelable"))
+    }
+
+}
+
+private fun testAncestors(name: String, superName: String?, vararg interfaces: String): Pair<String, Ancestors> {
+    return Pair(name, Ancestors(superName, interfaces.toList()))
+}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index d57d152..2f0c316 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -269,6 +269,10 @@
                     + identityChangedListener);
         }
 
+        if (attachCallback == null) {
+            throw new IllegalArgumentException("Null callback provided");
+        }
+
         synchronized (mLock) {
             Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
 
@@ -300,6 +304,10 @@
             DiscoverySessionCallback callback) {
         if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig);
 
+        if (callback == null) {
+            throw new IllegalArgumentException("Null callback provided");
+        }
+
         try {
             mService.publish(mContext.getOpPackageName(), clientId, publishConfig,
                     new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback,
@@ -333,6 +341,10 @@
             }
         }
 
+        if (callback == null) {
+            throw new IllegalArgumentException("Null callback provided");
+        }
+
         try {
             mService.subscribe(mContext.getOpPackageName(), clientId, subscribeConfig,
                     new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback,
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index b4c690f..240b3c1 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -123,6 +123,10 @@
                     + ", callback=" + callback + ", handler=" + handler);
         }
 
+        if (callback == null) {
+            throw new IllegalArgumentException("Null callback provided");
+        }
+
         Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {