Merge "Update the binder calls atom to track the calling app of a binder call."
diff --git a/Android.bp b/Android.bp
index 54b5760..56d6557 100644
--- a/Android.bp
+++ b/Android.bp
@@ -235,8 +235,7 @@
         "core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
         "core/java/android/os/IDeviceIdleController.aidl",
         "core/java/android/os/IHardwarePropertiesManager.aidl",
-        "core/java/android/os/IIncidentManager.aidl",
-        "core/java/android/os/IIncidentReportStatusListener.aidl",
+        ":libincident_aidl",
         "core/java/android/os/IMaintenanceActivityListener.aidl",
         "core/java/android/os/IMessenger.aidl",
         "core/java/android/os/INetworkActivityListener.aidl",
@@ -249,8 +248,7 @@
         "core/java/android/os/IRecoverySystemProgressListener.aidl",
         "core/java/android/os/IRemoteCallback.aidl",
         "core/java/android/os/ISchedulingPolicyService.aidl",
-        "core/java/android/os/IStatsCompanionService.aidl",
-        "core/java/android/os/IStatsManager.aidl",
+        ":statsd_aidl",
         "core/java/android/os/ISystemUpdateManager.aidl",
         "core/java/android/os/IThermalEventListener.aidl",
         "core/java/android/os/IThermalService.aidl",
@@ -724,6 +722,22 @@
 
 }
 
+filegroup {
+    name: "libincident_aidl",
+    srcs: [
+        "core/java/android/os/IIncidentManager.aidl",
+        "core/java/android/os/IIncidentReportStatusListener.aidl",
+    ],
+}
+
+filegroup {
+    name: "statsd_aidl",
+    srcs: [
+        "core/java/android/os/IStatsCompanionService.aidl",
+        "core/java/android/os/IStatsManager.aidl",
+    ],
+}
+
 java_library {
     name: "framework",
     defaults: ["framework-defaults"],
@@ -934,12 +948,14 @@
         "core/proto/android/os/batterytype.proto",
         "core/proto/android/os/cpufreq.proto",
         "core/proto/android/os/cpuinfo.proto",
+        "core/proto/android/os/data.proto",
         "core/proto/android/os/kernelwake.proto",
         "core/proto/android/os/pagetypeinfo.proto",
         "core/proto/android/os/procrank.proto",
         "core/proto/android/os/ps.proto",
         "core/proto/android/os/system_properties.proto",
         "core/proto/android/util/event_log_tags.proto",
+        "core/proto/android/util/log.proto",
     ],
 
     // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index e224fa3..35d3802 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -245,10 +245,11 @@
             state.pauseTiming();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -261,10 +262,11 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -277,10 +279,11 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -293,11 +296,12 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -310,11 +314,12 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -328,10 +333,11 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -345,10 +351,11 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -362,11 +369,12 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
@@ -380,11 +388,12 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.startRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
             layout.draw(c);
+            node.endRecording();
         }
     }
 
diff --git a/api/current.txt b/api/current.txt
index 02070c5..f27e83c 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -42607,6 +42607,7 @@
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
+    field public static final java.lang.String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
@@ -42713,6 +42714,7 @@
     field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
     field public static final int CONNECTION_UNKNOWN = 2147483647; // 0x7fffffff
     field public static final android.os.Parcelable.Creator<android.telephony.CellInfo> CREATOR;
+    field public static final int UNAVAILABLE = 2147483647; // 0x7fffffff
   }
 
   public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable {
@@ -51427,6 +51429,99 @@
 
 package android.view.textclassifier {
 
+  public final class ConversationActions implements android.os.Parcelable {
+    ctor public ConversationActions(java.util.List<android.view.textclassifier.ConversationActions.ConversationAction>);
+    method public int describeContents();
+    method public java.util.List<android.view.textclassifier.ConversationActions.ConversationAction> getConversationActions();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions> CREATOR;
+    field public static final java.lang.String HINT_FOR_IN_APP = "in_app";
+    field public static final java.lang.String HINT_FOR_NOTIFICATION = "notification";
+    field public static final java.lang.String TYPE_CALL_PHONE = "call_phone";
+    field public static final java.lang.String TYPE_CREATE_REMINDER = "create_reminder";
+    field public static final java.lang.String TYPE_OPEN_URL = "open_url";
+    field public static final java.lang.String TYPE_SEND_EMAIL = "send_email";
+    field public static final java.lang.String TYPE_SEND_SMS = "send_sms";
+    field public static final java.lang.String TYPE_SHARE_LOCATION = "share_location";
+    field public static final java.lang.String TYPE_TEXT_REPLY = "text_reply";
+    field public static final java.lang.String TYPE_TRACK_FLIGHT = "track_flight";
+    field public static final java.lang.String TYPE_VIEW_CALENDAR = "view_calendar";
+    field public static final java.lang.String TYPE_VIEW_MAP = "view_map";
+  }
+
+  public static final class ConversationActions.ConversationAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.app.RemoteAction getAction();
+    method public float getConfidenceScore();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getTextReply();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.ConversationAction> CREATOR;
+  }
+
+  public static final class ConversationActions.ConversationAction.Builder {
+    ctor public ConversationActions.ConversationAction.Builder(java.lang.String);
+    method public android.view.textclassifier.ConversationActions.ConversationAction build();
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setAction(android.app.RemoteAction);
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setConfidenceScore(float);
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.ConversationActions.ConversationAction.Builder setTextReply(java.lang.CharSequence);
+  }
+
+  public static final class ConversationActions.Message implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.app.Person getAuthor();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getText();
+    method public java.time.ZonedDateTime getTime();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
+  }
+
+  public static final class ConversationActions.Message.Builder {
+    ctor public ConversationActions.Message.Builder();
+    method public android.view.textclassifier.ConversationActions.Message build();
+    method public android.view.textclassifier.ConversationActions.Message.Builder setAuthor(android.app.Person);
+    method public android.view.textclassifier.ConversationActions.Message.Builder setComposeTime(java.time.ZonedDateTime);
+    method public android.view.textclassifier.ConversationActions.Message.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.ConversationActions.Message.Builder setText(java.lang.CharSequence);
+  }
+
+  public static final class ConversationActions.Request implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
+    method public java.util.List<java.lang.String> getHints();
+    method public int getMaxSuggestions();
+    method public android.view.textclassifier.ConversationActions.TypeConfig getTypeConfig();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Request> CREATOR;
+  }
+
+  public static final class ConversationActions.Request.Builder {
+    ctor public ConversationActions.Request.Builder(java.util.List<android.view.textclassifier.ConversationActions.Message>);
+    method public android.view.textclassifier.ConversationActions.Request build();
+    method public android.view.textclassifier.ConversationActions.Request.Builder setHints(java.util.List<java.lang.String>);
+    method public android.view.textclassifier.ConversationActions.Request.Builder setMaxSuggestions(int);
+    method public android.view.textclassifier.ConversationActions.Request.Builder setTypeConfig(android.view.textclassifier.ConversationActions.TypeConfig);
+  }
+
+  public static final class ConversationActions.TypeConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.Collection<java.lang.String> resolveTypes(java.util.Collection<java.lang.String>);
+    method public boolean shouldIncludeTypesFromTextClassifier();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.TypeConfig> CREATOR;
+  }
+
+  public static final class ConversationActions.TypeConfig.Builder {
+    ctor public ConversationActions.TypeConfig.Builder();
+    method public android.view.textclassifier.ConversationActions.TypeConfig build();
+    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder includeTypesFromTextClassifier(boolean);
+    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setExcludedTypes(java.util.Collection<java.lang.String>);
+    method public android.view.textclassifier.ConversationActions.TypeConfig.Builder setIncludedTypes(java.util.Collection<java.lang.String>);
+  }
+
   public final class SelectionEvent implements android.os.Parcelable {
     method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int);
     method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int, android.view.textclassifier.TextClassification);
@@ -51481,6 +51576,7 @@
     method public float getConfidenceScore(java.lang.String);
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
+    method public android.os.Bundle getExtras();
     method public deprecated android.graphics.drawable.Drawable getIcon();
     method public java.lang.String getId();
     method public deprecated android.content.Intent getIntent();
@@ -51496,6 +51592,7 @@
     method public android.view.textclassifier.TextClassification.Builder addAction(android.app.RemoteAction);
     method public android.view.textclassifier.TextClassification build();
     method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
+    method public android.view.textclassifier.TextClassification.Builder setExtras(android.os.Bundle);
     method public deprecated android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
     method public android.view.textclassifier.TextClassification.Builder setId(java.lang.String);
     method public deprecated android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
@@ -51508,6 +51605,7 @@
     method public int describeContents();
     method public android.os.LocaleList getDefaultLocales();
     method public int getEndIndex();
+    method public android.os.Bundle getExtras();
     method public java.time.ZonedDateTime getReferenceTime();
     method public int getStartIndex();
     method public java.lang.CharSequence getText();
@@ -51519,6 +51617,7 @@
     ctor public TextClassification.Request.Builder(java.lang.CharSequence, int, int);
     method public android.view.textclassifier.TextClassification.Request build();
     method public android.view.textclassifier.TextClassification.Request.Builder setDefaultLocales(android.os.LocaleList);
+    method public android.view.textclassifier.TextClassification.Request.Builder setExtras(android.os.Bundle);
     method public android.view.textclassifier.TextClassification.Request.Builder setReferenceTime(java.time.ZonedDateTime);
   }
 
@@ -51563,6 +51662,7 @@
     method public default int getMaxGenerateLinksTextLength();
     method public default boolean isDestroyed();
     method public default void onSelectionEvent(android.view.textclassifier.SelectionEvent);
+    method public default android.view.textclassifier.ConversationActions suggestConversationActions(android.view.textclassifier.ConversationActions.Request);
     method public default android.view.textclassifier.TextSelection suggestSelection(android.view.textclassifier.TextSelection.Request);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
     field public static final java.lang.String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
diff --git a/api/system-current.txt b/api/system-current.txt
index 7f58330..b1cbfa6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -93,6 +93,7 @@
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
     field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
+    field public static final java.lang.String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY";
     field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
     field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
@@ -870,9 +871,11 @@
     method public int getAppStandbyBucket(java.lang.String);
     method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
     method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
+    method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
     method public void unregisterAppUsageObserver(int);
+    method public void unregisterUsageSessionObserver(int);
     method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
     field public static final java.lang.String EXTRA_OBSERVER_ID = "android.app.usage.extra.OBSERVER_ID";
     field public static final java.lang.String EXTRA_TIME_LIMIT = "android.app.usage.extra.TIME_LIMIT";
@@ -6830,6 +6833,14 @@
 
 }
 
+package android.view.accessibility {
+
+  public final class AccessibilityManager {
+    method public void performAccessibilityShortcut();
+  }
+
+}
+
 package android.webkit {
 
   public abstract class CookieManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 8f08c71..d453395 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -960,6 +960,7 @@
 
   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 ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -1611,6 +1612,17 @@
 
 package android.view.accessibility {
 
+  public final class AccessibilityManager {
+    method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
+    method public java.lang.String getAccessibilityShortcutService();
+    method public void performAccessibilityShortcut();
+    method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
+  }
+
+  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
+    method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
+  }
+
   public class AccessibilityNodeInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
     method public void writeToParcelNoRecycle(android.os.Parcel, int);
diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp
new file mode 100644
index 0000000..2a5ec5b
--- /dev/null
+++ b/cmds/incident/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+cc_binary {
+    name: "incident",
+
+    srcs: [
+        "main.cpp",
+        ":incident_sections",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libincident",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+}
+
+genrule {
+    name: "incident_sections",
+    tools: ["incident-section-gen"],
+    out: ["incident_sections.cpp"],
+    cmd: "$(location incident-section-gen) incident > $(out)",
+}
diff --git a/cmds/incident/Android.mk b/cmds/incident/Android.mk
deleted file mode 100644
index 8615f9b..0000000
--- a/cmds/incident/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-        main.cpp
-
-LOCAL_MODULE := incident
-
-LOCAL_SHARED_LIBRARIES := \
-        libbase \
-        libbinder \
-        libcutils \
-        liblog \
-        libutils \
-        libincident
-
-LOCAL_CFLAGS += \
-        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-gen_src_dir := $(local-generated-sources-dir)
-
-gen := $(gen_src_dir)/incident_sections.cpp
-$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-$(gen): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/incident-section-gen incident > $@
-$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(gen)
-
-gen_src_dir:=
-gen:=
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
new file mode 100644
index 0000000..1e970f4
--- /dev/null
+++ b/cmds/incidentd/Android.bp
@@ -0,0 +1,117 @@
+// 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.
+
+// =========
+// incidentd
+// =========
+
+cc_binary {
+    name: "incidentd",
+
+    srcs: [
+        "src/**/*.cpp",
+        ":incidentd_section_list",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+
+        // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+        "-Wno-error=implicit-fallthrough",
+
+        // optimize for size (protobuf glop can get big)
+        "-Os",
+        //"-g",
+        //"-O0",
+    ],
+
+    local_include_dirs: ["src"],
+    generated_headers: ["gen-platform-proto-constants"],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libdebuggerd_client",
+        "libdumputils",
+        "libincident",
+        "liblog",
+        "libprotoutil",
+        "libservices",
+        "libutils",
+    ],
+
+    init_rc: ["incidentd.rc"],
+}
+
+// ==============
+// incidentd_test
+// ==============
+
+cc_test {
+    name: "incidentd_test",
+    test_suites: ["device-tests"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+
+        // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+        "-Wno-error=implicit-fallthrough",
+    ],
+
+    local_include_dirs: ["src"],
+    generated_headers: ["gen-platform-proto-constants"],
+
+    srcs: [
+        "tests/**/*.cpp",
+        "src/PrivacyBuffer.cpp",
+        "src/FdBuffer.cpp",
+        "src/Privacy.cpp",
+        "src/Reporter.cpp",
+        "src/Section.cpp",
+        "src/Throttler.cpp",
+        "src/incidentd_util.cpp",
+        "src/report_directory.cpp",
+    ],
+
+    data: ["testdata/**/*"],
+
+    static_libs: ["libgmock"],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libdebuggerd_client",
+        "libdumputils",
+        "libincident",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libprotoutil",
+        "libservices",
+        "libutils",
+    ],
+}
+
+genrule {
+    name: "incidentd_section_list",
+    tools: ["incident-section-gen"],
+    out: ["section_list.cpp"],
+    cmd: "$(location incident-section-gen) incidentd > $(out)",
+}
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
deleted file mode 100644
index eba5586..0000000
--- a/cmds/incidentd/Android.mk
+++ /dev/null
@@ -1,156 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# proto files used in incidentd to generate cppstream proto headers.
-PROTO_FILES:= \
-        frameworks/base/core/proto/android/os/backtrace.proto \
-        frameworks/base/core/proto/android/os/data.proto \
-        frameworks/base/core/proto/android/util/log.proto
-
-# ========= #
-# incidentd #
-# ========= #
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incidentd
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under, src) \
-
-LOCAL_CFLAGS += \
-        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-
-ifeq (debug,)
-    LOCAL_CFLAGS += \
-            -g -O0
-else
-    # optimize for size (protobuf glop can get big)
-    LOCAL_CFLAGS += \
-            -Os
-endif
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_SHARED_LIBRARIES := \
-        libbase \
-        libbinder \
-        libdebuggerd_client \
-        libdumputils \
-        libincident \
-        liblog \
-        libprotoutil \
-        libservices \
-        libutils
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-gen_src_dir := $(local-generated-sources-dir)
-
-# generate section_list.cpp
-GEN_LIST := $(gen_src_dir)/src/section_list.cpp
-$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-$(GEN_LIST): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/incident-section-gen incidentd > $@
-$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_LIST)
-GEN_LIST:=
-
-# generate cppstream proto, add proto files to PROTO_FILES
-GEN_PROTO := $(gen_src_dir)/proto.timestamp
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
-$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
-$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
-        --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
-        $(PROTO_FILES) \
-    && touch $@
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
-GEN_PROTO:=
-
-gen_src_dir:=
-
-LOCAL_INIT_RC := incidentd.rc
-
-include $(BUILD_EXECUTABLE)
-
-# ============== #
-# incidentd_test #
-# ============== #
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incidentd_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := -Werror -Wall -Wno-unused-variable -Wunused-parameter
-
-# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \
-    src/PrivacyBuffer.cpp \
-    src/FdBuffer.cpp \
-    src/Privacy.cpp \
-    src/Reporter.cpp \
-    src/Section.cpp \
-    src/Throttler.cpp \
-    src/incidentd_util.cpp \
-    src/report_directory.cpp \
-
-LOCAL_STATIC_LIBRARIES := \
-    libgmock \
-
-LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    libbinder \
-    libdebuggerd_client \
-    libdumputils \
-    libincident \
-    liblog \
-    libprotobuf-cpp-lite \
-    libprotoutil \
-    libservices \
-    libutils \
-
-LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, testdata)
-
-LOCAL_MODULE_CLASS := NATIVE_TESTS
-gen_src_dir := $(local-generated-sources-dir)
-# generate cppstream proto for testing
-GEN_PROTO := $(gen_src_dir)/test.proto.timestamp
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES)
-$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir)
-$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \
-    $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \
-        --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \
-        $(PROTO_FILES) \
-    && touch $@
-$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc
-	$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_PROTO)
-GEN_PROTO:=
-
-gen_src_dir:=
-
-include $(BUILD_NATIVE_TEST)
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 94203f4f..02dde5a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2015 The Android Open Source Project
+// Copyright (C) 2017 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -42,6 +42,277 @@
 
 }
 
+cc_defaults {
+    name: "statsd_defaults",
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+    },
+
+    srcs: [
+        ":statsd_aidl",
+        "src/statsd_config.proto",
+        "src/FieldValue.cpp",
+        "src/hash.cpp",
+        "src/stats_log_util.cpp",
+        "src/anomaly/AlarmMonitor.cpp",
+        "src/anomaly/AlarmTracker.cpp",
+        "src/anomaly/AnomalyTracker.cpp",
+        "src/anomaly/DurationAnomalyTracker.cpp",
+        "src/anomaly/subscriber_util.cpp",
+        "src/condition/CombinationConditionTracker.cpp",
+        "src/condition/condition_util.cpp",
+        "src/condition/SimpleConditionTracker.cpp",
+        "src/condition/ConditionWizard.cpp",
+        "src/condition/StateTracker.cpp",
+        "src/config/ConfigKey.cpp",
+        "src/config/ConfigListener.cpp",
+        "src/config/ConfigManager.cpp",
+        "src/external/Perfetto.cpp",
+        "src/external/Perfprofd.cpp",
+        "src/external/StatsPuller.cpp",
+        "src/external/StatsCompanionServicePuller.cpp",
+        "src/external/SubsystemSleepStatePuller.cpp",
+        "src/external/ResourceHealthManagerPuller.cpp",
+        "src/external/ResourceThermalManagerPuller.cpp",
+        "src/external/StatsPullerManager.cpp",
+        "src/external/puller_util.cpp",
+        "src/logd/LogEvent.cpp",
+        "src/logd/LogListener.cpp",
+        "src/matchers/CombinationLogMatchingTracker.cpp",
+        "src/matchers/EventMatcherWizard.cpp",
+        "src/matchers/matcher_util.cpp",
+        "src/matchers/SimpleLogMatchingTracker.cpp",
+        "src/metrics/MetricProducer.cpp",
+        "src/metrics/EventMetricProducer.cpp",
+        "src/metrics/CountMetricProducer.cpp",
+        "src/metrics/DurationMetricProducer.cpp",
+        "src/metrics/duration_helper/OringDurationTracker.cpp",
+        "src/metrics/duration_helper/MaxDurationTracker.cpp",
+        "src/metrics/ValueMetricProducer.cpp",
+        "src/metrics/GaugeMetricProducer.cpp",
+        "src/metrics/MetricsManager.cpp",
+        "src/metrics/metrics_manager_util.cpp",
+        "src/packages/UidMap.cpp",
+        "src/storage/StorageManager.cpp",
+        "src/StatsLogProcessor.cpp",
+        "src/StatsService.cpp",
+        "src/statscompanion_util.cpp",
+        "src/subscriber/IncidentdReporter.cpp",
+        "src/subscriber/SubscriberReporter.cpp",
+        "src/HashableDimensionKey.cpp",
+        "src/guardrail/StatsdStats.cpp",
+        "src/socket/StatsSocketListener.cpp",
+        "src/shell/ShellSubscriber.cpp",
+        "src/shell/shell_config.proto",
+
+        ":perfprofd_aidl",
+    ],
+
+    local_include_dirs: [
+        "src",
+    ],
+
+    static_libs: [
+        "libhealthhalutils",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libincident",
+        "liblog",
+        "libutils",
+        "libservices",
+        "libprotoutil",
+        "libstatslog",
+        "libhardware",
+        "libhardware_legacy",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "android.frameworks.stats@1.0",
+        "android.hardware.health@2.0",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.thermal@1.0",
+        "libpackagelistparser",
+        "libsysutils",
+        "libcutils",
+    ],
+}
+
+// =========
+// statsd
+// =========
+
+cc_binary {
+    name: "statsd",
+    defaults: ["statsd_defaults"],
+
+    srcs: ["src/main.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+        // optimize for size (protobuf glop can get big)
+        "-Os",
+        // "-g",
+        // "-O0",
+    ],
+
+    product_variables: {
+        eng: {
+            // Enable sanitizer ONLY on eng builds
+            //sanitize: {
+            //    address: true,
+            //},
+        },
+        debuggable: {
+            // Add a flag to enable stats log printing from statsd on debug builds.
+            cflags: ["-DVERY_VERBOSE_PRINTING"],
+        },
+    },
+
+    proto: {
+        type: "lite",
+    },
+
+    shared_libs: ["libgtest_prod"],
+
+    init_rc: ["statsd.rc"],
+}
+
+// ==============
+// statsd_test
+// ==============
+
+cc_test {
+    name: "statsd_test",
+    defaults: ["statsd_defaults"],
+    test_suites: ["device-tests"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wno-unused-function",
+        "-Wno-unused-parameter",
+    ],
+
+    srcs: [
+        "src/atom_field_options.proto",
+        "src/atoms.proto",
+        "src/stats_log.proto",
+        "src/shell/shell_data.proto",
+        "tests/AlarmMonitor_test.cpp",
+        "tests/anomaly/AlarmTracker_test.cpp",
+        "tests/anomaly/AnomalyTracker_test.cpp",
+        "tests/ConfigManager_test.cpp",
+        "tests/external/puller_util_test.cpp",
+        "tests/indexed_priority_queue_test.cpp",
+        "tests/LogEntryMatcher_test.cpp",
+        "tests/LogEvent_test.cpp",
+        "tests/MetricsManager_test.cpp",
+        "tests/StatsLogProcessor_test.cpp",
+        "tests/StatsService_test.cpp",
+        "tests/UidMap_test.cpp",
+        "tests/FieldValue_test.cpp",
+        "tests/condition/CombinationConditionTracker_test.cpp",
+        "tests/condition/SimpleConditionTracker_test.cpp",
+        "tests/condition/StateTracker_test.cpp",
+        "tests/metrics/OringDurationTracker_test.cpp",
+        "tests/metrics/MaxDurationTracker_test.cpp",
+        "tests/metrics/CountMetricProducer_test.cpp",
+        "tests/metrics/DurationMetricProducer_test.cpp",
+        "tests/metrics/EventMetricProducer_test.cpp",
+        "tests/metrics/ValueMetricProducer_test.cpp",
+        "tests/metrics/GaugeMetricProducer_test.cpp",
+        "tests/guardrail/StatsdStats_test.cpp",
+        "tests/metrics/metrics_test_helper.cpp",
+        "tests/statsd_test_util.cpp",
+        "tests/e2e/WakelockDuration_e2e_test.cpp",
+        "tests/e2e/MetricActivation_e2e_test.cpp",
+        "tests/e2e/MetricConditionLink_e2e_test.cpp",
+        "tests/e2e/Alarm_e2e_test.cpp",
+        "tests/e2e/Attribution_e2e_test.cpp",
+        "tests/e2e/GaugeMetric_e2e_push_test.cpp",
+        "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
+        "tests/e2e/ValueMetric_pull_e2e_test.cpp",
+        "tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp",
+        "tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp",
+        "tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp",
+        "tests/e2e/Anomaly_count_e2e_test.cpp",
+        "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
+        "tests/e2e/ConfigTtl_e2e_test.cpp",
+        "tests/e2e/PartialBucket_e2e_test.cpp",
+        "tests/shell/ShellSubscriber_test.cpp",
+    ],
+
+    static_libs: [
+        "libgmock",
+        "libplatformprotos",
+    ],
+
+    proto: {
+        type: "full",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    shared_libs: ["libprotobuf-cpp-full"],
+
+}
+
+//#############################
+// statsd micro benchmark
+//#############################
+
+cc_benchmark {
+    name: "statsd_benchmark",
+    defaults: ["statsd_defaults"],
+
+    srcs: [
+        "src/atom_field_options.proto",
+        "src/atoms.proto",
+        "src/stats_log.proto",
+        "benchmark/main.cpp",
+        "benchmark/hello_world_benchmark.cpp",
+        "benchmark/log_event_benchmark.cpp",
+        "benchmark/stats_write_benchmark.cpp",
+        "benchmark/filter_value_benchmark.cpp",
+        "benchmark/get_dimensions_for_condition_benchmark.cpp",
+        "benchmark/metric_util.cpp",
+        "benchmark/duration_metric_benchmark.cpp",
+    ],
+
+    proto: {
+        type: "full",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+        "-Wno-unused-function",
+
+        // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
+        "-Wno-varargs"
+    ],
+
+    static_libs: [
+        "libplatformprotos",
+    ],
+
+    shared_libs: [
+        "libgtest_prod",
+        "libstatslog",
+        "libprotobuf-cpp-full",
+    ],
+}
 
 // ====  java proto device library (for test only)  ==============================
 java_library {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
deleted file mode 100644
index 5818f5d..0000000
--- a/cmds/statsd/Android.mk
+++ /dev/null
@@ -1,319 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-statsd_common_src := \
-    ../../core/java/android/os/IStatsCompanionService.aidl \
-    ../../core/java/android/os/IStatsManager.aidl \
-    src/statsd_config.proto \
-    src/FieldValue.cpp \
-    src/hash.cpp \
-    src/stats_log_util.cpp \
-    src/anomaly/AlarmMonitor.cpp \
-    src/anomaly/AlarmTracker.cpp \
-    src/anomaly/AnomalyTracker.cpp \
-    src/anomaly/DurationAnomalyTracker.cpp \
-    src/anomaly/subscriber_util.cpp \
-    src/condition/CombinationConditionTracker.cpp \
-    src/condition/condition_util.cpp \
-    src/condition/SimpleConditionTracker.cpp \
-    src/condition/ConditionWizard.cpp \
-    src/condition/StateTracker.cpp \
-    src/config/ConfigKey.cpp \
-    src/config/ConfigListener.cpp \
-    src/config/ConfigManager.cpp \
-    src/external/Perfetto.cpp \
-    src/external/Perfprofd.cpp \
-    src/external/StatsPuller.cpp \
-    src/external/StatsCompanionServicePuller.cpp \
-    src/external/SubsystemSleepStatePuller.cpp \
-    src/external/ResourceHealthManagerPuller.cpp \
-    src/external/ResourceThermalManagerPuller.cpp \
-    src/external/StatsPullerManager.cpp \
-    src/external/puller_util.cpp \
-    src/logd/LogEvent.cpp \
-    src/logd/LogListener.cpp \
-    src/matchers/CombinationLogMatchingTracker.cpp \
-    src/matchers/EventMatcherWizard.cpp \
-    src/matchers/matcher_util.cpp \
-    src/matchers/SimpleLogMatchingTracker.cpp \
-    src/metrics/MetricProducer.cpp \
-    src/metrics/EventMetricProducer.cpp \
-    src/metrics/CountMetricProducer.cpp \
-    src/metrics/DurationMetricProducer.cpp \
-    src/metrics/duration_helper/OringDurationTracker.cpp \
-    src/metrics/duration_helper/MaxDurationTracker.cpp \
-    src/metrics/ValueMetricProducer.cpp \
-    src/metrics/GaugeMetricProducer.cpp \
-    src/metrics/MetricsManager.cpp \
-    src/metrics/metrics_manager_util.cpp \
-    src/packages/UidMap.cpp \
-    src/storage/StorageManager.cpp \
-    src/StatsLogProcessor.cpp \
-    src/StatsService.cpp \
-    src/statscompanion_util.cpp \
-    src/subscriber/IncidentdReporter.cpp \
-    src/subscriber/SubscriberReporter.cpp \
-    src/HashableDimensionKey.cpp \
-    src/guardrail/StatsdStats.cpp \
-    src/socket/StatsSocketListener.cpp \
-    src/shell/ShellSubscriber.cpp \
-    src/shell/shell_config.proto
-
-# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
-statsd_common_src += \
-    ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl
-
-statsd_common_c_includes := \
-    $(LOCAL_PATH)/src \
-    $(LOCAL_PATH)/../../libs/services/include
-
-statsd_common_aidl_includes := \
-    $(LOCAL_PATH)/../../core/java
-
-statsd_common_static_libraries := \
-    libhealthhalutils
-
-statsd_common_shared_libraries := \
-    libbase \
-    libbinder \
-    libincident \
-    liblog \
-    libutils \
-    libservices \
-    libprotoutil \
-    libstatslog \
-    libhardware \
-    libhardware_legacy \
-    libhidlbase \
-    libhidltransport \
-    libhwbinder \
-    android.frameworks.stats@1.0 \
-    android.hardware.health@2.0 \
-    android.hardware.power@1.0 \
-    android.hardware.power@1.1 \
-    android.hardware.thermal@1.0 \
-    libpackagelistparser \
-    libsysutils \
-    libcutils
-
-# =========
-# statsd
-# =========
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := statsd
-
-LOCAL_SRC_FILES := \
-    $(statsd_common_src) \
-    src/main.cpp
-
-LOCAL_CFLAGS += \
-    -Wall \
-    -Wextra \
-    -Werror \
-    -Wno-unused-parameter
-
-ifeq (debug,)
-    LOCAL_CFLAGS += \
-            -g -O0
-else
-    # optimize for size (protobuf glop can get big)
-    LOCAL_CFLAGS += \
-            -Os
-endif
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-
-LOCAL_STATIC_LIBRARIES := $(statsd_common_static_libraries)
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-    libgtest_prod
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-# Enable sanitizer ONLY on eng builds.
-#ifeq ($(TARGET_BUILD_VARIANT),eng)
-#    LOCAL_CLANG := true
-#    LOCAL_SANITIZE := address
-#endif
-
-# Add a flag to enable stats log printing from statsd on debug builds.
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-    LOCAL_CFLAGS += \
-        -DVERY_VERBOSE_PRINTING
-endif
-
-LOCAL_INIT_RC := statsd.rc
-
-include $(BUILD_EXECUTABLE)
-
-
-# ==============
-# statsd_test
-# ==============
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := statsd_test
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-
-LOCAL_CFLAGS += \
-    -Wall \
-    -Werror \
-    -Wno-missing-field-initializers \
-    -Wno-unused-variable \
-    -Wno-unused-function \
-    -Wno-unused-parameter
-
-LOCAL_SRC_FILES := \
-    $(statsd_common_src) \
-    src/atom_field_options.proto \
-    src/atoms.proto \
-    src/stats_log.proto \
-    src/shell/shell_data.proto \
-    tests/AlarmMonitor_test.cpp \
-    tests/anomaly/AlarmTracker_test.cpp \
-    tests/anomaly/AnomalyTracker_test.cpp \
-    tests/ConfigManager_test.cpp \
-    tests/external/puller_util_test.cpp \
-    tests/indexed_priority_queue_test.cpp \
-    tests/LogEntryMatcher_test.cpp \
-    tests/LogEvent_test.cpp \
-    tests/MetricsManager_test.cpp \
-    tests/StatsLogProcessor_test.cpp \
-    tests/StatsService_test.cpp \
-    tests/UidMap_test.cpp \
-    tests/FieldValue_test.cpp \
-    tests/condition/CombinationConditionTracker_test.cpp \
-    tests/condition/SimpleConditionTracker_test.cpp \
-    tests/condition/StateTracker_test.cpp \
-    tests/metrics/OringDurationTracker_test.cpp \
-    tests/metrics/MaxDurationTracker_test.cpp \
-    tests/metrics/CountMetricProducer_test.cpp \
-    tests/metrics/DurationMetricProducer_test.cpp \
-    tests/metrics/EventMetricProducer_test.cpp \
-    tests/metrics/ValueMetricProducer_test.cpp \
-    tests/metrics/GaugeMetricProducer_test.cpp \
-    tests/guardrail/StatsdStats_test.cpp \
-    tests/metrics/metrics_test_helper.cpp \
-    tests/statsd_test_util.cpp \
-    tests/e2e/WakelockDuration_e2e_test.cpp \
-    tests/e2e/MetricActivation_e2e_test.cpp \
-    tests/e2e/MetricConditionLink_e2e_test.cpp \
-    tests/e2e/Alarm_e2e_test.cpp \
-    tests/e2e/Attribution_e2e_test.cpp \
-    tests/e2e/GaugeMetric_e2e_push_test.cpp \
-    tests/e2e/GaugeMetric_e2e_pull_test.cpp \
-    tests/e2e/ValueMetric_pull_e2e_test.cpp \
-    tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
-    tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
-    tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
-    tests/e2e/Anomaly_count_e2e_test.cpp \
-    tests/e2e/Anomaly_duration_sum_e2e_test.cpp \
-    tests/e2e/ConfigTtl_e2e_test.cpp \
-    tests/e2e/PartialBucket_e2e_test.cpp \
-    tests/shell/ShellSubscriber_test.cpp
-
-LOCAL_STATIC_LIBRARIES := \
-    $(statsd_common_static_libraries) \
-    libgmock \
-    libplatformprotos
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-
-LOCAL_PROTOC_FLAGS := \
-    -Iexternal/protobuf/src
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-                        libprotobuf-cpp-full
-
-include $(BUILD_NATIVE_TEST)
-
-##############################
-# statsd micro benchmark
-##############################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := statsd_benchmark
-
-LOCAL_SRC_FILES := $(statsd_common_src) \
-                    src/atom_field_options.proto \
-                    src/atoms.proto \
-                    src/stats_log.proto \
-                   benchmark/main.cpp \
-                   benchmark/hello_world_benchmark.cpp \
-                   benchmark/log_event_benchmark.cpp \
-                   benchmark/stats_write_benchmark.cpp \
-                   benchmark/filter_value_benchmark.cpp \
-                   benchmark/get_dimensions_for_condition_benchmark.cpp \
-                   benchmark/metric_util.cpp \
-                   benchmark/duration_metric_benchmark.cpp
-
-LOCAL_STATIC_LIBRARIES := \
-    $(statsd_common_static_libraries)
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-
-LOCAL_PROTOC_FLAGS := \
-    -Iexternal/protobuf/src
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-                        libprotobuf-cpp-full
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    platformprotoslite
-
-LOCAL_C_INCLUDES := $(statsd_common_c_includes)
-
-LOCAL_CFLAGS := -Wall \
-                -Werror \
-                -Wno-unused-parameter \
-                -Wno-unused-variable \
-                -Wno-unused-function \
-
-# Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-LOCAL_CFLAGS += -Wno-varargs
-
-LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
-
-LOCAL_STATIC_LIBRARIES := \
-    $(statsd_common_static_libraries) \
-    libplatformprotos
-
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
-    libgtest_prod \
-    libstatslog
-
-LOCAL_MODULE_TAGS := eng tests
-
-include $(BUILD_NATIVE_BENCHMARK)
-
-
-statsd_common_src:=
-statsd_common_aidl_includes:=
-statsd_common_c_includes:=
-statsd_common_static_libraries:=
-statsd_common_shared_libraries:=
-
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/statsd/tools/Android.mk b/cmds/statsd/tools/Android.mk
deleted file mode 100644
index 7253c96..0000000
--- a/cmds/statsd/tools/Android.mk
+++ /dev/null
@@ -1,20 +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.
-#
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-#Include the sub-makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/Android.bp b/cmds/statsd/tools/dogfood/Android.bp
new file mode 100644
index 0000000..bb494a6
--- /dev/null
+++ b/cmds/statsd/tools/dogfood/Android.bp
@@ -0,0 +1,37 @@
+// 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.
+//
+//
+
+android_app {
+    name: "StatsdDogfood",
+    platform_apis: true,
+
+    srcs: ["src/**/*.java"],
+
+    resource_dirs: ["res"],
+    static_libs: [
+        "platformprotoslite",
+        "statsdprotolite",
+    ],
+
+    privileged: true,
+    dex_preopt: {
+        enabled: false,
+    },
+    certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
deleted file mode 100644
index baf235b..0000000
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ /dev/null
@@ -1,33 +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.
-#
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := StatsdDogfood
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
-                               statsdprotolite
-
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_DEX_PREOPT := false
-LOCAL_CERTIFICATE := platform
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/loadtest/Android.bp b/cmds/statsd/tools/loadtest/Android.bp
new file mode 100644
index 0000000..bf87fc5
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/Android.bp
@@ -0,0 +1,37 @@
+// 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.
+//
+//
+
+android_app {
+    name: "StatsdLoadtest",
+    platform_apis: true,
+
+    srcs: ["src/**/*.java"],
+
+    resource_dirs: ["res"],
+    static_libs: [
+        "platformprotoslite",
+        "statsdprotolite",
+    ],
+
+    certificate: "platform",
+    privileged: true,
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
deleted file mode 100644
index 219cd95..0000000
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ /dev/null
@@ -1,33 +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.
-#
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := StatsdLoadtest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
-                               statsdprotolite
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8f82ee7..a4f1db3 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -34,6 +34,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.util.ArrayMap;
 
 import com.android.internal.app.IAppOpsActiveCallback;
@@ -1584,6 +1585,26 @@
      * @hide
      */
     public static int opToDefaultMode(int op) {
+        // STOPSHIP b/118520006: Hardcode the default values once the feature is stable.
+        switch (op) {
+            // SMS permissions
+            case AppOpsManager.OP_SEND_SMS:
+            case AppOpsManager.OP_RECEIVE_SMS:
+            case AppOpsManager.OP_READ_SMS:
+            case AppOpsManager.OP_RECEIVE_WAP_PUSH:
+            case AppOpsManager.OP_RECEIVE_MMS:
+            case AppOpsManager.OP_READ_CELL_BROADCASTS:
+            // CallLog permissions
+            case AppOpsManager.OP_READ_CALL_LOG:
+            case AppOpsManager.OP_WRITE_CALL_LOG:
+            case AppOpsManager.OP_PROCESS_OUTGOING_CALLS: {
+                // ActivityThread.currentApplication() is never null
+                if (Settings.Global.getInt(ActivityThread.currentApplication().getContentResolver(),
+                        Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1) {
+                    return AppOpsManager.MODE_DEFAULT;
+                }
+            }
+        }
         return sOpDefaultMode[op];
     }
 
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 9713527..4d52263 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -51,4 +51,8 @@
     void registerAppUsageObserver(int observerId, in String[] packages, long timeLimitMs,
             in PendingIntent callback, String callingPackage);
     void unregisterAppUsageObserver(int observerId, String callingPackage);
+    void registerUsageSessionObserver(int sessionObserverId, in String[] observed, long timeLimitMs,
+            long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
+            in PendingIntent sessionEndCallbackIntent, String callingPackage);
+    void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
 }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index dbb00eb..6d7400e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -595,7 +596,7 @@
      *                       exceeded by the group of apps. The delivered Intent will also contain
      *                       the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
      *                       {@link #EXTRA_TIME_USED}. Cannot be null.
-     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
      *                           is not the profile owner of this user.
      */
     @SystemApi
@@ -616,7 +617,7 @@
      * to any observer registered by this application. Unregistering an observer that was already
      * unregistered or never registered will have no effect.
      * @param observerId The id of the observer that was previously registered.
-     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or is
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and is
      *                           not the profile owner of this user.
      */
     @SystemApi
@@ -629,6 +630,81 @@
         }
     }
 
+    /**
+     * Register a usage session observer that receives a callback on the provided {@code
+     * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds
+     * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has
+     * been reached, the usage session observer will receive a callback on the provided {@code
+     * sessionEndCallbackIntent} when the usage session ends. Registering another session
+     * observer against a {@code sessionObserverId} that has already been registered will
+     * override the previous session observer.
+     *
+     * @param sessionObserverId A unique id associated with the group of apps to be
+     *                          monitored. There can be multiple groups with common
+     *                          packages and different time limits.
+     * @param packages The list of packages to observe for foreground activity time. Cannot be null
+     *                 and must include at least one package.
+     * @param timeLimit The total time the set of apps can be used continuously before the {@code
+     *                  limitReachedCallbackIntent} is delivered. Must be at least one minute.
+     * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
+     * @param sessionThresholdTime The time that can take place between usage sessions before the
+     *                             next session is considered a new session. Must be non-negative.
+     * @param sessionThresholdTimeUnit The unit for time specified in {@code sessionThreshold}.
+     *                                 Cannot be null.
+     * @param limitReachedCallbackIntent The {@link PendingIntent} that will be dispatched when the
+     *                                   time limit is exceeded by the group of apps. The delivered
+     *                                   Intent will also contain the extras {@link
+     *                                   #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and {@link
+     *                                   #EXTRA_TIME_USED}. Cannot be null.
+     * @param sessionEndCallbackIntent The {@link PendingIntent}  that will be dispatched when the
+     *                                 session has ended after the time limit has been exceeded. The
+     *                                 session is considered at its end after the {@code observed}
+     *                                 usage has stopped and an additional {@code
+     *                                 sessionThresholdTime} has passed. The delivered Intent will
+     *                                 also contain the extras {@link #EXTRA_OBSERVER_ID} and {@link
+     *                                 #EXTRA_TIME_USED}. Can be null.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
+     *                           is not the profile owner of this user.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+    public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages,
+            long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime,
+            @NonNull TimeUnit sessionThresholdTimeUnit,
+            @NonNull PendingIntent limitReachedCallbackIntent,
+            @Nullable PendingIntent sessionEndCallbackIntent) {
+        try {
+            mService.registerUsageSessionObserver(sessionObserverId, packages,
+                    timeUnit.toMillis(timeLimit),
+                    sessionThresholdTimeUnit.toMillis(sessionThresholdTime),
+                    limitReachedCallbackIntent, sessionEndCallbackIntent,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister the usage session observer specified by the {@code sessionObserverId}. This will
+     * only apply to any app session observer registered by this application. Unregistering an
+     * observer that was already unregistered or never registered will have no effect.
+     *
+     * @param sessionObserverId The id of the observer that was previously registered.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission and
+     *                           is not the profile owner of this user.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+    public void unregisterUsageSessionObserver(int sessionObserverId) {
+        try {
+            mService.unregisterUsageSessionObserver(sessionObserverId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public static String reasonToString(int standbyReason) {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index e270fc2..5447f59 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -811,14 +811,19 @@
      * packet needs to be subtracted from the root UID on the base interface both for tx
      * and rx traffic (http://b/12249687, http:/b/33681750).
      *
+     * As for eBPF, the per uid stats is collected by different hook, the rx packets on base
+     * interface will not be counted. Thus, the adjustment on root uid is only needed in tx
+     * direction.
+     *
      * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
      * {@code ConcurrentHashMap}
      * @param baseTraffic Traffic on the base interfaces. Will be mutated.
      * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+     * @param useBpfStats True if eBPF is in use.
      */
     public static void apply464xlatAdjustments(NetworkStats baseTraffic,
-            NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
+            NetworkStats stackedTraffic, Map<String, String> stackedIfaces, boolean useBpfStats) {
         // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
         // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
         final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
@@ -837,15 +842,20 @@
                 continue;
             }
             // Subtract any 464lat traffic seen for the root UID on the current base interface.
+            // However, for eBPF, the per uid stats is collected by different hook, the rx packets
+            // on base interface will not be counted. Thus, the adjustment on root uid is only
+            // needed in tx direction.
             adjust.iface = baseIface;
-            adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+            if (!useBpfStats) {
+                adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+                adjust.rxPackets = -entry.rxPackets;
+            }
             adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
-            adjust.rxPackets = -entry.rxPackets;
             adjust.txPackets = -entry.txPackets;
             adjustments.combineValues(adjust);
 
-            // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
-            // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
+            // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
+            // sent on the stacked interface with prefix "v4-" and drops the IPv6 header size after
             // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
             // difference for all packets (http://b/12249687, http:/b/33681750).
             entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
@@ -864,8 +874,8 @@
      * base and stacked traffic.
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
      */
-    public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
-        apply464xlatAdjustments(this, this, stackedIfaces);
+    public void apply464xlatAdjustments(Map<String, String> stackedIfaces, boolean useBpfStats) {
+        apply464xlatAdjustments(this, this, stackedIfaces, useBpfStats);
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 45e6343..f58624f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6038,6 +6038,7 @@
          * shortcut. Must be its flattened {@link ComponentName}.
          * @hide
          */
+        @TestApi
         public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
                 "accessibility_shortcut_target_service";
 
@@ -7396,8 +7397,8 @@
                 "call_screening_default_component";
 
         /**
-         * Specifies the package name currently configured to be the default application to perform
-         * the user-defined call redirection service with Telecom.
+         * Specifies the component name currently configured to be the default application to
+         * perform the user-defined call redirection service with Telecom.
          * @hide
          */
         @UnsupportedAppUsage
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 3ab8a0a..e5fd292 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1269,29 +1269,47 @@
      * scrolling on the specified line.
      */
     public float getLineLeft(int line) {
-        int dir = getParagraphDirection(line);
-        Alignment align = getParagraphAlignment(line);
+        final int dir = getParagraphDirection(line);
+        final Alignment align = getParagraphAlignment(line);
 
-        if (align == Alignment.ALIGN_LEFT) {
-            return 0;
-        } else if (align == Alignment.ALIGN_NORMAL) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return getParagraphRight(line) - getLineMax(line);
-            else
-                return 0;
-        } else if (align == Alignment.ALIGN_RIGHT) {
-            return mWidth - getLineMax(line);
-        } else if (align == Alignment.ALIGN_OPPOSITE) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return 0;
-            else
+        // First convert combinations of alignment and direction settings to
+        // three basic cases: ALIGN_LEFT, ALIGN_RIGHT and ALIGN_CENTER.
+        // For unexpected cases, it will fallback to ALIGN_LEFT.
+        final Alignment resultAlign;
+        switch(align) {
+            case ALIGN_NORMAL:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_RIGHT : Alignment.ALIGN_LEFT;
+                break;
+            case ALIGN_OPPOSITE:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_LEFT : Alignment.ALIGN_RIGHT;
+                break;
+            case ALIGN_CENTER:
+                resultAlign = Alignment.ALIGN_CENTER;
+                break;
+            case ALIGN_RIGHT:
+                resultAlign = Alignment.ALIGN_RIGHT;
+                break;
+            default: /* align == Alignment.ALIGN_LEFT */
+                resultAlign = Alignment.ALIGN_LEFT;
+        }
+
+        // Here we must use getLineMax() to do the computation, because it maybe overridden by
+        // derived class. And also note that line max equals the width of the text in that line
+        // plus the leading margin.
+        switch (resultAlign) {
+            case ALIGN_CENTER:
+                final int left = getParagraphLeft(line);
+                final float max = getLineMax(line);
+                // This computation only works when mWidth equals leadingMargin plus
+                // the width of text in this line. If this condition doesn't meet anymore,
+                // please change here too.
+                return (float) Math.floor(left + (mWidth - max) / 2);
+            case ALIGN_RIGHT:
                 return mWidth - getLineMax(line);
-        } else { /* align == Alignment.ALIGN_CENTER */
-            int left = getParagraphLeft(line);
-            int right = getParagraphRight(line);
-            int max = ((int) getLineMax(line)) & ~1;
-
-            return left + ((right - left) - max) / 2;
+            default: /* resultAlign == Alignment.ALIGN_LEFT */
+                return 0;
         }
     }
 
@@ -1300,29 +1318,40 @@
      * scrolling on the specified line.
      */
     public float getLineRight(int line) {
-        int dir = getParagraphDirection(line);
-        Alignment align = getParagraphAlignment(line);
+        final int dir = getParagraphDirection(line);
+        final Alignment align = getParagraphAlignment(line);
 
-        if (align == Alignment.ALIGN_LEFT) {
-            return getParagraphLeft(line) + getLineMax(line);
-        } else if (align == Alignment.ALIGN_NORMAL) {
-            if (dir == DIR_RIGHT_TO_LEFT)
+        final Alignment resultAlign;
+        switch(align) {
+            case ALIGN_NORMAL:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_RIGHT : Alignment.ALIGN_LEFT;
+                break;
+            case ALIGN_OPPOSITE:
+                resultAlign =
+                        dir == DIR_RIGHT_TO_LEFT ? Alignment.ALIGN_LEFT : Alignment.ALIGN_RIGHT;
+                break;
+            case ALIGN_CENTER:
+                resultAlign = Alignment.ALIGN_CENTER;
+                break;
+            case ALIGN_RIGHT:
+                resultAlign = Alignment.ALIGN_RIGHT;
+                break;
+            default: /* align == Alignment.ALIGN_LEFT */
+                resultAlign = Alignment.ALIGN_LEFT;
+        }
+
+        switch (resultAlign) {
+            case ALIGN_CENTER:
+                final int right = getParagraphRight(line);
+                final float max = getLineMax(line);
+                // This computation only works when mWidth equals leadingMargin plus width of the
+                // text in this line. If this condition doesn't meet anymore, please change here.
+                return (float) Math.ceil(right - (mWidth - max) / 2);
+            case ALIGN_RIGHT:
                 return mWidth;
-            else
-                return getParagraphLeft(line) + getLineMax(line);
-        } else if (align == Alignment.ALIGN_RIGHT) {
-            return mWidth;
-        } else if (align == Alignment.ALIGN_OPPOSITE) {
-            if (dir == DIR_RIGHT_TO_LEFT)
+            default: /* resultAlign == Alignment.ALIGN_LEFT */
                 return getLineMax(line);
-            else
-                return mWidth;
-        } else { /* align == Alignment.ALIGN_CENTER */
-            int left = getParagraphLeft(line);
-            int right = getParagraphRight(line);
-            int max = ((int) getLineMax(line)) & ~1;
-
-            return right - ((right - left) - max) / 2;
         }
     }
 
@@ -1671,7 +1700,7 @@
      * Return the vertical position of the baseline of the specified line.
      */
     public final int getLineBaseline(int line) {
-        // getLineTop(line+1) == getLineTop(line)
+        // getLineTop(line+1) == getLineBottom(line)
         return getLineTop(line+1) - getLineDescent(line);
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e88682e..a7d0cfb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -23,8 +23,11 @@
 import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -204,6 +207,7 @@
      *
      * @hide
      */
+    @TestApi
     public interface AccessibilityServicesStateChangeListener {
 
         /**
@@ -778,6 +782,7 @@
      *                for a callback on the process's main handler.
      * @hide
      */
+    @TestApi
     public void addAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
         synchronized (mLock) {
@@ -793,6 +798,7 @@
      *
      * @hide
      */
+    @TestApi
     public void removeAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener) {
         synchronized (mLock) {
@@ -1056,6 +1062,9 @@
      *
      * @hide
      */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
     public void performAccessibilityShortcut() {
         final IAccessibilityManager service;
         synchronized (mLock) {
@@ -1139,6 +1148,30 @@
         }
     }
 
+    /**
+     * Get the component name of the service currently assigned to the accessibility shortcut.
+     *
+     * @return The flattened component name
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    @Nullable
+    public String getAccessibilityShortcutService() {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+        }
+        if (service != null) {
+            try {
+                return service.getAccessibilityShortcutService();
+            } catch (RemoteException re) {
+                re.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
     private IAccessibilityManager getServiceLocked() {
         if (mService == null) {
             tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 3e2ef18..61a8a1da 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -67,9 +67,12 @@
 
     void notifyAccessibilityButtonVisibilityChanged(boolean available);
 
-    // Requires WRITE_SECURE_SETTINGS
+    // Requires Manifest.permission.MANAGE_ACCESSIBILITY
     void performAccessibilityShortcut();
 
+    // Requires Manifest.permission.MANAGE_ACCESSIBILITY
+    String getAccessibilityShortcutService();
+
     // System process only
     boolean sendFingerprintGesture(int gestureKeyCode);
 
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
new file mode 100644
index 0000000..5fcf227
--- /dev/null
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.textclassifier;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.app.Person;
+import android.app.RemoteAction;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.SpannedString;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
+ *
+ * @see TextClassifier#suggestConversationActions(Request)
+ */
+public final class ConversationActions implements Parcelable {
+
+    public static final Creator<ConversationActions> CREATOR =
+            new Creator<ConversationActions>() {
+                @Override
+                public ConversationActions createFromParcel(Parcel in) {
+                    return new ConversationActions(in);
+                }
+
+                @Override
+                public ConversationActions[] newArray(int size) {
+                    return new ConversationActions[size];
+                }
+            };
+
+    /** @hide */
+    @Retention(SOURCE)
+    @StringDef(
+            value = {
+                    TYPE_VIEW_CALENDAR,
+                    TYPE_VIEW_MAP,
+                    TYPE_TRACK_FLIGHT,
+                    TYPE_OPEN_URL,
+                    TYPE_SEND_SMS,
+                    TYPE_CALL_PHONE,
+                    TYPE_SEND_EMAIL,
+                    TYPE_TEXT_REPLY,
+                    TYPE_CREATE_REMINDER,
+                    TYPE_SHARE_LOCATION
+            },
+            prefix = "TYPE_")
+    public @interface ActionType {}
+
+    /**
+     * Indicates an action to view a calendar at a specified time.
+     */
+    public static final String TYPE_VIEW_CALENDAR = "view_calendar";
+    /**
+     * Indicates an action to view the map at a specified location.
+     */
+    public static final String TYPE_VIEW_MAP = "view_map";
+    /**
+     * Indicates an action to track a flight.
+     */
+    public static final String TYPE_TRACK_FLIGHT = "track_flight";
+    /**
+     * Indicates an action to open an URL.
+     */
+    public static final String TYPE_OPEN_URL = "open_url";
+    /**
+     * Indicates an action to send a SMS.
+     */
+    public static final String TYPE_SEND_SMS = "send_sms";
+    /**
+     * Indicates an action to call a phone number.
+     */
+    public static final String TYPE_CALL_PHONE = "call_phone";
+    /**
+     * Indicates an action to send an email.
+     */
+    public static final String TYPE_SEND_EMAIL = "send_email";
+    /**
+     * Indicates an action to reply with a text message.
+     */
+    public static final String TYPE_TEXT_REPLY = "text_reply";
+    /**
+     * Indicates an action to create a reminder.
+     */
+    public static final String TYPE_CREATE_REMINDER = "create_reminder";
+    /**
+     * Indicates an action to reply with a location.
+     */
+    public static final String TYPE_SHARE_LOCATION = "share_location";
+
+    /** @hide */
+    @Retention(SOURCE)
+    @StringDef(
+            value = {
+                    HINT_FOR_NOTIFICATION,
+                    HINT_FOR_IN_APP,
+            },
+            prefix = "HINT_")
+    public @interface Hint {}
+    /**
+     * To indicate the generated actions will be used within the app.
+     */
+    public static final String HINT_FOR_IN_APP = "in_app";
+    /**
+     * To indicate the generated actions will be used for notification.
+     */
+    public static final String HINT_FOR_NOTIFICATION = "notification";
+
+    private List<ConversationAction> mConversationActions;
+
+    /** Constructs a {@link ConversationActions} object. */
+    public ConversationActions(@NonNull List<ConversationAction> conversationActions) {
+        mConversationActions =
+                Collections.unmodifiableList(Preconditions.checkNotNull(conversationActions));
+    }
+
+    private ConversationActions(Parcel in) {
+        mConversationActions =
+                Collections.unmodifiableList(in.createTypedArrayList(ConversationAction.CREATOR));
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeTypedList(mConversationActions);
+    }
+
+    /** Returns an immutable list of {@link ConversationAction} objects. */
+    @NonNull
+    public List<ConversationAction> getConversationActions() {
+        return mConversationActions;
+    }
+
+    /** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+    public static final class ConversationAction implements Parcelable {
+
+        public static final Creator<ConversationAction> CREATOR =
+                new Creator<ConversationAction>() {
+                    @Override
+                    public ConversationAction createFromParcel(Parcel in) {
+                        return new ConversationAction(in);
+                    }
+
+                    @Override
+                    public ConversationAction[] newArray(int size) {
+                        return new ConversationAction[size];
+                    }
+                };
+
+        @NonNull
+        @ActionType
+        private final String mType;
+        @NonNull
+        private final CharSequence mTextReply;
+        @Nullable
+        private final RemoteAction mAction;
+
+        @FloatRange(from = 0, to = 1)
+        private final float mScore;
+
+        @NonNull
+        private final Bundle mExtras;
+
+        private ConversationAction(
+                @NonNull String type,
+                @Nullable RemoteAction action,
+                @Nullable CharSequence textReply,
+                float score,
+                @NonNull Bundle extras) {
+            mType = Preconditions.checkNotNull(type);
+            mAction = action;
+            mTextReply = textReply;
+            mScore = score;
+            mExtras = Preconditions.checkNotNull(extras);
+        }
+
+        private ConversationAction(Parcel in) {
+            mType = in.readString();
+            mAction = in.readParcelable(null);
+            mTextReply = in.readCharSequence();
+            mScore = in.readFloat();
+            mExtras = in.readBundle();
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeString(mType);
+            parcel.writeParcelable(mAction, flags);
+            parcel.writeCharSequence(mTextReply);
+            parcel.writeFloat(mScore);
+            parcel.writeBundle(mExtras);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @NonNull
+        @ActionType
+        /** Returns the type of this action, for example, {@link #TYPE_VIEW_CALENDAR}. */
+        public String getType() {
+            return mType;
+        }
+
+        @Nullable
+        /**
+         * Returns a RemoteAction object, which contains the icon, label and a PendingIntent, for
+         * the specified action type.
+         */
+        public RemoteAction getAction() {
+            return mAction;
+        }
+
+        /**
+         * Returns the confidence score for the specified action. The value ranges from 0 (low
+         * confidence) to 1 (high confidence).
+         */
+        @FloatRange(from = 0, to = 1)
+        public float getConfidenceScore() {
+            return mScore;
+        }
+
+        /**
+         * Returns the text reply that could be sent as a reply to the given conversation.
+         * <p>
+         * This is only available when the type of the action is {@link #TYPE_TEXT_REPLY}.
+         */
+        @Nullable
+        public CharSequence getTextReply() {
+            return mTextReply;
+        }
+
+        /**
+         * Returns the extended data related to this conversation action.
+         *
+         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+         * prefer to hold a reference to the returned bundle rather than frequently calling this
+         * method.
+         */
+        @NonNull
+        public Bundle getExtras() {
+            return mExtras.deepCopy();
+        }
+
+        /** Builder class to construct {@link ConversationAction}. */
+        public static final class Builder {
+            @Nullable
+            @ActionType
+            private String mType;
+            @Nullable
+            private RemoteAction mAction;
+            @Nullable
+            private CharSequence mTextReply;
+            private float mScore;
+            @Nullable
+            private Bundle mExtras;
+
+            public Builder(@NonNull @ActionType String actionType) {
+                mType = Preconditions.checkNotNull(actionType);
+            }
+
+            /**
+             * Sets an action that may be performed on the given conversation.
+             */
+            @NonNull
+            public Builder setAction(@Nullable RemoteAction action) {
+                mAction = action;
+                return this;
+            }
+
+            /**
+             * Sets a text reply that may be performed on the given conversation.
+             */
+            @NonNull
+            public Builder setTextReply(@Nullable CharSequence textReply) {
+                mTextReply = textReply;
+                return this;
+            }
+
+            /** Sets the confident score. */
+            @NonNull
+            public Builder setConfidenceScore(@FloatRange(from = 0, to = 1) float score) {
+                mScore = score;
+                return this;
+            }
+
+            /**
+             * Sets the extended data for the conversation action object.
+             */
+            @NonNull
+            public Builder setExtras(@Nullable Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /** Builds the {@link ConversationAction} object. */
+            @NonNull
+            public ConversationAction build() {
+                return new ConversationAction(
+                        mType,
+                        mAction,
+                        mTextReply,
+                        mScore,
+                        mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
+            }
+        }
+    }
+
+    /** Represents a message in the conversation. */
+    public static final class Message implements Parcelable {
+        @Nullable
+        private final Person mAuthor;
+        @Nullable
+        private final ZonedDateTime mComposeTime;
+        @Nullable
+        private final CharSequence mText;
+        @NonNull
+        private final Bundle mExtras;
+
+        private Message(
+                @Nullable Person author,
+                @Nullable ZonedDateTime composeTime,
+                @Nullable CharSequence text,
+                @NonNull Bundle bundle) {
+            mAuthor = author;
+            mComposeTime = composeTime;
+            mText = text;
+            mExtras = Preconditions.checkNotNull(bundle);
+        }
+
+        private Message(Parcel in) {
+            mAuthor = in.readParcelable(null);
+            mComposeTime =
+                    in.readInt() == 0
+                            ? null
+                            : ZonedDateTime.parse(
+                                    in.readString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
+            mText = in.readCharSequence();
+            mExtras = in.readBundle();
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeParcelable(mAuthor, flags);
+            parcel.writeInt(mComposeTime != null ? 1 : 0);
+            if (mComposeTime != null) {
+                parcel.writeString(mComposeTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+            }
+            parcel.writeCharSequence(mText);
+            parcel.writeBundle(mExtras);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Message> CREATOR =
+                new Creator<Message>() {
+                    @Override
+                    public Message createFromParcel(Parcel in) {
+                        return new Message(in);
+                    }
+
+                    @Override
+                    public Message[] newArray(int size) {
+                        return new Message[size];
+                    }
+                };
+
+        /** Returns the person that composed the message. */
+        @Nullable
+        public Person getAuthor() {
+            return mAuthor;
+        }
+
+        /** Returns the compose time of the message. */
+        @Nullable
+        public ZonedDateTime getTime() {
+            return mComposeTime;
+        }
+
+        /** Returns the text of the message. */
+        @Nullable
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Returns the extended data related to this conversation action.
+         *
+         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+         * prefer to hold a reference to the returned bundle rather than frequently calling this
+         * method.
+         */
+        @NonNull
+        public Bundle getExtras() {
+            return mExtras.deepCopy();
+        }
+
+        /** Builder class to construct a {@link Message} */
+        public static final class Builder {
+            @Nullable
+            private Person mAuthor;
+            @Nullable
+            private ZonedDateTime mComposeTime;
+            @Nullable
+            private CharSequence mText;
+            @Nullable
+            private Bundle mExtras;
+
+            /** Sets the person who composed this message. */
+            @NonNull
+            public Builder setAuthor(@Nullable Person author) {
+                mAuthor = author;
+                return this;
+            }
+
+            /** Sets the text of this message */
+            @NonNull
+            public Builder setText(@Nullable CharSequence text) {
+                mText = text;
+                return this;
+            }
+
+            /** Sets the compose time of this message */
+            @NonNull
+            public Builder setComposeTime(@Nullable ZonedDateTime composeTime) {
+                mComposeTime = composeTime;
+                return this;
+            }
+
+            /** Sets a set of extended data to the message. */
+            @NonNull
+            public Builder setExtras(@Nullable Bundle bundle) {
+                this.mExtras = bundle;
+                return this;
+            }
+
+            /** Builds the {@link Message} object. */
+            @NonNull
+            public Message build() {
+                return new Message(
+                        mAuthor,
+                        mComposeTime,
+                        mText == null ? null : new SpannedString(mText),
+                        mExtras == null ? new Bundle() : mExtras.deepCopy());
+            }
+        }
+    }
+
+    /** Configuration object for specifying what action types to identify. */
+    public static final class TypeConfig implements Parcelable {
+        @NonNull
+        @ActionType
+        private final Set<String> mExcludedTypes;
+        @NonNull
+        @ActionType
+        private final Set<String> mIncludedTypes;
+        private final boolean mIncludeTypesFromTextClassifier;
+
+        private TypeConfig(
+                @NonNull Set<String> includedTypes,
+                @NonNull Set<String> excludedTypes,
+                boolean includeTypesFromTextClassifier) {
+            mIncludedTypes = Preconditions.checkNotNull(includedTypes);
+            mExcludedTypes = Preconditions.checkNotNull(excludedTypes);
+            mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+        }
+
+        private TypeConfig(Parcel in) {
+            mIncludedTypes = new ArraySet<>(in.createStringArrayList());
+            mExcludedTypes = new ArraySet<>(in.createStringArrayList());
+            mIncludeTypesFromTextClassifier = in.readByte() != 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeStringList(new ArrayList<>(mIncludedTypes));
+            parcel.writeStringList(new ArrayList<>(mExcludedTypes));
+            parcel.writeByte((byte) (mIncludeTypesFromTextClassifier ? 1 : 0));
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<TypeConfig> CREATOR =
+                new Creator<TypeConfig>() {
+                    @Override
+                    public TypeConfig createFromParcel(Parcel in) {
+                        return new TypeConfig(in);
+                    }
+
+                    @Override
+                    public TypeConfig[] newArray(int size) {
+                        return new TypeConfig[size];
+                    }
+                };
+
+        /**
+         * Returns a final list of types that the text classifier should look for.
+         *
+         * <p>NOTE: This method is intended for use by a text classifier.
+         *
+         * @param defaultTypes types the text classifier thinks should be included before factoring
+         *    in the included/excluded types given by the client.
+         */
+        @NonNull
+        public Collection<String> resolveTypes(@Nullable Collection<String> defaultTypes) {
+            Set<String> types = new ArraySet<>();
+            if (mIncludeTypesFromTextClassifier && defaultTypes != null) {
+                types.addAll(defaultTypes);
+            }
+            types.addAll(mIncludedTypes);
+            types.removeAll(mExcludedTypes);
+            return Collections.unmodifiableCollection(types);
+        }
+
+        /**
+         * Return whether the client allows the text classifier to include its own list of default
+         * types. If this function returns {@code true}, the text classifier can consider specifying
+         * a default list of entity types in {@link #resolveTypes(Collection)}.
+         *
+         * <p>NOTE: This method is intended for use by a text classifier.
+         *
+         * @see #resolveTypes(Collection)
+         */
+        public boolean shouldIncludeTypesFromTextClassifier() {
+            return mIncludeTypesFromTextClassifier;
+        }
+
+        /** Builder class to construct the {@link TypeConfig} object. */
+        public static final class Builder {
+            @Nullable
+            private Collection<String> mExcludedTypes;
+            @Nullable
+            private Collection<String> mIncludedTypes;
+            private boolean mIncludeTypesFromTextClassifier = true;
+
+            /**
+             * Sets a collection of types that are explicitly included, for example, {@link
+             * #TYPE_VIEW_CALENDAR}.
+             */
+            @NonNull
+            public Builder setIncludedTypes(
+                    @Nullable @ActionType Collection<String> includedTypes) {
+                mIncludedTypes = includedTypes;
+                return this;
+            }
+
+            /**
+             * Sets a collection of types that are explicitly excluded, for example, {@link
+             * #TYPE_VIEW_CALENDAR}.
+             */
+            @NonNull
+            public Builder setExcludedTypes(
+                    @Nullable @ActionType Collection<String> excludedTypes) {
+                mExcludedTypes = excludedTypes;
+                return this;
+            }
+
+            /**
+             * Specifies whether or not to include the types suggested by the text classifier. By
+             * default, it is included.
+             */
+            @NonNull
+            public Builder includeTypesFromTextClassifier(boolean includeTypesFromTextClassifier) {
+                mIncludeTypesFromTextClassifier = includeTypesFromTextClassifier;
+                return this;
+            }
+
+            /**
+             * Combines all of the options that have been set and returns a new {@link TypeConfig}
+             * object.
+             */
+            @NonNull
+            public TypeConfig build() {
+                return new TypeConfig(
+                        mIncludedTypes == null
+                                ? Collections.emptySet()
+                                : new ArraySet<>(mIncludedTypes),
+                        mExcludedTypes == null
+                                ? Collections.emptySet()
+                                : new ArraySet<>(mExcludedTypes),
+                        mIncludeTypesFromTextClassifier);
+            }
+        }
+    }
+
+    /**
+     * A request object for generating conversation action suggestions.
+     *
+     * @see TextClassifier#suggestConversationActions(Request)
+     */
+    public static final class Request implements Parcelable {
+        @NonNull
+        private final List<Message> mConversation;
+        @NonNull
+        private final TypeConfig mTypeConfig;
+        private final int mMaxSuggestions;
+        @NonNull
+        @Hint
+        private final List<String> mHints;
+
+        private Request(
+                @NonNull List<Message> conversation,
+                @NonNull TypeConfig typeConfig,
+                int maxSuggestions,
+                @Nullable @Hint List<String> hints) {
+            mConversation = Preconditions.checkNotNull(conversation);
+            mTypeConfig = Preconditions.checkNotNull(typeConfig);
+            mMaxSuggestions = maxSuggestions;
+            mHints = hints;
+        }
+
+        private Request(Parcel in) {
+            List<Message> conversation = new ArrayList<>();
+            in.readParcelableList(conversation, null);
+            mConversation = Collections.unmodifiableList(conversation);
+            mTypeConfig = in.readParcelable(null);
+            mMaxSuggestions = in.readInt();
+            List<String> hints = new ArrayList<>();
+            in.readStringList(hints);
+            mHints = Collections.unmodifiableList(hints);
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeParcelableList(mConversation, flags);
+            parcel.writeParcelable(mTypeConfig, flags);
+            parcel.writeInt(mMaxSuggestions);
+            parcel.writeStringList(mHints);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Request> CREATOR =
+                new Creator<Request>() {
+                    @Override
+                    public Request createFromParcel(Parcel in) {
+                        return new Request(in);
+                    }
+
+                    @Override
+                    public Request[] newArray(int size) {
+                        return new Request[size];
+                    }
+                };
+
+        /** Returns the type config. */
+        @NonNull
+        public TypeConfig getTypeConfig() {
+            return mTypeConfig;
+        }
+
+        /** Returns an immutable list of messages that make up the conversation. */
+        @NonNull
+        public List<Message> getConversation() {
+            return mConversation;
+        }
+
+        /**
+         * Return the maximal number of suggestions the caller wants, value 0 means no restriction.
+         */
+        @IntRange(from = 0)
+        public int getMaxSuggestions() {
+            return mMaxSuggestions;
+        }
+
+        /** Returns an immutable list of hints */
+        @Nullable
+        @Hint
+        public List<String> getHints() {
+            return mHints;
+        }
+
+        /** Builder object to construct the {@link Request} object. */
+        public static final class Builder {
+            @NonNull
+            private List<Message> mConversation;
+            @Nullable
+            private TypeConfig mTypeConfig;
+            private int mMaxSuggestions;
+            @Nullable
+            @Hint
+            private List<String> mHints;
+
+            /**
+             * Constructs a builder.
+             *
+             * @param conversation the conversation that the text classifier is going to generate
+             *     actions for.
+             */
+            public Builder(@NonNull List<Message> conversation) {
+                mConversation = Preconditions.checkNotNull(conversation);
+            }
+
+            /**
+             * Sets the hints to help text classifier to generate actions. It could be used to help
+             * text classifier to infer what types of actions the caller may be interested in.
+             */
+            public Builder setHints(@Nullable @Hint List<String> hints) {
+                mHints = hints;
+                return this;
+            }
+
+            /** Sets the type config. */
+            @NonNull
+            public Builder setTypeConfig(@Nullable TypeConfig typeConfig) {
+                mTypeConfig = typeConfig;
+                return this;
+            }
+
+            /** Sets the maximum number of suggestions you want.
+             * <p>
+             * Value 0 means no restriction.
+             */
+            @NonNull
+            public Builder setMaxSuggestions(@IntRange(from = 0) int maxSuggestions) {
+                mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions);
+                return this;
+            }
+
+            /** Builds the {@link Request} object. */
+            @NonNull
+            public Request build() {
+                return new Request(
+                        Collections.unmodifiableList(mConversation),
+                        mTypeConfig == null ? new TypeConfig.Builder().build() : mTypeConfig,
+                        mMaxSuggestions,
+                        mHints == null
+                                ? Collections.emptyList()
+                                : Collections.unmodifiableList(mHints));
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 9511a9e..d972bb8 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -33,6 +33,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -126,6 +127,7 @@
     @NonNull private final List<RemoteAction> mActions;
     @NonNull private final EntityConfidence mEntityConfidence;
     @Nullable private final String mId;
+    @NonNull private final Bundle mExtras;
 
     private TextClassification(
             @Nullable String text,
@@ -135,7 +137,8 @@
             @Nullable OnClickListener legacyOnClickListener,
             @NonNull List<RemoteAction> actions,
             @NonNull Map<String, Float> entityConfidence,
-            @Nullable String id) {
+            @Nullable String id,
+            @NonNull Bundle extras) {
         mText = text;
         mLegacyIcon = legacyIcon;
         mLegacyLabel = legacyLabel;
@@ -144,6 +147,7 @@
         mActions = Collections.unmodifiableList(actions);
         mEntityConfidence = new EntityConfidence(entityConfidence);
         mId = id;
+        mExtras = extras;
     }
 
     /**
@@ -255,6 +259,18 @@
         return mId;
     }
 
+    /**
+     * Returns the extended data.
+     *
+     * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+     * prefer to hold a reference to the returned bundle rather than frequently calling this
+     * method.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras.deepCopy();
+    }
+
     @Override
     public String toString() {
         return String.format(Locale.US,
@@ -359,6 +375,7 @@
         @Nullable private Intent mLegacyIntent;
         @Nullable private OnClickListener mLegacyOnClickListener;
         @Nullable private String mId;
+        @Nullable private Bundle mExtras;
 
         /**
          * Sets the classified text.
@@ -471,12 +488,22 @@
         }
 
         /**
+         * Sets the extended data.
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
          * Builds and returns a {@link TextClassification} object.
          */
         @NonNull
         public TextClassification build() {
             return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
-                    mLegacyOnClickListener, mActions, mEntityConfidence, mId);
+                    mLegacyOnClickListener, mActions, mEntityConfidence, mId,
+                    mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
         }
     }
 
@@ -490,18 +517,21 @@
         private final int mEndIndex;
         @Nullable private final LocaleList mDefaultLocales;
         @Nullable private final ZonedDateTime mReferenceTime;
+        @NonNull private final Bundle mExtras;
 
         private Request(
                 CharSequence text,
                 int startIndex,
                 int endIndex,
                 LocaleList defaultLocales,
-                ZonedDateTime referenceTime) {
+                ZonedDateTime referenceTime,
+                Bundle extras) {
             mText = text;
             mStartIndex = startIndex;
             mEndIndex = endIndex;
             mDefaultLocales = defaultLocales;
             mReferenceTime = referenceTime;
+            mExtras = extras;
         }
 
         /**
@@ -548,6 +578,18 @@
         }
 
         /**
+         * Returns the extended data.
+         *
+         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+         * prefer to hold a reference to the returned bundle rather than frequently calling this
+         * method.
+         */
+        @NonNull
+        public Bundle getExtras() {
+            return mExtras.deepCopy();
+        }
+
+        /**
          * A builder for building TextClassification requests.
          */
         public static final class Builder {
@@ -555,6 +597,7 @@
             private final CharSequence mText;
             private final int mStartIndex;
             private final int mEndIndex;
+            private Bundle mExtras;
 
             @Nullable private LocaleList mDefaultLocales;
             @Nullable private ZonedDateTime mReferenceTime;
@@ -602,11 +645,23 @@
             }
 
             /**
+             * Sets the extended data.
+             *
+             * @return this builder
+             */
+            @NonNull
+            public Builder setExtras(@Nullable Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /**
              * Builds and returns the request object.
              */
             @NonNull
             public Request build() {
-                return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime);
+                return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+                        mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
         }
 
@@ -628,6 +683,7 @@
             if (mReferenceTime != null) {
                 dest.writeString(mReferenceTime.toString());
             }
+            dest.writeBundle(mExtras);
         }
 
         public static final Parcelable.Creator<Request> CREATOR =
@@ -649,6 +705,7 @@
             mEndIndex = in.readInt();
             mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
             mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
+            mExtras = in.readBundle();
         }
     }
 
@@ -664,6 +721,7 @@
         dest.writeTypedList(mActions);
         mEntityConfidence.writeToParcel(dest, flags);
         dest.writeString(mId);
+        dest.writeBundle(mExtras);
     }
 
     public static final Parcelable.Creator<TextClassification> CREATOR =
@@ -695,6 +753,7 @@
         mLegacyIntent = null; // mLegacyIntent is not parcelled.
         mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
         mId = in.readString();
+        mExtras = in.readBundle();
     }
 
     // Best effort attempt to try to load a drawable from the provided icon.
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 2e92f14..e675744 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -325,6 +325,17 @@
     }
 
     /**
+     * Suggests and returns a list of actions according to the given conversation.
+     */
+    @WorkerThread
+    default ConversationActions suggestConversationActions(
+            @NonNull ConversationActions.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        return new ConversationActions(Collections.emptyList());
+    }
+
+    /**
      * Reports a selection event.
      *
      * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index d1c2799..0a7cff6 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -113,11 +113,12 @@
 
     /**
      * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
-     * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
+     * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map, boolean)
      */
     public static void apply464xlatAdjustments(NetworkStats baseTraffic,
-            NetworkStats stackedTraffic) {
-        NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces);
+            NetworkStats stackedTraffic, boolean useBpfStats) {
+        NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces,
+                useBpfStats);
     }
 
     @VisibleForTesting
@@ -263,7 +264,7 @@
 
         // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
         // TODO: remove this and only apply adjustments in NetworkStatsService.
-        stats.apply464xlatAdjustments(sStackedIfaces);
+        stats.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
 
         return stats;
     }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 98b7b5d..65213c0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -216,6 +216,11 @@
      */
     native protected static void nativeUnmountStorageOnInit();
 
+    private static void callPostForkSystemServerHooks() {
+        // SystemServer specific post fork hooks run before child post fork hooks.
+        VM_HOOKS.postForkSystemServer();
+    }
+
     private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
             boolean isZygote, String instructionSet) {
         VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3e04bb3..4e20e29 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -84,6 +84,7 @@
 static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
 static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
 static jclass gZygoteClass;
+static jmethodID gCallPostForkSystemServerHooks;
 static jmethodID gCallPostForkChildHooks;
 
 static bool g_is_security_enforced = true;
@@ -886,6 +887,18 @@
   // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
   UnsetChldSignalHandler();
 
+  if (is_system_server) {
+    env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks);
+    if (env->ExceptionCheck()) {
+      fail_fn("Error calling post fork system server hooks.");
+    }
+    // TODO(oth): Remove hardcoded label here (b/117874058).
+    static const char* kSystemServerLabel = "u:r:system_server:s0";
+    if (selinux_android_setcon(kSystemServerLabel) != 0) {
+      fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
+    }
+  }
+
   env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
                             is_system_server, is_child_zygote, instructionSet);
   if (env->ExceptionCheck()) {
@@ -1181,6 +1194,9 @@
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
   gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
+  gCallPostForkSystemServerHooks = GetStaticMethodIDOrDie(env, gZygoteClass,
+                                                          "callPostForkSystemServerHooks",
+                                                          "()V");
   gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
                                                    "(IZZLjava/lang/String;)V");
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cd6bf66..e728ead 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4199,6 +4199,11 @@
     <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows modifying accessibility state.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_ACCESSIBILITY"
+                android:protectionLevel="signature|setup" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index de863d7..91a5440 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -29,6 +29,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.support.test.InstrumentationRegistry;
@@ -48,6 +49,13 @@
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationTest {
 
+    private static final String BUNDLE_KEY = "key";
+    private static final String BUNDLE_VALUE = "value";
+    private static final Bundle BUNDLE = new Bundle();
+    static {
+        BUNDLE.putString(BUNDLE_KEY, BUNDLE_VALUE);
+    }
+
     public Icon generateTestIcon(int width, int height, int colorValue) {
         final int numPixels = width * height;
         final int[] colors = new int[numPixels];
@@ -89,6 +97,7 @@
                 .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
                 .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
                 .setId(id)
+                .setExtras(BUNDLE)
                 .build();
 
         // Parcel and unparcel
@@ -119,6 +128,9 @@
         assertEquals(TextClassifier.TYPE_ADDRESS, result.getEntity(1));
         assertEquals(0.7f, result.getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
         assertEquals(0.3f, result.getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
+
+        // Extras
+        assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
     }
 
     @Test
@@ -182,6 +194,7 @@
                 new TextClassification.Request.Builder(text, 0, text.length())
                         .setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
                         .setReferenceTime(referenceTime)
+                        .setExtras(BUNDLE)
                         .build();
 
         // Parcel and unparcel.
@@ -197,5 +210,6 @@
         assertEquals(referenceTime, result.getReferenceTime());
         assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
         assertEquals(referenceTime, result.getReferenceTime());
+        assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
     }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 28e92db..8964ee3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -313,6 +313,7 @@
         <permission name="android.permission.INSTALL_PACKAGES"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.MANAGE_ACCESSIBILITY"/>
         <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_USB"/>
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 3efe655..6f78651 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -48,9 +48,6 @@
  * </p>
  */
 public class MeasuredText {
-    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-            MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
     private long mNativePtr;
     private @NonNull char[] mChars;
 
@@ -166,6 +163,9 @@
      * Note: The appendStyle and appendReplacementRun should be called to cover the text length.
      */
     public static class Builder {
+        private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
         private long mNativePtr;
 
         private final @NonNull char[] mText;
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
new file mode 100644
index 0000000..0619a9c
--- /dev/null
+++ b/libs/incident/Android.bp
@@ -0,0 +1,50 @@
+// 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.
+
+cc_library_shared {
+    name: "libincident",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+        export_aidl_headers: true,
+    },
+
+    srcs: [
+        ":libincident_aidl",
+        "proto/android/os/header.proto",
+        "proto/android/os/metadata.proto",
+        "src/IncidentReportArgs.cpp",
+    ],
+
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
+
+    export_include_dirs: ["include"],
+}
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
deleted file mode 100644
index 08c8346..0000000
--- a/libs/incident/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libincident
-
-LOCAL_CFLAGS := \
-        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_SHARED_LIBRARIES := \
-        libbinder \
-        liblog \
-        libutils
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES := \
-        $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
-        ../../core/java/android/os/IIncidentManager.aidl \
-        ../../core/java/android/os/IIncidentReportStatusListener.aidl \
-        proto/android/os/header.proto \
-        proto/android/os/metadata.proto \
-        src/IncidentReportArgs.cpp
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_PROTO_OPTIMIZE_TYPE := lite
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index a842229..3152e65 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -127,4 +127,17 @@
     default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
             int state, int bluetoothProfile) {
     }
+
+    /**
+     * Called when ACL connection state is changed. It listens to
+     * {@link android.bluetooth.BluetoothDevice#ACTION_ACL_CONNECTED} and {@link
+     * android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECTED}
+     *
+     * @param cachedDevice Bluetooth device that changed
+     * @param state        the Bluetooth device connection state, the possible values are:
+     *                     {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTED},
+     *                     {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED}
+     */
+    default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 022bf69..2b7babd0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -119,6 +119,10 @@
         addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED,
                 new AudioModeChangedHandler());
 
+        // ACL connection changed broadcasts
+        addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler());
+        addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler());
+
         registerAdapterIntentReceiver();
     }
 
@@ -236,6 +240,15 @@
         }
     }
 
+    private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice,
+            int state) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onAclConnectionStateChanged(activeDevice, state);
+            }
+        }
+    }
+
     @VisibleForTesting
     void addHandler(String action, Handler handler) {
         mHandlerMap.put(action, handler);
@@ -447,6 +460,32 @@
         }
     }
 
+    private class AclStateChangedHandler implements Handler {
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            final String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "AclStateChangedHandler: action is null");
+                return;
+            }
+            final CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+            final int state;
+            switch (action) {
+                case BluetoothDevice.ACTION_ACL_CONNECTED:
+                    state = BluetoothAdapter.STATE_CONNECTED;
+                    break;
+                case BluetoothDevice.ACTION_ACL_DISCONNECTED:
+                    state = BluetoothAdapter.STATE_DISCONNECTED;
+                    break;
+                default:
+                    Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+                    return;
+
+            }
+            dispatchAclStateChanged(activeDevice, state);
+        }
+    }
+
     private class AudioModeChangedHandler implements Handler {
 
         @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 020234c6..c147d5e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -19,7 +19,10 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
@@ -50,6 +53,8 @@
     private BluetoothCallback mBluetoothCallback;
     @Mock
     private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
 
     private Context mContext;
     private Intent mIntent;
@@ -62,6 +67,7 @@
 
         mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
                 mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
     }
 
     @Test
@@ -126,4 +132,28 @@
         verify(mBluetoothCallback).onProfileConnectionStateChanged(mCachedBluetoothDevice,
                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
     }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() {
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice,
+                BluetoothAdapter.STATE_CONNECTED);
+    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index bf4374a..bca3530 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -116,6 +116,7 @@
         public boolean isTransient = false;
         public String expandedAccessibilityClassName;
         public SlashState slash;
+        public boolean handlesLongClick = true;
 
         public boolean copyTo(State other) {
             if (other == null) throw new IllegalArgumentException();
@@ -133,7 +134,8 @@
                     || !Objects.equals(other.state, state)
                     || !Objects.equals(other.isTransient, isTransient)
                     || !Objects.equals(other.dualTarget, dualTarget)
-                    || !Objects.equals(other.slash, slash);
+                    || !Objects.equals(other.slash, slash)
+                    || !Objects.equals(other.handlesLongClick, handlesLongClick);
             other.icon = icon;
             other.iconSupplier = iconSupplier;
             other.label = label;
@@ -146,6 +148,7 @@
             other.dualTarget = dualTarget;
             other.isTransient = isTransient;
             other.slash = slash != null ? slash.copy() : null;
+            other.handlesLongClick = handlesLongClick;
             return changed;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index d42127e..0638998 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -194,6 +194,7 @@
         }
 
         setClickable(state.state != Tile.STATE_UNAVAILABLE);
+        setLongClickable(state.handlesLongClick);
         mIcon.setIcon(state, allowAnimations);
         setContentDescription(state.contentDescription);
 
@@ -287,10 +288,14 @@
                 info.setText(label);
                 info.setChecked(b);
                 info.setCheckable(true);
-                info.addAction(
-                        new AccessibilityNodeInfo.AccessibilityAction(
-                                AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.getId(),
-                                getResources().getString(R.string.accessibility_long_click_tile)));
+                if (isLongClickable()) {
+                    info.addAction(
+                            new AccessibilityNodeInfo.AccessibilityAction(
+                                    AccessibilityNodeInfo.AccessibilityAction
+                                            .ACTION_LONG_CLICK.getId(),
+                                    getResources().getString(
+                                            R.string.accessibility_long_click_tile)));
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index f2ead1c..d7ac253 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.content.Intent;
-import android.graphics.drawable.Drawable;
 import android.provider.MediaStore;
 import android.service.quicksettings.Tile;
 import android.widget.Switch;
@@ -50,7 +49,9 @@
 
     @Override
     public BooleanState newTileState() {
-        return new BooleanState();
+        BooleanState state = new BooleanState();
+        state.handlesLongClick = false;
+        return state;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 38011d9..427d169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.statusbar;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 
 import android.app.ActivityManager;
@@ -292,7 +293,8 @@
             return false;
         }
         return mShowLockscreenNotifications
-                && !getEntryManager().getNotificationData().isAmbient(sbn.getKey());
+                && getEntryManager().getNotificationData().getImportance(sbn.getKey())
+                >= IMPORTANCE_DEFAULT;
     }
 
     private void setShowLockscreenNotifications(boolean show) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c560301..df99a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -124,7 +125,8 @@
 
     // Dismiss action to be launched when we stop dozing or the keyguard is gone.
     private DismissWithActionRequest mPendingWakeupAction;
-    private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+    private final KeyguardMonitorImpl mKeyguardMonitor =
+            (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
     private final NotificationMediaManager mMediaManager =
             Dependency.get(NotificationMediaManager.class);
 
@@ -202,6 +204,8 @@
     public void show(Bundle options) {
         mShowing = true;
         mStatusBarWindowController.setKeyguardShowing(true);
+        mKeyguardMonitor.notifyKeyguardState(
+                mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
         reset(true /* hideBouncerWhenShowing */);
         StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
             StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
@@ -424,6 +428,8 @@
      */
     public void hide(long startTime, long fadeoutDuration) {
         mShowing = false;
+        mKeyguardMonitor.notifyKeyguardState(
+                mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
         launchPendingWakeupAction();
 
         if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fbceade..097405a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2068,7 +2068,9 @@
                 && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
             return false;
         }
+
         userState.mServiceToEnableWithShortcut = componentNameToEnable;
+        scheduleNotifyClientsOfServicesStateChange(userState);
         return true;
     }
 
@@ -2343,10 +2345,10 @@
     @Override
     public void performAccessibilityShortcut() {
         if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
-                && (mContext.checkCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+                && (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
                 != PackageManager.PERMISSION_GRANTED)) {
             throw new SecurityException(
-                    "performAccessibilityShortcut requires the WRITE_SECURE_SETTINGS permission");
+                    "performAccessibilityShortcut requires the MANAGE_ACCESSIBILITY permission");
         }
         final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
@@ -2381,6 +2383,19 @@
         }
     };
 
+    @Override
+    public String getAccessibilityShortcutService() {
+        if (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
+        }
+        synchronized(mLock) {
+            final UserState userState = getUserStateLocked(mCurrentUserId);
+            return userState.mServiceToEnableWithShortcut.flattenToString();
+        }
+    }
+
     /**
      * Enables accessibility service specified by {@param componentName} for the {@param userId}.
      */
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index a72470d..05293b5 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -373,7 +373,8 @@
         return getOrCreateStack(windowingMode, activityType, onTop);
     }
 
-    private int getNextStackId() {
+    @VisibleForTesting
+    int getNextStackId() {
         return sNextFreeStackId++;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 026c5cc..0049e22 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -48,41 +48,7 @@
 
 import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
 import static com.android.server.am.ActivityDisplay.POSITION_TOP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
@@ -102,8 +68,42 @@
 import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+
 import static java.lang.Integer.MAX_VALUE;
 
 import android.app.Activity;
@@ -1807,13 +1807,6 @@
             return false;
         }
 
-        final ActivityRecord top = topRunningActivityLocked();
-        if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
-            // Shouldn't be visible if you don't have any running activities, not starting one, and
-            // not the top stack on display.
-            return false;
-        }
-
         final ActivityDisplay display = getDisplay();
         boolean gotSplitScreenStack = false;
         boolean gotOpaqueSplitScreenPrimary = false;
@@ -1822,9 +1815,16 @@
         final boolean isAssistantType = isActivityTypeAssistant();
         for (int i = display.getChildCount() - 1; i >= 0; --i) {
             final ActivityStack other = display.getChildAt(i);
+            final boolean hasRunningActivities = other.topRunningActivityLocked() != null;
             if (other == this) {
-                // Should be visible if there is no other stack occluding it.
-                return true;
+                // Should be visible if there is no other stack occluding it, unless it doesn't
+                // have any running activities, not starting one and not home stack.
+                return hasRunningActivities || isInStackLocked(starting) != null
+                        || isActivityTypeHome();
+            }
+
+            if (!hasRunningActivities) {
+                continue;
             }
 
             final int otherWindowingMode = other.getWindowingMode();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 6d3a3b6..dc3bfbc 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1627,7 +1627,8 @@
         // fold tethering stats and operations into uid snapshot
         final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
         tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
-        NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
+        NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot,
+                mUseBpfTrafficStats);
         uidSnapshot.combineAllValues(tetherSnapshot);
 
         final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
@@ -1637,7 +1638,8 @@
         final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
         if (vtStats != null) {
             vtStats.filter(UID_ALL, ifaces, TAG_ALL);
-            NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats);
+            NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
+                    mUseBpfTrafficStats);
             uidSnapshot.combineAllValues(vtStats);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
index 0da5742..59c4067 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -60,8 +60,7 @@
     public void testLastFocusedStackIsUpdatedWhenMovingStack() {
         // Create a stack at bottom.
         final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack stack = display.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, !ON_TOP);
+        final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
         final ActivityStack prevFocusedStack = display.getFocusedStack();
 
         stack.moveToFront("moveStackToFront");
@@ -140,16 +139,14 @@
      */
     @Test
     public void testTopRunningActivity() {
-        // Create stack to hold focus.
         final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
         final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
+        final ActivityStack stack = new StackBuilder(mSupervisor).build();
+        final ActivityRecord activity = stack.getTopActivity();
+
+        // Create empty stack on top.
+        final ActivityStack emptyStack =
+                new StackBuilder(mSupervisor).setCreateActivity(false).build();
 
         // Make sure the top running activity is not affected when keyguard is not locked.
         assertTopRunningActivity(activity, display);
@@ -159,8 +156,8 @@
         assertEquals(activity, display.topRunningActivity());
         assertNull(display.topRunningActivity(true /* considerKeyguardState */));
 
-        // Change focus to stack with activity.
-        stack.moveToFront("focusChangeToTestStack");
+        // Move stack with activity to top.
+        stack.moveToFront("testStackToFront");
         assertEquals(stack, display.getFocusedStack());
         assertEquals(activity, display.topRunningActivity());
         assertNull(display.topRunningActivity(true /* considerKeyguardState */));
@@ -175,11 +172,10 @@
         // Ensure the show when locked activity is returned.
         assertTopRunningActivity(showWhenLockedActivity, display);
 
-        // Change focus back to empty stack.
-        emptyStack.moveToFront("focusChangeToEmptyStack");
-        assertEquals(emptyStack, display.getFocusedStack());
-        // If there is no running activity in focused stack, the running activity in next focusable
-        // stack should be returned.
+        // Move empty stack to front. The running activity in focusable stack which below the
+        // empty stack should be returned.
+        emptyStack.moveToFront("emptyStackToFront");
+        assertEquals(stack, display.getFocusedStack());
         assertTopRunningActivity(showWhenLockedActivity, display);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index ffc7fa2..1015008 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -79,10 +79,9 @@
         super.setUp();
 
         setupActivityTaskManagerService();
-        mStack = mSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
-        mActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        mStack = new StackBuilder(mSupervisor).build();
+        mTask = mStack.getChildAt(0);
+        mActivity = mTask.getTopActivity();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 2c993d3..bdceec7 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -359,15 +359,12 @@
     public void testFindTaskToMoveToFrontWhenRecentsOnTop() throws Exception {
         // Create stack/task on default display.
         final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
+        final TaskRecord targetTask = targetStack.getChildAt(0);
 
         // Create Recents on top of the display.
-        final ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        new ActivityBuilder(mService).setTask(task).build();
+        final ActivityStack stack =
+                new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
 
         final String reason = "findTaskToMoveToFront";
         mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 53f67af..ea33de74 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -26,8 +26,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
@@ -361,14 +361,12 @@
                 translucentStack.topRunningActivityLocked();
         topRunningTranslucentActivity.finishing = true;
 
-        // Home shouldn't be visible since its activity is marked as finishing and it isn't the top
-        // of the stack list.
-        assertFalse(homeStack.shouldBeVisible(null /* starting */));
+        // Home stack should be visible even there are no running activities.
+        assertTrue(homeStack.shouldBeVisible(null /* starting */));
         // Home should be visible if we are starting an activity within it.
         assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
-        // The translucent stack should be visible since it is the top of the stack list even though
-        // it has its activity marked as finishing.
-        assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+        // The translucent stack shouldn't be visible since its activity marked as finishing.
+        assertFalse(translucentStack.shouldBeVisible(null /* starting */));
     }
 
     @Test
@@ -672,9 +670,10 @@
 
         // There is still an activity1 in stack1 so the activity2 should be added to finishing list
         // that will be destroyed until idle.
+        stack2.getTopActivity().visible = true;
         final ActivityRecord activity2 = finishCurrentActivity(stack2);
-        assertEquals(FINISHING, activity2.getState());
-        assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
+        assertEquals(STOPPING, activity2.getState());
+        assertTrue(mSupervisor.mStoppingActivities.contains(activity2));
 
         // The display becomes empty. Since there is no next activity to be idle, the activity
         // should be destroyed immediately with updating configuration to restore original state.
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 094241e..adf861b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -39,14 +39,6 @@
 
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
-import android.content.pm.PackageManagerInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.DisplayWindowController;
-
-import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
@@ -54,6 +46,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
@@ -73,7 +66,10 @@
 import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.ServiceThread;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.AppWindowContainerController;
+import com.android.server.wm.DisplayWindowController;
 import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.RootWindowContainerController;
 import com.android.server.wm.StackWindowController;
@@ -83,7 +79,9 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
 
 import java.io.File;
 import java.util.List;
@@ -608,23 +606,9 @@
         @Override
         <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
                 int stackId, boolean onTop) {
-            if (windowingMode == WINDOWING_MODE_PINNED) {
-                return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop) {
-                    @Override
-                    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
-                        return new Rect(50, 50, 100, 100);
-                    }
-
-                    @Override
-                    PinnedStackWindowController createStackWindowController(int displayId,
-                            boolean onTop, Rect outBounds) {
-                        return mock(PinnedStackWindowController.class);
-                    }
-                };
-            } else {
-                return (T) new TestActivityStack(
-                        this, stackId, mSupervisor, windowingMode, activityType, onTop);
-            }
+            return new StackBuilder(mSupervisor).setDisplay(this)
+                    .setWindowingMode(windowingMode).setActivityType(activityType)
+                    .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
         }
 
         @Override
@@ -677,8 +661,19 @@
         private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
 
         TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-                int windowingMode, int activityType, boolean onTop) {
+                int windowingMode, int activityType, boolean onTop, boolean createActivity) {
             super(display, stackId, supervisor, windowingMode, activityType, onTop);
+            if (createActivity) {
+                new ActivityBuilder(mService).setCreateTask(true).setStack(this).build();
+                if (onTop) {
+                    // We move the task to front again in order to regain focus after activity
+                    // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack}
+                    // could be other stacks (e.g. home stack).
+                    moveToFront("createActivityStack");
+                } else {
+                    moveToBack("createActivityStack", null);
+                }
+            }
         }
 
         @Override
@@ -750,4 +745,71 @@
                                  ActivityOptions options) {
         }
     }
+
+    protected static class StackBuilder {
+        private final ActivityStackSupervisor mSupervisor;
+        private ActivityDisplay mDisplay;
+        private int mStackId = -1;
+        private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        private int mActivityType = ACTIVITY_TYPE_STANDARD;
+        private boolean mOnTop = true;
+        private boolean mCreateActivity = true;
+
+        StackBuilder(ActivityStackSupervisor supervisor) {
+            mSupervisor = supervisor;
+            mDisplay = mSupervisor.getDefaultDisplay();
+        }
+
+        StackBuilder setWindowingMode(int windowingMode) {
+            mWindowingMode = windowingMode;
+            return this;
+        }
+
+        StackBuilder setActivityType(int activityType) {
+            mActivityType = activityType;
+            return this;
+        }
+
+        StackBuilder setStackId(int stackId) {
+            mStackId = stackId;
+            return this;
+        }
+
+        StackBuilder setDisplay(ActivityDisplay display) {
+            mDisplay = display;
+            return this;
+        }
+
+        StackBuilder setOnTop(boolean onTop) {
+            mOnTop = onTop;
+            return this;
+        }
+
+        StackBuilder setCreateActivity(boolean createActivity) {
+            mCreateActivity = createActivity;
+            return this;
+        }
+
+        <T extends ActivityStack> T build() {
+            final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
+            if (mWindowingMode == WINDOWING_MODE_PINNED) {
+                return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
+                    @Override
+                    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
+                        return new Rect(50, 50, 100, 100);
+                    }
+
+                    @Override
+                    PinnedStackWindowController createStackWindowController(int displayId,
+                            boolean onTop, Rect outBounds) {
+                        return mock(PinnedStackWindowController.class);
+                    }
+                };
+            } else {
+                return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
+                        mActivityType, mOnTop, mCreateActivity);
+            }
+        }
+
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 27e8c63..129b835 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -883,7 +883,7 @@
 
         MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
             super(display, LAST_STACK_ID++, supervisor, WINDOWING_MODE_FULLSCREEN,
-                    ACTIVITY_TYPE_STANDARD, true);
+                    ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
             mDisplay = display;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index aa3046f..849a411 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -16,9 +16,7 @@
 
 package com.android.server.am;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -30,7 +28,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
@@ -72,8 +69,8 @@
 
         final int numStacks = 2;
         for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
-            final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor,
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
+            final ActivityStack stack =
+                    new StackBuilder(mSupervisor).setCreateActivity(false).build();
             display.addChild(stack, POSITION_BOTTOM);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 047addd..793d6b0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -61,6 +61,7 @@
 
     private static final long TIME_30_MIN = 30 * 60_000L;
     private static final long TIME_10_MIN = 10 * 60_000L;
+    private static final long TIME_1_MIN = 10 * 60_000L;
 
     private static final long MAX_OBSERVER_PER_UID = 10;
     private static final long MIN_TIME_LIMIT = 4_000L;
@@ -77,7 +78,8 @@
             PKG_GAME1, PKG_GAME2
     };
 
-    private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+    private CountDownLatch mLimitReachedLatch = new CountDownLatch(1);
+    private CountDownLatch mSessionEndLatch = new CountDownLatch(1);
 
     private AppTimeLimitController mController;
 
@@ -85,18 +87,24 @@
 
     private long mUptimeMillis;
 
-    AppTimeLimitController.OnLimitReachedListener mListener
-            = new AppTimeLimitController.OnLimitReachedListener() {
+    AppTimeLimitController.TimeLimitCallbackListener mListener =
+            new AppTimeLimitController.TimeLimitCallbackListener() {
+                @Override
+                public void onLimitReached(int observerId, int userId, long timeLimit,
+                        long timeElapsed,
+                        PendingIntent callbackIntent) {
+                    mLimitReachedLatch.countDown();
+                }
 
-        @Override
-        public void onLimitReached(int observerId, int userId, long timeLimit, long timeElapsed,
-                PendingIntent callbackIntent) {
-            mCountDownLatch.countDown();
-        }
-    };
+                @Override
+                public void onSessionEnd(int observerId, int userId, long timeElapsed,
+                        PendingIntent callbackIntent) {
+                    mSessionEndLatch.countDown();
+                }
+            };
 
     class MyAppTimeLimitController extends AppTimeLimitController {
-        MyAppTimeLimitController(AppTimeLimitController.OnLimitReachedListener listener,
+        MyAppTimeLimitController(AppTimeLimitController.TimeLimitCallbackListener listener,
                 Looper looper) {
             super(listener, looper);
         }
@@ -107,7 +115,12 @@
         }
 
         @Override
-        protected long getObserverPerUidLimit() {
+        protected long getAppUsageObserverPerUidLimit() {
+            return MAX_OBSERVER_PER_UID;
+        }
+
+        @Override
+        protected long getUsageSessionObserverPerUidLimit() {
             return MAX_OBSERVER_PER_UID;
         }
 
@@ -129,188 +142,551 @@
         mThread.quit();
     }
 
-    /** Verify observer is added */
+    /** Verify app usage observer is added */
     @Test
-    public void testAddObserver() {
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
-        addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID2));
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
+    public void testAppUsageObserver_AddObserver() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        addAppUsageObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID2));
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
     }
 
-    /** Verify observer is removed */
+    /** Verify usage session observer is added */
     @Test
-    public void testRemoveObserver() {
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
-        mController.removeObserver(UID, OBS_ID1, USER_ID);
-        assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+    public void testUsageSessionObserver_AddObserver() {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+        addUsageSessionObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID2));
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Verify app usage observer is removed */
+    @Test
+    public void testAppUsageObserver_RemoveObserver() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        mController.removeAppUsageObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /** Verify usage session observer is removed */
+    @Test
+    public void testUsageSessionObserver_RemoveObserver() {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+        mController.removeUsageSessionObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
     }
 
     /** Re-adding an observer should result in only one copy */
     @Test
-    public void testObserverReAdd() {
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
-        addObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+    public void testAppUsageObserver_ObserverReAdd() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
         assertTrue("Observer wasn't added",
-                mController.getObserverGroup(OBS_ID1, USER_ID).timeLimit == TIME_10_MIN);
-        mController.removeObserver(UID, OBS_ID1, USER_ID);
-        assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+                mController.getAppUsageGroup(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
+        mController.removeAppUsageObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /** Re-adding an observer should result in only one copy */
+    @Test
+    public void testUsageSessionObserver_ObserverReAdd() {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_10_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added",
+                mController.getSessionUsageGroup(UID, OBS_ID1).getTimeLimitMs() == TIME_10_MIN);
+        mController.removeUsageSessionObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Different type observers can be registered to the same observerId value */
+    @Test
+    public void testAllObservers_ExclusiveObserverIds() {
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        assertTrue("Observer wasn't added", hasAppUsageObserver(UID, OBS_ID1));
+        assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
+
+        AppTimeLimitController.UsageGroup appUsageGroup = mController.getAppUsageGroup(UID,
+                OBS_ID1);
+        AppTimeLimitController.UsageGroup sessionUsageGroup = mController.getSessionUsageGroup(UID,
+                OBS_ID1);
+
+        // Verify data still intact
+        assertEquals(TIME_10_MIN, appUsageGroup.getTimeLimitMs());
+        assertEquals(TIME_30_MIN, sessionUsageGroup.getTimeLimitMs());
     }
 
     /** Verify that usage across different apps within a group are added up */
     @Test
-    public void testAccumulation() throws Exception {
+    public void testAppUsageObserver_Accumulation() throws Exception {
         setTime(0L);
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        moveToForeground(PKG_SOC1);
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        startUsage(PKG_SOC1);
         // Add 10 mins
         setTime(TIME_10_MIN);
-        moveToBackground(PKG_SOC1);
+        stopUsage(PKG_SOC1);
 
-        long timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+        AppTimeLimitController.UsageGroup group = mController.getAppUsageGroup(UID, OBS_ID1);
+
+        long timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
         assertEquals(TIME_10_MIN * 2, timeRemaining);
 
-        moveToForeground(PKG_SOC1);
+        startUsage(PKG_SOC1);
         setTime(TIME_10_MIN * 2);
-        moveToBackground(PKG_SOC1);
+        stopUsage(PKG_SOC1);
 
-        timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+        timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
         assertEquals(TIME_10_MIN, timeRemaining);
 
         setTime(TIME_30_MIN);
 
-        assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
 
         // Add a different package in the group
-        moveToForeground(PKG_GAME1);
+        startUsage(PKG_GAME1);
         setTime(TIME_30_MIN + TIME_10_MIN);
-        moveToBackground(PKG_GAME1);
+        stopUsage(PKG_GAME1);
 
-        assertEquals(0, mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining);
-        assertTrue(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+        assertEquals(0, group.getTimeLimitMs() - group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+    }
+
+    /** Verify that usage across different apps within a group are added up */
+    @Test
+    public void testUsageSessionObserver_Accumulation() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        startUsage(PKG_SOC1);
+        // Add 10 mins
+        setTime(TIME_10_MIN);
+        stopUsage(PKG_SOC1);
+
+        AppTimeLimitController.UsageGroup group = mController.getSessionUsageGroup(UID, OBS_ID1);
+
+        long timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+        assertEquals(TIME_10_MIN * 2, timeRemaining);
+
+        startUsage(PKG_SOC1);
+        setTime(TIME_10_MIN * 2);
+        stopUsage(PKG_SOC1);
+
+        timeRemaining = group.getTimeLimitMs() - group.getUsageTimeMs();
+        assertEquals(TIME_10_MIN, timeRemaining);
+
+        setTime(TIME_30_MIN);
+
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+        // Add a different package in the group
+        startUsage(PKG_GAME1);
+        setTime(TIME_30_MIN + TIME_10_MIN);
+        stopUsage(PKG_GAME1);
+
+        assertEquals(0, group.getTimeLimitMs() - group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
     }
 
     /** Verify that time limit does not get triggered due to a different app */
     @Test
-    public void testTimeoutOtherApp() throws Exception {
+    public void testAppUsageObserver_TimeoutOtherApp() throws Exception {
         setTime(0L);
-        addObserver(OBS_ID1, GROUP1, 4_000L);
-        moveToForeground(PKG_SOC2);
-        assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        addAppUsageObserver(OBS_ID1, GROUP1, 4_000L);
+        startUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
         setTime(6_000L);
-        moveToBackground(PKG_SOC2);
-        assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+    }
+
+    /** Verify that time limit does not get triggered due to a different app */
+    @Test
+    public void testUsageSessionObserver_TimeoutOtherApp() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 4_000L, 1_000L);
+        startUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        setTime(6_000L);
+        stopUsage(PKG_SOC2);
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
     }
 
     /** Verify the timeout message is delivered at the right time */
     @Test
-    public void testTimeout() throws Exception {
+    public void testAppUsageObserver_Timeout() throws Exception {
         setTime(0L);
-        addObserver(OBS_ID1, GROUP1, 4_000L);
-        moveToForeground(PKG_SOC1);
+        addAppUsageObserver(OBS_ID1, GROUP1, 4_000L);
+        startUsage(PKG_SOC1);
         setTime(6_000L);
-        assertTrue(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
-        moveToBackground(PKG_SOC1);
+        assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
         // Verify that the observer was removed
-        assertFalse(hasObserver(OBS_ID1));
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /** Verify the timeout message is delivered at the right time */
+    @Test
+    public void testUsageSessionObserver_Timeout() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 4_000L, 1_000L);
+        startUsage(PKG_SOC1);
+        setTime(6_000L);
+        assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in a second. Verify session end occurs in a second
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was not removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
     }
 
     /** If an app was already running, make sure it is partially counted towards the time limit */
     @Test
-    public void testAlreadyRunning() throws Exception {
+    public void testAppUsageObserver_AlreadyRunning() throws Exception {
         setTime(TIME_10_MIN);
-        moveToForeground(PKG_GAME1);
+        startUsage(PKG_GAME1);
         setTime(TIME_30_MIN);
-        addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
         setTime(TIME_30_MIN + TIME_10_MIN);
-        moveToBackground(PKG_GAME1);
-        assertFalse(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_GAME1);
+        assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
 
-        moveToForeground(PKG_GAME2);
+        startUsage(PKG_GAME2);
         setTime(TIME_30_MIN + TIME_30_MIN);
-        moveToBackground(PKG_GAME2);
-        assertTrue(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_GAME2);
+        assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
         // Verify that the observer was removed
-        assertFalse(hasObserver(OBS_ID2));
+        assertFalse(hasAppUsageObserver(UID, OBS_ID2));
+    }
+
+    /** If an app was already running, make sure it is partially counted towards the time limit */
+    @Test
+    public void testUsageSessionObserver_AlreadyRunning() throws Exception {
+        setTime(TIME_10_MIN);
+        startUsage(PKG_GAME1);
+        setTime(TIME_30_MIN);
+        addUsageSessionObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_1_MIN);
+        setTime(TIME_30_MIN + TIME_10_MIN);
+        stopUsage(PKG_GAME1);
+        assertFalse(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+
+        startUsage(PKG_GAME2);
+        setTime(TIME_30_MIN + TIME_30_MIN);
+        stopUsage(PKG_GAME2);
+        assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID2));
     }
 
     /** If watched app is already running, verify the timeout callback happens at the right time */
     @Test
-    public void testAlreadyRunningTimeout() throws Exception {
+    public void testAppUsageObserver_AlreadyRunningTimeout() throws Exception {
         setTime(0);
-        moveToForeground(PKG_SOC1);
+        startUsage(PKG_SOC1);
         setTime(TIME_10_MIN);
         // 10 second time limit
-        addObserver(OBS_ID1, GROUP_SOC, 10_000L);
+        addAppUsageObserver(OBS_ID1, GROUP_SOC, 10_000L);
         setTime(TIME_10_MIN + 5_000L);
         // Shouldn't call back in 6 seconds
-        assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
         setTime(TIME_10_MIN + 10_000L);
         // Should call back by 11 seconds (6 earlier + 5 now)
-        assertTrue(mCountDownLatch.await(5_000L, TimeUnit.MILLISECONDS));
+        assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
         // Verify that the observer was removed
-        assertFalse(hasObserver(OBS_ID1));
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
     }
 
-    /** Verify that App Time Limit Controller will limit the number of observerIds */
+    /** If watched app is already running, verify the timeout callback happens at the right time */
     @Test
-    public void testMaxObserverLimit() throws Exception {
+    public void testUsageSessionObserver_AlreadyRunningTimeout() throws Exception {
+        setTime(0);
+        startUsage(PKG_SOC1);
+        setTime(TIME_10_MIN);
+        // 10 second time limit
+        addUsageSessionObserver(OBS_ID1, GROUP_SOC, 10_000L, 1_000L);
+        setTime(TIME_10_MIN + 5_000L);
+        // Shouldn't call back in 6 seconds
+        assertFalse(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        setTime(TIME_10_MIN + 10_000L);
+        // Should call back by 11 seconds (6 earlier + 5 now)
+        assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in a second. Verify session end occurs in a second
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /**
+     * Verify that App Time Limit Controller will limit the number of observerIds for app usage
+     * observers
+     */
+    @Test
+    public void testAppUsageObserver_MaxObserverLimit() throws Exception {
         boolean receivedException = false;
         int ANOTHER_UID = UID + 1;
-        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID2, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID3, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID4, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID5, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID6, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID7, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID8, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID9, GROUP1, TIME_30_MIN);
-        addObserver(OBS_ID10, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID2, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID3, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID4, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID6, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID7, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID8, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID9, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID10, GROUP1, TIME_30_MIN);
         // Readding an observer should not cause an IllegalStateException
-        addObserver(OBS_ID5, GROUP1, TIME_30_MIN);
+        addAppUsageObserver(OBS_ID5, GROUP1, TIME_30_MIN);
         // Adding an observer for a different uid shouldn't cause an IllegalStateException
-        mController.addObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
+        mController.addAppUsageObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, null, USER_ID);
         try {
-            addObserver(OBS_ID11, GROUP1, TIME_30_MIN);
+            addAppUsageObserver(OBS_ID11, GROUP1, TIME_30_MIN);
         } catch (IllegalStateException ise) {
             receivedException = true;
         }
         assertTrue("Should have caused an IllegalStateException", receivedException);
     }
 
-    /** Verify that addObserver minimum time limit is one minute */
+    /**
+     * Verify that App Time Limit Controller will limit the number of observerIds for usage session
+     * observers
+     */
     @Test
-    public void testMinimumTimeLimit() throws Exception {
+    public void testUsageSessionObserver_MaxObserverLimit() throws Exception {
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        boolean receivedException = false;
+        int ANOTHER_UID = UID + 1;
+        addUsageSessionObserver(OBS_ID2, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID3, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID4, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID6, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID7, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID8, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID9, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        addUsageSessionObserver(OBS_ID10, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        // Readding an observer should not cause an IllegalStateException
+        addUsageSessionObserver(OBS_ID5, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        // Adding an observer for a different uid shouldn't cause an IllegalStateException
+        mController.addUsageSessionObserver(ANOTHER_UID, OBS_ID11, GROUP1, TIME_30_MIN, TIME_1_MIN,
+                null, null, USER_ID);
+        try {
+            addUsageSessionObserver(OBS_ID11, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        } catch (IllegalStateException ise) {
+            receivedException = true;
+        }
+        assertTrue("Should have caused an IllegalStateException", receivedException);
+    }
+
+    /** Verify that addAppUsageObserver minimum time limit is one minute */
+    @Test
+    public void testAppUsageObserver_MinimumTimeLimit() throws Exception {
         boolean receivedException = false;
         // adding an observer with a one minute time limit should not cause an exception
-        addObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
+        addAppUsageObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT);
         try {
-            addObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
+            addAppUsageObserver(OBS_ID1, GROUP1, MIN_TIME_LIMIT - 1);
         } catch (IllegalArgumentException iae) {
             receivedException = true;
         }
         assertTrue("Should have caused an IllegalArgumentException", receivedException);
     }
 
-    private void moveToForeground(String packageName) {
-        mController.moveToForeground(packageName, "class", USER_ID);
+    /** Verify that addUsageSessionObserver minimum time limit is one minute */
+    @Test
+    public void testUsageSessionObserver_MinimumTimeLimit() throws Exception {
+        boolean receivedException = false;
+        // test also for session observers
+        addUsageSessionObserver(OBS_ID10, GROUP1, MIN_TIME_LIMIT, TIME_1_MIN);
+        try {
+            addUsageSessionObserver(OBS_ID10, GROUP1, MIN_TIME_LIMIT - 1, TIME_1_MIN);
+        } catch (IllegalArgumentException iae) {
+            receivedException = true;
+        }
+        assertTrue("Should have caused an IllegalArgumentException", receivedException);
     }
 
-    private void moveToBackground(String packageName) {
-        mController.moveToBackground(packageName, "class", USER_ID);
+    /** Verify that concurrent usage from multiple apps in the same group will counted correctly */
+    @Test
+    public void testAppUsageObserver_ConcurrentUsage() throws Exception {
+        setTime(0L);
+        addAppUsageObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        AppTimeLimitController.UsageGroup group = mController.getAppUsageGroup(UID, OBS_ID1);
+        startUsage(PKG_SOC1);
+        // Add 10 mins
+        setTime(TIME_10_MIN);
+
+        // Add a different package in the group will first package is still in use
+        startUsage(PKG_GAME1);
+        setTime(TIME_10_MIN * 2);
+        // Stop first package usage
+        stopUsage(PKG_SOC1);
+
+        setTime(TIME_30_MIN);
+        stopUsage(PKG_GAME1);
+
+        assertEquals(TIME_30_MIN, group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
     }
 
-    private void addObserver(int observerId, String[] packages, long timeLimit) {
-        mController.addObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+    /** Verify that concurrent usage from multiple apps in the same group will counted correctly */
+    @Test
+    public void testUsageSessionObserver_ConcurrentUsage() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, TIME_30_MIN, TIME_1_MIN);
+        AppTimeLimitController.UsageGroup group = mController.getSessionUsageGroup(UID, OBS_ID1);
+        startUsage(PKG_SOC1);
+        // Add 10 mins
+        setTime(TIME_10_MIN);
+
+        // Add a different package in the group will first package is still in use
+        startUsage(PKG_GAME1);
+        setTime(TIME_10_MIN * 2);
+        // Stop first package usage
+        stopUsage(PKG_SOC1);
+
+        setTime(TIME_30_MIN);
+        stopUsage(PKG_GAME1);
+
+        assertEquals(TIME_30_MIN, group.getUsageTimeMs());
+        assertTrue(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
     }
 
-    /** Is there still an observer by that id */
-    private boolean hasObserver(int observerId) {
-        return mController.getObserverGroup(observerId, USER_ID) != null;
+    /** Verify that a session will continue if usage starts again within the session threshold */
+    @Test
+    public void testUsageSessionObserver_ContinueSession() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 2_000L);
+        startUsage(PKG_SOC1);
+        setTime(6_000L);
+        stopUsage(PKG_SOC1);
+        // Wait momentarily, Session should not end
+        assertFalse(mSessionEndLatch.await(1_000L, TimeUnit.MILLISECONDS));
+
+        setTime(7_000L);
+        startUsage(PKG_SOC1);
+        setTime(10_500L);
+        stopUsage(PKG_SOC1);
+        // Total usage time has not reached the limit. Time limit callback should not fire yet
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+        setTime(10_600L);
+        startUsage(PKG_SOC1);
+        setTime(12_000L);
+        assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in 2 seconds. Verify session end occurs
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(1_900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was not removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Verify that a new session will start if next usage starts after the session threshold */
+    @Test
+    public void testUsageSessionObserver_NewSession() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 1_000L);
+        startUsage(PKG_SOC1);
+        setTime(6_000L);
+        stopUsage(PKG_SOC1);
+        // Wait for longer than the session threshold. Session end callback should not be triggered
+        // because the usage timelimit hasn't been triggered.
+        assertFalse(mSessionEndLatch.await(1_500L, TimeUnit.MILLISECONDS));
+
+        setTime(7_500L);
+        // This should be the start of a new session
+        startUsage(PKG_SOC1);
+        setTime(16_000L);
+        stopUsage(PKG_SOC1);
+        // Total usage has exceed the timelimit, but current session time has not
+        assertFalse(mLimitReachedLatch.await(100L, TimeUnit.MILLISECONDS));
+
+        setTime(16_100L);
+        startUsage(PKG_SOC1);
+        setTime(18_000L);
+        assertTrue(mLimitReachedLatch.await(2000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in 2 seconds. Verify session end occurs
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was not removed
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    /** Verify that the callbacks will be triggered for multiple sessions */
+    @Test
+    public void testUsageSessionObserver_RepeatSessions() throws Exception {
+        setTime(0L);
+        addUsageSessionObserver(OBS_ID1, GROUP1, 10_000L, 1_000L);
+        startUsage(PKG_SOC1);
+        setTime(9_000L);
+        stopUsage(PKG_SOC1);
+        // Stutter usage here, to reduce real world time needed trigger limit reached callback
+        startUsage(PKG_SOC1);
+        setTime(11_000L);
+        assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Usage has stopped, Session should end in 1 seconds. Verify session end occurs
+        // (+/- 100ms, which is hopefully not too slim a margin)
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+
+        // Rearm the countdown latches
+        mLimitReachedLatch = new CountDownLatch(1);
+        mSessionEndLatch = new CountDownLatch(1);
+
+        // New session start
+        setTime(20_000L);
+        startUsage(PKG_SOC1);
+        setTime(29_000L);
+        stopUsage(PKG_SOC1);
+        startUsage(PKG_SOC1);
+        setTime(31_000L);
+        assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+        assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
+        assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
+    }
+
+    private void startUsage(String packageName) {
+        mController.noteUsageStart(packageName, USER_ID);
+    }
+
+    private void stopUsage(String packageName) {
+        mController.noteUsageStop(packageName, USER_ID);
+    }
+
+    private void addAppUsageObserver(int observerId, String[] packages, long timeLimit) {
+        mController.addAppUsageObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+    }
+
+    private void addUsageSessionObserver(int observerId, String[] packages, long timeLimit,
+            long sessionThreshold) {
+        mController.addUsageSessionObserver(UID, observerId, packages, timeLimit, sessionThreshold,
+                null, null, USER_ID);
+    }
+
+    /** Is there still an app usage observer by that id */
+    private boolean hasAppUsageObserver(int uid, int observerId) {
+        return mController.getAppUsageGroup(uid, observerId) != null;
+    }
+
+    /** Is there still an usage session observer by that id */
+    private boolean hasUsageSessionObserver(int uid, int observerId) {
+        return mController.getSessionUsageGroup(uid, observerId) != null;
     }
 
     private void setTime(long time) {
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 5916b04c..eaaf9b2 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -22,17 +22,16 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
-import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -57,72 +56,432 @@
 
     private final MyHandler mHandler;
 
-    private OnLimitReachedListener mListener;
+    private TimeLimitCallbackListener mListener;
 
     private static final long MAX_OBSERVER_PER_UID = 1000;
 
     private static final long ONE_MINUTE = 60_000L;
 
+    /** Collection of data for each user that has reported usage */
     @GuardedBy("mLock")
     private final SparseArray<UserData> mUsers = new SparseArray<>();
 
-    private static class UserData {
+    /**
+     * Collection of data for each app that is registering observers
+     * WARNING: Entries are currently not removed, based on the assumption there are a small
+     *          fixed number of apps on device that can register observers.
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<ObserverAppData> mObserverApps = new SparseArray<>();
+
+    private class UserData {
         /** userId of the user */
-        private @UserIdInt int userId;
+        private @UserIdInt
+        int userId;
 
-        /** The app that is currently in the foreground */
-        private String currentForegroundedPackage;
+        /** Set of the currently active entities */
+        private final ArraySet<String> currentlyActive = new ArraySet<>();
 
-        /** The time when the current app came to the foreground */
-        private long currentForegroundedTime;
-
-        /** Map from package name for quick lookup */
-        private ArrayMap<String, ArrayList<TimeLimitGroup>> packageMap = new ArrayMap<>();
-
-        /** Map of observerId to details of the time limit group */
-        private SparseArray<TimeLimitGroup> groups = new SparseArray<>();
-
-        /** Map of the number of observerIds registered by uid */
-        private SparseIntArray observerIdCounts = new SparseIntArray();
+        /** Map from entity name for quick lookup */
+        private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
 
         private UserData(@UserIdInt int userId) {
             this.userId = userId;
         }
+
+        @GuardedBy("mLock")
+        boolean isActive(String[] entities) {
+            // TODO: Consider using a bloom filter here if number of actives becomes large
+            final int size = entities.length;
+            for (int i = 0; i < size; i++) {
+                if (currentlyActive.contains(entities[i])) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        void addUsageGroup(UsageGroup group) {
+            final int size = group.mObserved.length;
+            for (int i = 0; i < size; i++) {
+                ArrayList<UsageGroup> list = observedMap.get(group.mObserved[i]);
+                if (list == null) {
+                    list = new ArrayList<>();
+                    observedMap.put(group.mObserved[i], list);
+                }
+                list.add(group);
+            }
+        }
+
+        @GuardedBy("mLock")
+        void removeUsageGroup(UsageGroup group) {
+            final int size = group.mObserved.length;
+            for (int i = 0; i < size; i++) {
+                final ArrayList<UsageGroup> list = observedMap.get(group.mObserved[i]);
+                if (list != null) {
+                    list.remove(group);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            pw.print(" userId=");
+            pw.println(userId);
+            pw.print(" Currently Active:");
+            final int nActive = currentlyActive.size();
+            for (int i = 0; i < nActive; i++) {
+                pw.print(currentlyActive.valueAt(i));
+                pw.print(", ");
+            }
+            pw.println();
+            pw.print(" Observed Entities:");
+            final int nEntities = currentlyActive.size();
+            for (int i = 0; i < nEntities; i++) {
+                pw.print(observedMap.keyAt(i));
+                pw.print(", ");
+            }
+            pw.println();
+        }
+    }
+
+
+    private class ObserverAppData {
+        /** uid of the observing app */
+        private int uid;
+
+        /** Map of observerId to details of the time limit group */
+        SparseArray<AppUsageGroup> appUsageGroups = new SparseArray<>();
+
+        /** Map of observerId to details of the time limit group */
+        SparseArray<SessionUsageGroup> sessionUsageGroups = new SparseArray<>();
+
+        private ObserverAppData(int uid) {
+            this.uid = uid;
+        }
+
+        @GuardedBy("mLock")
+        void removeAppUsageGroup(int observerId) {
+            appUsageGroups.remove(observerId);
+        }
+
+        @GuardedBy("mLock")
+        void removeSessionUsageGroup(int observerId) {
+            sessionUsageGroups.remove(observerId);
+        }
+
+
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            pw.print(" uid=");
+            pw.println(uid);
+            pw.println("    App Usage Groups:");
+            final int nAppUsageGroups = appUsageGroups.size();
+            for (int i = 0; i < nAppUsageGroups; i++) {
+                appUsageGroups.valueAt(i).dump(pw);
+                pw.println();
+            }
+            pw.println("    Session Usage Groups:");
+            final int nSessionUsageGroups = appUsageGroups.size();
+            for (int i = 0; i < nSessionUsageGroups; i++) {
+                sessionUsageGroups.valueAt(i).dump(pw);
+                pw.println();
+            }
+        }
     }
 
     /**
      * Listener interface for being informed when an app group's time limit is reached.
      */
-    public interface OnLimitReachedListener {
+    public interface TimeLimitCallbackListener {
         /**
          * Time limit for a group, keyed by the observerId, has been reached.
-         * @param observerId The observerId of the group whose limit was reached
-         * @param userId The userId
-         * @param timeLimit The original time limit in milliseconds
-         * @param timeElapsed How much time was actually spent on apps in the group, in milliseconds
+         *
+         * @param observerId     The observerId of the group whose limit was reached
+         * @param userId         The userId
+         * @param timeLimit      The original time limit in milliseconds
+         * @param timeElapsed    How much time was actually spent on apps in the group, in
+         *                       milliseconds
          * @param callbackIntent The PendingIntent to send when the limit is reached
          */
         public void onLimitReached(int observerId, @UserIdInt int userId, long timeLimit,
                 long timeElapsed, PendingIntent callbackIntent);
+
+        /**
+         * Session ended for a group, keyed by the observerId, after limit was reached.
+         *
+         * @param observerId     The observerId of the group whose limit was reached
+         * @param userId         The userId
+         * @param timeElapsed    How much time was actually spent on apps in the group, in
+         *                       milliseconds
+         * @param callbackIntent The PendingIntent to send when the limit is reached
+         */
+        public void onSessionEnd(int observerId, @UserIdInt int userId, long timeElapsed,
+                PendingIntent callbackIntent);
     }
 
-    static class TimeLimitGroup {
-        int requestingUid;
-        int observerId;
-        String[] packages;
-        long timeLimit;
-        long timeRequested;
-        long timeRemaining;
-        PendingIntent callbackIntent;
-        String currentPackage;
-        long timeCurrentPackageStarted;
-        int userId;
+    abstract class UsageGroup {
+        protected int mObserverId;
+        protected String[] mObserved;
+        protected long mTimeLimitMs;
+        protected long mUsageTimeMs;
+        protected int mActives;
+        protected long mLastKnownUsageTimeMs;
+        protected WeakReference<UserData> mUserRef;
+        protected WeakReference<ObserverAppData> mObserverAppRef;
+        protected PendingIntent mLimitReachedCallback;
+
+        UsageGroup(UserData user, ObserverAppData observerApp, int observerId, String[] observed,
+                long timeLimitMs, PendingIntent limitReachedCallback) {
+            mUserRef = new WeakReference<>(user);
+            mObserverAppRef = new WeakReference<>(observerApp);
+            mObserverId = observerId;
+            mObserved = observed;
+            mTimeLimitMs = timeLimitMs;
+            mLimitReachedCallback = limitReachedCallback;
+        }
+
+        @GuardedBy("mLock")
+        public long getTimeLimitMs() { return mTimeLimitMs; }
+
+        @GuardedBy("mLock")
+        public long getUsageTimeMs() { return mUsageTimeMs; }
+
+        @GuardedBy("mLock")
+        public void remove() {
+            UserData user = mUserRef.get();
+            if (user != null) {
+                user.removeUsageGroup(this);
+            }
+            // Clear the callback, so any racy inflight message will do nothing
+            mLimitReachedCallback = null;
+        }
+
+        @GuardedBy("mLock")
+        void noteUsageStart(long startTimeMs) {
+            noteUsageStart(startTimeMs, startTimeMs);
+        }
+
+        @GuardedBy("mLock")
+        void noteUsageStart(long startTimeMs, long currentTimeMs) {
+            if (mActives++ == 0) {
+                mLastKnownUsageTimeMs = startTimeMs;
+                final long timeRemaining =
+                        mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs;
+                if (timeRemaining > 0) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Posting timeout for " + mObserverId + " for "
+                                + timeRemaining + "ms");
+                    }
+                    postCheckTimeoutLocked(this, timeRemaining);
+                }
+            } else {
+                if (mActives > mObserved.length) {
+                    // Try to get to a sane state and log the issue
+                    mActives = mObserved.length;
+                    final UserData user = mUserRef.get();
+                    if (user == null) return;
+                    final Object[] array = user.currentlyActive.toArray();
+                    Slog.e(TAG,
+                            "Too many noted usage starts! Observed entities: " + Arrays.toString(
+                                    mObserved) + "   Active Entities: " + Arrays.toString(array));
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        void noteUsageStop(long stopTimeMs) {
+            if (--mActives == 0) {
+                final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs;
+                mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs;
+                if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) {
+                    // Crossed the limit
+                    if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId);
+                    postInformLimitReachedListenerLocked(this);
+                }
+                cancelCheckTimeoutLocked(this);
+            } else {
+                if (mActives < 0) {
+                    // Try to get to a sane state and log the issue
+                    mActives = 0;
+                    final UserData user = mUserRef.get();
+                    if (user == null) return;
+                    final Object[] array = user.currentlyActive.toArray();
+                    Slog.e(TAG,
+                            "Too many noted usage stops! Observed entities: " + Arrays.toString(
+                                    mObserved) + "   Active Entities: " + Arrays.toString(array));
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        void checkTimeout(long currentTimeMs) {
+            final UserData user = mUserRef.get();
+            if (user == null) return;
+
+            long timeRemainingMs = mTimeLimitMs - mUsageTimeMs;
+
+            if (DEBUG) Slog.d(TAG, "checkTimeout timeRemaining=" + timeRemainingMs);
+
+            // Already reached the limit, no need to report again
+            if (timeRemainingMs <= 0) return;
+
+            if (DEBUG) {
+                Slog.d(TAG, "checkTimeout");
+            }
+
+            // Double check that at least one entity in this group is currently active
+            if (user.isActive(mObserved)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "checkTimeout group is active");
+                }
+                final long timeUsedMs = currentTimeMs - mLastKnownUsageTimeMs;
+                if (timeRemainingMs <= timeUsedMs) {
+                    if (DEBUG) Slog.d(TAG, "checkTimeout : Time limit reached");
+                    // Hit the limit, set timeRemaining to zero to avoid checking again
+                    mUsageTimeMs += timeUsedMs;
+                    mLastKnownUsageTimeMs = currentTimeMs;
+                    AppTimeLimitController.this.postInformLimitReachedListenerLocked(this);
+                } else {
+                    if (DEBUG) Slog.d(TAG, "checkTimeout : Some more time remaining");
+                    AppTimeLimitController.this.postCheckTimeoutLocked(this,
+                            timeRemainingMs - timeUsedMs);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        public void onLimitReached() {
+            UserData user = mUserRef.get();
+            if (user == null) return;
+            if (mListener != null) {
+                mListener.onLimitReached(mObserverId, user.userId, mTimeLimitMs, mUsageTimeMs,
+                        mLimitReachedCallback);
+            }
+        }
+
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            pw.print("        Group id=");
+            pw.print(mObserverId);
+            pw.print(" timeLimit=");
+            pw.print(mTimeLimitMs);
+            pw.print(" used=");
+            pw.print(mUsageTimeMs);
+            pw.print(" lastKnownUsage=");
+            pw.print(mLastKnownUsageTimeMs);
+            pw.print(" mActives=");
+            pw.print(mActives);
+            pw.print(" observed=");
+            pw.print(Arrays.toString(mObserved));
+        }
     }
 
+    class AppUsageGroup extends UsageGroup {
+        public AppUsageGroup(UserData user, ObserverAppData observerApp, int observerId,
+                String[] observed, long timeLimitMs, PendingIntent limitReachedCallback) {
+            super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void remove() {
+            super.remove();
+            ObserverAppData observerApp = mObserverAppRef.get();
+            if (observerApp != null) {
+                observerApp.removeAppUsageGroup(mObserverId);
+            }
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void onLimitReached() {
+            super.onLimitReached();
+            // Unregister since the limit has been met and observer was informed.
+            remove();
+        }
+    }
+
+    class SessionUsageGroup extends UsageGroup {
+        private long mLastUsageEndTimeMs;
+        private long mNewSessionThresholdMs;
+        private PendingIntent mSessionEndCallback;
+
+        public SessionUsageGroup(UserData user, ObserverAppData observerApp, int observerId,
+                String[] observed, long timeLimitMs, PendingIntent limitReachedCallback,
+                long newSessionThresholdMs, PendingIntent sessionEndCallback) {
+            super(user, observerApp, observerId, observed, timeLimitMs, limitReachedCallback);
+            this.mNewSessionThresholdMs = newSessionThresholdMs;
+            this.mSessionEndCallback = sessionEndCallback;
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void remove() {
+            super.remove();
+            ObserverAppData observerApp = mObserverAppRef.get();
+            if (observerApp != null) {
+                observerApp.removeSessionUsageGroup(mObserverId);
+            }
+            // Clear the callback, so any racy inflight messages will do nothing
+            mSessionEndCallback = null;
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void noteUsageStart(long startTimeMs, long currentTimeMs) {
+            if (mActives == 0) {
+                if (startTimeMs - mLastUsageEndTimeMs > mNewSessionThresholdMs) {
+                    // New session has started, clear usage time.
+                    mUsageTimeMs = 0;
+                }
+                AppTimeLimitController.this.cancelInformSessionEndListener(this);
+            }
+            super.noteUsageStart(startTimeMs, currentTimeMs);
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        public void noteUsageStop(long stopTimeMs) {
+            super.noteUsageStop(stopTimeMs);
+            if (mActives == 0) {
+                mLastUsageEndTimeMs = stopTimeMs;
+                if (mUsageTimeMs >= mTimeLimitMs) {
+                    // Usage has ended. Schedule the session end callback to be triggered once
+                    // the new session threshold has been reached
+                    AppTimeLimitController.this.postInformSessionEndListenerLocked(this,
+                            mNewSessionThresholdMs);
+                }
+
+            }
+        }
+
+        @GuardedBy("mLock")
+        public void onSessionEnd() {
+            UserData user = mUserRef.get();
+            if (user == null) return;
+            if (mListener != null) {
+                mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback);
+            }
+        }
+
+        @Override
+        @GuardedBy("mLock")
+        void dump(PrintWriter pw) {
+            super.dump(pw);
+            pw.print(" lastUsageEndTime=");
+            pw.print(mLastUsageEndTimeMs);
+            pw.print(" newSessionThreshold=");
+            pw.print(mNewSessionThresholdMs);
+        }
+    }
+
+
     private class MyHandler extends Handler {
-
         static final int MSG_CHECK_TIMEOUT = 1;
-        static final int MSG_INFORM_LISTENER = 2;
+        static final int MSG_INFORM_LIMIT_REACHED_LISTENER = 2;
+        static final int MSG_INFORM_SESSION_END = 3;
 
         MyHandler(Looper looper) {
             super(looper);
@@ -132,10 +491,19 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_CHECK_TIMEOUT:
-                    checkTimeout((TimeLimitGroup) msg.obj);
+                    synchronized (mLock) {
+                        ((UsageGroup) msg.obj).checkTimeout(getUptimeMillis());
+                    }
                     break;
-                case MSG_INFORM_LISTENER:
-                    informListener((TimeLimitGroup) msg.obj);
+                case MSG_INFORM_LIMIT_REACHED_LISTENER:
+                    synchronized (mLock) {
+                        ((UsageGroup) msg.obj).onLimitReached();
+                    }
+                    break;
+                case MSG_INFORM_SESSION_END:
+                    synchronized (mLock) {
+                        ((SessionUsageGroup) msg.obj).onSessionEnd();
+                    }
                     break;
                 default:
                     super.handleMessage(msg);
@@ -144,7 +512,7 @@
         }
     }
 
-    public AppTimeLimitController(OnLimitReachedListener listener, Looper looper) {
+    public AppTimeLimitController(TimeLimitCallbackListener listener, Looper looper) {
         mHandler = new MyHandler(looper);
         mListener = listener;
     }
@@ -157,7 +525,13 @@
 
     /** Overrideable for testing purposes */
     @VisibleForTesting
-    protected long getObserverPerUidLimit() {
+    protected long getAppUsageObserverPerUidLimit() {
+        return MAX_OBSERVER_PER_UID;
+    }
+
+    /** Overrideable for testing purposes */
+    @VisibleForTesting
+    protected long getUsageSessionObserverPerUidLimit() {
         return MAX_OBSERVER_PER_UID;
     }
 
@@ -167,6 +541,21 @@
         return ONE_MINUTE;
     }
 
+    @VisibleForTesting
+    AppUsageGroup getAppUsageGroup(int observerAppUid, int observerId) {
+        synchronized (mLock) {
+            return getOrCreateObserverAppDataLocked(observerAppUid).appUsageGroups.get(observerId);
+        }
+    }
+
+    @VisibleForTesting
+    SessionUsageGroup getSessionUsageGroup(int observerAppUid, int observerId) {
+        synchronized (mLock) {
+            return getOrCreateObserverAppDataLocked(observerAppUid).sessionUsageGroups.get(
+                    observerId);
+        }
+    }
+
     /** Returns an existing UserData object for the given userId, or creates one */
     @GuardedBy("mLock")
     private UserData getOrCreateUserDataLocked(int userId) {
@@ -178,6 +567,17 @@
         return userData;
     }
 
+    /** Returns an existing ObserverAppData object for the given uid, or creates one */
+    @GuardedBy("mLock")
+    private ObserverAppData getOrCreateObserverAppDataLocked(int uid) {
+        ObserverAppData appData = mObserverApps.get(uid);
+        if (appData == null) {
+            appData = new ObserverAppData(uid);
+            mObserverApps.put(uid, appData);
+        }
+        return appData;
+    }
+
     /** Clean up data if user is removed */
     public void onUserRemoved(int userId) {
         synchronized (mLock) {
@@ -187,300 +587,219 @@
     }
 
     /**
-     * Registers an observer with the given details. Existing observer with the same observerId
-     * is removed.
+     * Check if group has any currently active entities.
      */
-    public void addObserver(int requestingUid, int observerId, String[] packages, long timeLimit,
-            PendingIntent callbackIntent, @UserIdInt int userId) {
+    @GuardedBy("mLock")
+    private void noteActiveLocked(UserData user, UsageGroup group, long currentTimeMs) {
+        // TODO: Consider using a bloom filter here if number of actives becomes large
+        final int size = group.mObserved.length;
+        for (int i = 0; i < size; i++) {
+            if (user.currentlyActive.contains(group.mObserved[i])) {
+                // Entity is currently active. Start group's usage.
+                group.noteUsageStart(currentTimeMs);
+            }
+        }
+    }
 
+    /**
+     * Registers an app usage observer with the given details.
+     * Existing app usage observer with the same observerId will be removed.
+     */
+    public void addAppUsageObserver(int requestingUid, int observerId, String[] observed,
+            long timeLimit, PendingIntent callbackIntent, @UserIdInt int userId) {
         if (timeLimit < getMinTimeLimit()) {
             throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
         }
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
-            removeObserverLocked(user, requestingUid, observerId, /*readding =*/ true);
-
-            final int observerIdCount = user.observerIdCounts.get(requestingUid, 0);
-            if (observerIdCount >= getObserverPerUidLimit()) {
-                throw new IllegalStateException(
-                        "Too many observers added by uid " + requestingUid);
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            AppUsageGroup group = observerApp.appUsageGroups.get(observerId);
+            if (group != null) {
+                // Remove previous app usage group associated with observerId
+                observerApp.appUsageGroups.get(observerId).remove();
             }
-            user.observerIdCounts.put(requestingUid, observerIdCount + 1);
 
-            TimeLimitGroup group = new TimeLimitGroup();
-            group.observerId = observerId;
-            group.callbackIntent = callbackIntent;
-            group.packages = packages;
-            group.timeLimit = timeLimit;
-            group.timeRemaining = group.timeLimit;
-            group.timeRequested = getUptimeMillis();
-            group.requestingUid = requestingUid;
-            group.timeCurrentPackageStarted = -1L;
-            group.userId = userId;
-
-            user.groups.append(observerId, group);
-
-            addGroupToPackageMapLocked(user, packages, group);
+            final int observerIdCount = observerApp.appUsageGroups.size();
+            if (observerIdCount >= getAppUsageObserverPerUidLimit()) {
+                throw new IllegalStateException(
+                        "Too many app usage observers added by uid " + requestingUid);
+            }
+            group = new AppUsageGroup(user, observerApp, observerId, observed, timeLimit,
+                    callbackIntent);
+            observerApp.appUsageGroups.append(observerId, group);
 
             if (DEBUG) {
-                Slog.d(TAG, "addObserver " + packages + " for " + timeLimit);
+                Slog.d(TAG, "addObserver " + observed + " for " + timeLimit);
             }
-            // Handle the case where a target package is already in the foreground when observer
-            // is added.
-            if (user.currentForegroundedPackage != null && inPackageList(group.packages,
-                    user.currentForegroundedPackage)) {
-                group.timeCurrentPackageStarted = group.timeRequested;
-                group.currentPackage = user.currentForegroundedPackage;
-                if (group.timeRemaining > 0) {
-                    postCheckTimeoutLocked(group, group.timeRemaining);
-                }
-            }
+
+            user.addUsageGroup(group);
+            noteActiveLocked(user, group, getUptimeMillis());
         }
     }
 
     /**
      * Remove a registered observer by observerId and calling uid.
+     *
      * @param requestingUid The calling uid
-     * @param observerId The unique observer id for this user
-     * @param userId The user id of the observer
+     * @param observerId    The unique observer id for this user
+     * @param userId        The user id of the observer
      */
-    public void removeObserver(int requestingUid, int observerId, @UserIdInt int userId) {
+    public void removeAppUsageObserver(int requestingUid, int observerId, @UserIdInt int userId) {
+        synchronized (mLock) {
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            observerApp.appUsageGroups.get(observerId).remove();
+        }
+    }
+
+
+    /**
+     * Registers a usage session observer with the given details.
+     * Existing usage session observer with the same observerId will be removed.
+     */
+    public void addUsageSessionObserver(int requestingUid, int observerId, String[] observed,
+            long timeLimit, long sessionThresholdTime,
+            PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
+            @UserIdInt int userId) {
+        if (timeLimit < getMinTimeLimit()) {
+            throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
+        }
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
-            removeObserverLocked(user, requestingUid, observerId, /*readding =*/ false);
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            SessionUsageGroup group = observerApp.sessionUsageGroups.get(observerId);
+            if (group != null) {
+                // Remove previous app usage group associated with observerId
+                observerApp.sessionUsageGroups.get(observerId).remove();
+            }
+
+            final int observerIdCount = observerApp.sessionUsageGroups.size();
+            if (observerIdCount >= getUsageSessionObserverPerUidLimit()) {
+                throw new IllegalStateException(
+                        "Too many app usage observers added by uid " + requestingUid);
+            }
+            group = new SessionUsageGroup(user, observerApp, observerId, observed, timeLimit,
+                    limitReachedCallbackIntent, sessionThresholdTime, sessionEndCallbackIntent);
+            observerApp.sessionUsageGroups.append(observerId, group);
+
+            user.addUsageGroup(group);
+            noteActiveLocked(user, group, getUptimeMillis());
         }
     }
 
-    @VisibleForTesting
-    TimeLimitGroup getObserverGroup(int observerId, int userId) {
+    /**
+     * Remove a registered observer by observerId and calling uid.
+     *
+     * @param requestingUid The calling uid
+     * @param observerId    The unique observer id for this user
+     * @param userId        The user id of the observer
+     */
+    public void removeUsageSessionObserver(int requestingUid, int observerId,
+            @UserIdInt int userId) {
         synchronized (mLock) {
-            return getOrCreateUserDataLocked(userId).groups.get(observerId);
+            ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+            observerApp.sessionUsageGroups.get(observerId).remove();
         }
     }
 
-    private static boolean inPackageList(String[] packages, String packageName) {
-        return ArrayUtils.contains(packages, packageName);
+    /**
+     * Called when an entity becomes active.
+     *
+     * @param name   The entity that became active
+     * @param userId The user
+     */
+    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+            if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active");
+            if (user.currentlyActive.contains(name)) {
+                throw new IllegalArgumentException(
+                        "Unable to start usage for " + name + ", already in use");
+            }
+            final long currentTime = getUptimeMillis();
+
+            // Add to the list of active entities
+            user.currentlyActive.add(name);
+
+            ArrayList<UsageGroup> groups = user.observedMap.get(name);
+            if (groups == null) return;
+
+            final int size = groups.size();
+            for (int i = 0; i < size; i++) {
+                UsageGroup group = groups.get(i);
+                group.noteUsageStart(currentTime);
+            }
+        }
+    }
+
+    /**
+     * Called when an entity becomes inactive.
+     *
+     * @param name   The entity that became inactive
+     * @param userId The user
+     */
+    public void noteUsageStop(String name, int userId) throws IllegalArgumentException {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+            if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive");
+            if (!user.currentlyActive.remove(name)) {
+                throw new IllegalArgumentException(
+                        "Unable to stop usage for " + name + ", not in use");
+            }
+            final long currentTime = getUptimeMillis();
+
+            // Check if any of the groups need to watch for this entity
+            ArrayList<UsageGroup> groups = user.observedMap.get(name);
+            if (groups == null) return;
+
+            final int size = groups.size();
+            for (int i = 0; i < size; i++) {
+                UsageGroup group = groups.get(i);
+                group.noteUsageStop(currentTime);
+            }
+        }
     }
 
     @GuardedBy("mLock")
-    private void removeObserverLocked(UserData user, int requestingUid, int observerId,
-            boolean readding) {
-        TimeLimitGroup group = user.groups.get(observerId);
-        if (group != null && group.requestingUid == requestingUid) {
-            removeGroupFromPackageMapLocked(user, group);
-            user.groups.remove(observerId);
-            mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
-            final int observerIdCount = user.observerIdCounts.get(requestingUid);
-            if (observerIdCount <= 1 && !readding) {
-                user.observerIdCounts.delete(requestingUid);
-            } else {
-                user.observerIdCounts.put(requestingUid, observerIdCount - 1);
-            }
-        }
-    }
-
-    /**
-     * Called when an app has moved to the foreground.
-     * @param packageName The app that is foregrounded
-     * @param className The className of the activity
-     * @param userId The user
-     */
-    public void moveToForeground(String packageName, String className, int userId) {
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(userId);
-            if (DEBUG) Slog.d(TAG, "Setting mCurrentForegroundedPackage to " + packageName);
-            // Note the current foreground package
-            user.currentForegroundedPackage = packageName;
-            user.currentForegroundedTime = getUptimeMillis();
-
-            // Check if any of the groups need to watch for this package
-            maybeWatchForPackageLocked(user, packageName, user.currentForegroundedTime);
-        }
-    }
-
-    /**
-     * Called when an app is sent to the background.
-     *
-     * @param packageName
-     * @param className
-     * @param userId
-     */
-    public void moveToBackground(String packageName, String className, int userId) {
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(userId);
-            if (!TextUtils.equals(user.currentForegroundedPackage, packageName)) {
-                Slog.w(TAG, "Eh? Last foregrounded package = " + user.currentForegroundedPackage
-                        + " and now backgrounded = " + packageName);
-                return;
-            }
-            final long stopTime = getUptimeMillis();
-
-            // Add up the usage time to all groups that contain the package
-            ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
-            if (groups != null) {
-                final int size = groups.size();
-                for (int i = 0; i < size; i++) {
-                    final TimeLimitGroup group = groups.get(i);
-                    // Don't continue to send
-                    if (group.timeRemaining <= 0) continue;
-
-                    final long startTime = Math.max(user.currentForegroundedTime,
-                            group.timeRequested);
-                    long diff = stopTime - startTime;
-                    group.timeRemaining -= diff;
-                    if (group.timeRemaining <= 0) {
-                        if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + group.observerId);
-                        postInformListenerLocked(group);
-                    }
-                    // Reset indicators that observer was added when package was already fg
-                    group.currentPackage = null;
-                    group.timeCurrentPackageStarted = -1L;
-                    mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
-                }
-            }
-            user.currentForegroundedPackage = null;
-        }
-    }
-
-    private void postInformListenerLocked(TimeLimitGroup group) {
-        mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MSG_INFORM_LISTENER,
+    private void postInformLimitReachedListenerLocked(UsageGroup group) {
+        mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MSG_INFORM_LIMIT_REACHED_LISTENER,
                 group));
     }
 
-    /**
-     * Inform the observer and unregister it, as the limit has been reached.
-     * @param group the observed group
-     */
-    private void informListener(TimeLimitGroup group) {
-        if (mListener != null) {
-            mListener.onLimitReached(group.observerId, group.userId, group.timeLimit,
-                    group.timeLimit - group.timeRemaining, group.callbackIntent);
-        }
-        // Unregister since the limit has been met and observer was informed.
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(group.userId);
-            removeObserverLocked(user, group.requestingUid, group.observerId, false);
-        }
-    }
-
-    /** Check if any of the groups care about this package and set up delayed messages */
     @GuardedBy("mLock")
-    private void maybeWatchForPackageLocked(UserData user, String packageName, long uptimeMillis) {
-        ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
-        if (groups == null) return;
-
-        final int size = groups.size();
-        for (int i = 0; i < size; i++) {
-            TimeLimitGroup group = groups.get(i);
-            if (group.timeRemaining > 0) {
-                group.timeCurrentPackageStarted = uptimeMillis;
-                group.currentPackage = packageName;
-                if (DEBUG) {
-                    Slog.d(TAG, "Posting timeout for " + packageName + " for "
-                            + group.timeRemaining + "ms");
-                }
-                postCheckTimeoutLocked(group, group.timeRemaining);
-            }
-        }
+    private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
+                timeout);
     }
 
-    private void addGroupToPackageMapLocked(UserData user, String[] packages,
-            TimeLimitGroup group) {
-        for (int i = 0; i < packages.length; i++) {
-            ArrayList<TimeLimitGroup> list = user.packageMap.get(packages[i]);
-            if (list == null) {
-                list = new ArrayList<>();
-                user.packageMap.put(packages[i], list);
-            }
-            list.add(group);
-        }
+    @GuardedBy("mLock")
+    private void cancelInformSessionEndListener(SessionUsageGroup group) {
+        mHandler.removeMessages(MyHandler.MSG_INFORM_SESSION_END, group);
     }
 
-    /**
-     * Remove the group reference from the package to group mapping, which is 1 to many.
-     * @param group The group to remove from the package map.
-     */
-    private void removeGroupFromPackageMapLocked(UserData user, TimeLimitGroup group) {
-        final int mapSize = user.packageMap.size();
-        for (int i = 0; i < mapSize; i++) {
-            ArrayList<TimeLimitGroup> list = user.packageMap.valueAt(i);
-            list.remove(group);
-        }
-    }
-
-    private void postCheckTimeoutLocked(TimeLimitGroup group, long timeout) {
+    @GuardedBy("mLock")
+    private void postCheckTimeoutLocked(UsageGroup group, long timeout) {
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_CHECK_TIMEOUT, group),
                 timeout);
     }
 
-    /**
-     * See if the given group has reached the timeout if the current foreground app is included
-     * and it exceeds the time remaining.
-     * @param group the group of packages to check
-     */
-    void checkTimeout(TimeLimitGroup group) {
-        // For each package in the group, check if any of the currently foregrounded apps are adding
-        // up to hit the limit and inform the observer
-        synchronized (mLock) {
-            UserData user = getOrCreateUserDataLocked(group.userId);
-            // This group doesn't exist anymore, nothing to see here.
-            if (user.groups.get(group.observerId) != group) return;
-
-            if (DEBUG) Slog.d(TAG, "checkTimeout timeRemaining=" + group.timeRemaining);
-
-            // Already reached the limit, no need to report again
-            if (group.timeRemaining <= 0) return;
-
-            if (DEBUG) {
-                Slog.d(TAG, "checkTimeout foregroundedPackage="
-                        + user.currentForegroundedPackage);
-            }
-
-            if (inPackageList(group.packages, user.currentForegroundedPackage)) {
-                if (DEBUG) {
-                    Slog.d(TAG, "checkTimeout package in foreground="
-                            + user.currentForegroundedPackage);
-                }
-                if (group.timeCurrentPackageStarted < 0) {
-                    Slog.w(TAG, "startTime was not set correctly for " + group);
-                }
-                final long timeInForeground = getUptimeMillis() - group.timeCurrentPackageStarted;
-                if (group.timeRemaining <= timeInForeground) {
-                    if (DEBUG) Slog.d(TAG, "checkTimeout : Time limit reached");
-                    // Hit the limit, set timeRemaining to zero to avoid checking again
-                    group.timeRemaining -= timeInForeground;
-                    postInformListenerLocked(group);
-                    // Reset
-                    group.timeCurrentPackageStarted = -1L;
-                    group.currentPackage = null;
-                } else {
-                    if (DEBUG) Slog.d(TAG, "checkTimeout : Some more time remaining");
-                    postCheckTimeoutLocked(group, group.timeRemaining - timeInForeground);
-                }
-            }
-        }
+    @GuardedBy("mLock")
+    private void cancelCheckTimeoutLocked(UsageGroup group) {
+        mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
     }
 
     void dump(PrintWriter pw) {
         synchronized (mLock) {
             pw.println("\n  App Time Limits");
-            int nUsers = mUsers.size();
+            final int nUsers = mUsers.size();
             for (int i = 0; i < nUsers; i++) {
-                UserData user = mUsers.valueAt(i);
-                pw.print("   User "); pw.println(user.userId);
-                int nGroups = user.groups.size();
-                for (int j = 0; j < nGroups; j++) {
-                    TimeLimitGroup group = user.groups.valueAt(j);
-                    pw.print("    Group id="); pw.print(group.observerId);
-                    pw.print(" timeLimit="); pw.print(group.timeLimit);
-                    pw.print(" remaining="); pw.print(group.timeRemaining);
-                    pw.print(" currentPackage="); pw.print(group.currentPackage);
-                    pw.print(" timeCurrentPkgStarted="); pw.print(group.timeCurrentPackageStarted);
-                    pw.print(" packages="); pw.println(Arrays.toString(group.packages));
-                }
-                pw.println();
-                pw.print("    currentForegroundedPackage=");
-                pw.println(user.currentForegroundedPackage);
+                pw.print("   User ");
+                mUsers.valueAt(i).dump(pw);
+            }
+            pw.println();
+            final int nObserverApps = mObserverApps.size();
+            for (int i = 0; i < nObserverApps; i++) {
+                pw.print("   Observer App ");
+                mObserverApps.valueAt(i).dump(pw);
             }
         }
     }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index dd1ddfa..2621252 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -165,16 +165,36 @@
         mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
 
         mAppTimeLimit = new AppTimeLimitController(
-                (observerId, userId, timeLimit, timeElapsed, callbackIntent) -> {
-                    Intent intent = new Intent();
-                    intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
-                    intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
-                    intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
-                    try {
-                        callbackIntent.send(getContext(), 0, intent);
-                    } catch (PendingIntent.CanceledException e) {
-                        Slog.w(TAG, "Couldn't deliver callback: "
-                                + callbackIntent);
+                new AppTimeLimitController.TimeLimitCallbackListener() {
+                    @Override
+                    public void onLimitReached(int observerId, int userId, long timeLimit,
+                            long timeElapsed, PendingIntent callbackIntent) {
+                        if (callbackIntent == null) return;
+                        Intent intent = new Intent();
+                        intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
+                        intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
+                        intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
+                        try {
+                            callbackIntent.send(getContext(), 0, intent);
+                        } catch (PendingIntent.CanceledException e) {
+                            Slog.w(TAG, "Couldn't deliver callback: "
+                                    + callbackIntent);
+                        }
+                    }
+
+                    @Override
+                    public void onSessionEnd(int observerId, int userId, long timeElapsed,
+                            PendingIntent callbackIntent) {
+                        if (callbackIntent == null) return;
+                        Intent intent = new Intent();
+                        intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
+                        intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
+                        try {
+                            callbackIntent.send(getContext(), 0, intent);
+                        } catch (PendingIntent.CanceledException e) {
+                            Slog.w(TAG, "Couldn't deliver callback: "
+                                    + callbackIntent);
+                        }
                     }
                 }, mHandler.getLooper());
 
@@ -412,12 +432,18 @@
             mAppStandby.reportEvent(event, elapsedRealtime, userId);
             switch (event.mEventType) {
                 case Event.MOVE_TO_FOREGROUND:
-                    mAppTimeLimit.moveToForeground(event.getPackageName(), event.getClassName(),
-                            userId);
+                    try {
+                        mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+                    } catch (IllegalArgumentException iae) {
+                        Slog.e(TAG, "Failed to note usage start", iae);
+                    }
                     break;
                 case Event.MOVE_TO_BACKGROUND:
-                    mAppTimeLimit.moveToBackground(event.getPackageName(), event.getClassName(),
-                            userId);
+                    try {
+                        mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+                    } catch (IllegalArgumentException iae) {
+                        Slog.e(TAG, "Failed to note usage stop", iae);
+                    }
                     break;
             }
         }
@@ -1151,16 +1177,70 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void registerUsageSessionObserver(int sessionObserverId, String[] observed,
+                long timeLimitMs, long sessionThresholdTimeMs,
+                PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
+                String callingPackage) {
+            if (!hasObserverPermission(callingPackage)) {
+                throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
+            }
+
+            if (observed == null || observed.length == 0) {
+                throw new IllegalArgumentException("Must specify at least one observed entity");
+            }
+            if (limitReachedCallbackIntent == null) {
+                throw new NullPointerException("limitReachedCallbackIntent can't be null");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                UsageStatsService.this.registerUsageSessionObserver(callingUid, sessionObserverId,
+                        observed, timeLimitMs, sessionThresholdTimeMs, limitReachedCallbackIntent,
+                        sessionEndCallbackIntent, userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage) {
+            if (!hasObserverPermission(callingPackage)) {
+                throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
+            }
+
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
             long timeLimitMs, PendingIntent callbackIntent, int userId) {
-        mAppTimeLimit.addObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
                 userId);
     }
 
     void unregisterAppUsageObserver(int callingUid, int observerId, int userId) {
-        mAppTimeLimit.removeObserver(callingUid, observerId, userId);
+        mAppTimeLimit.removeAppUsageObserver(callingUid, observerId, userId);
+    }
+
+    void registerUsageSessionObserver(int callingUid, int observerId, String[] observed,
+            long timeLimitMs, long sessionThresholdTime, PendingIntent limitReachedCallbackIntent,
+            PendingIntent sessionEndCallbackIntent, int userId) {
+        mAppTimeLimit.addUsageSessionObserver(callingUid, observerId, observed, timeLimitMs,
+                sessionThresholdTime, limitReachedCallbackIntent, sessionEndCallbackIntent, userId);
+    }
+
+    void unregisterUsageSessionObserver(int callingUid, int sessionObserverId, int userId) {
+        mAppTimeLimit.removeUsageSessionObserver(callingUid, sessionObserverId, userId);
     }
 
     /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 66918eb..f4b5f86 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2231,6 +2231,13 @@
     public static final String KEY_CALL_WAITING_OVER_UT_WARNING_BOOL =
             "call_waiting_over_ut_warning_bool";
 
+    /**
+     * Flag indicating whether to support "Network default" option in Caller ID settings for Calling
+     * Line Identification Restriction (CLIR).
+     */
+    public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL =
+            "support_clir_network_default_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -2579,6 +2586,7 @@
         sDefaults.putBoolean(KEY_CALL_BARRING_OVER_UT_WARNING_BOOL, false);
         sDefaults.putBoolean(KEY_CALLER_ID_OVER_UT_WARNING_BOOL, false);
         sDefaults.putBoolean(KEY_CALL_WAITING_OVER_UT_WARNING_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL, true);
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 9218bdc..598f567 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.cdma.CdmaCellLocation;
@@ -56,11 +55,11 @@
      */
     public CellIdentityCdma() {
         super(TAG, CellInfo.TYPE_CDMA, null, null, null, null);
-        mNetworkId = Integer.MAX_VALUE;
-        mSystemId = Integer.MAX_VALUE;
-        mBasestationId = Integer.MAX_VALUE;
-        mLongitude = Integer.MAX_VALUE;
-        mLatitude = Integer.MAX_VALUE;
+        mNetworkId = CellInfo.UNAVAILABLE;
+        mSystemId = CellInfo.UNAVAILABLE;
+        mBasestationId = CellInfo.UNAVAILABLE;
+        mLongitude = CellInfo.UNAVAILABLE;
+        mLatitude = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -104,7 +103,7 @@
             mLongitude = lon;
             mLatitude = lat;
         } else {
-            mLongitude = mLatitude = Integer.MAX_VALUE;
+            mLongitude = mLatitude = CellInfo.UNAVAILABLE;
         }
     }
 
@@ -130,21 +129,24 @@
     }
 
     /**
-     * @return Network Id 0..65535, Integer.MAX_VALUE if unknown
+     * @return Network Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
      */
     public int getNetworkId() {
         return mNetworkId;
     }
 
     /**
-     * @return System Id 0..32767, Integer.MAX_VALUE if unknown
+     * @return System Id 0..32767, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
      */
     public int getSystemId() {
         return mSystemId;
     }
 
     /**
-     * @return Base Station Id 0..65535, Integer.MAX_VALUE if unknown
+     * @return Base Station Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
      */
     public int getBasestationId() {
         return mBasestationId;
@@ -155,7 +157,7 @@
      * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
      * of 0.25 seconds and ranges from -2592000 to 2592000, both
      * values inclusive (corresponding to a range of -180
-     * to +180 degrees). Integer.MAX_VALUE if unknown.
+     * to +180 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLongitude() {
         return mLongitude;
@@ -166,7 +168,7 @@
      * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
      * of 0.25 seconds and ranges from -1296000 to 1296000, both
      * values inclusive (corresponding to a range of -90
-     * to +90 degrees). Integer.MAX_VALUE if unknown.
+     * to +90 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLatitude() {
         return mLatitude;
@@ -182,10 +184,10 @@
     @Override
     public CdmaCellLocation asCellLocation() {
         CdmaCellLocation cl = new CdmaCellLocation();
-        int bsid = mBasestationId != Integer.MAX_VALUE ? mBasestationId : -1;
-        int sid = mSystemId != Integer.MAX_VALUE ? mSystemId : -1;
-        int nid = mNetworkId != Integer.MAX_VALUE ? mNetworkId : -1;
-        // lat and long already use Integer.MAX_VALUE for invalid/unknown
+        int bsid = mBasestationId != CellInfo.UNAVAILABLE ? mBasestationId : -1;
+        int sid = mSystemId != CellInfo.UNAVAILABLE ? mSystemId : -1;
+        int nid = mNetworkId != CellInfo.UNAVAILABLE ? mNetworkId : -1;
+        // lat and long already use CellInfo.UNAVAILABLE for invalid/unknown
         cl.setCellLocationData(bsid, mLatitude, mLongitude, sid, nid);
         return cl;
     }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index cb9dbf3..04c28e5 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -48,10 +47,10 @@
     @UnsupportedAppUsage
     public CellIdentityGsm() {
         super(TAG, CellInfo.TYPE_GSM, null, null, null, null);
-        mLac = Integer.MAX_VALUE;
-        mCid = Integer.MAX_VALUE;
-        mArfcn = Integer.MAX_VALUE;
-        mBsic = Integer.MAX_VALUE;
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mArfcn = CellInfo.UNAVAILABLE;
+        mBsic = CellInfo.UNAVAILABLE;
     }
     /**
      * public constructor
@@ -63,7 +62,7 @@
      * @hide
      */
     public CellIdentityGsm(int mcc, int mnc, int lac, int cid) {
-        this(lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE,
+        this(lac, cid, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 String.valueOf(mcc), String.valueOf(mnc), null, null);
     }
 
@@ -103,7 +102,7 @@
         mArfcn = arfcn;
         // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
         // for inbound parcels
-        mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
+        mBsic = (bsic == 0xFF) ? CellInfo.UNAVAILABLE : bsic;
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -116,69 +115,73 @@
     }
 
     /**
-     * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
-        return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
-        return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLac() {
         return mLac;
     }
 
     /**
-     * @return CID
-     * Either 16-bit GSM Cell Identity described
-     * in TS 27.007, 0..65535, Integer.MAX_VALUE if unknown
+     * @return 16-bit GSM Cell Identity described in TS 27.007, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCid() {
         return mCid;
     }
 
     /**
-     * @return 16-bit GSM Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     * @return 16-bit GSM Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getArfcn() {
         return mArfcn;
     }
 
     /**
-     * @return 6-bit Base Station Identity Code, Integer.MAX_VALUE if unknown
+     * @return 6-bit Base Station Identity Code,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getBsic() {
         return mBsic;
     }
 
     /**
-     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
     /**
-     * @return Mobile Country Code in string format, null if unknown
+     * @return Mobile Country Code in string format, null if unavailable.
      */
     public String getMccString() {
         return mMccStr;
     }
 
     /**
-     * @return Mobile Network Code in string format, null if unknown
+     * @return Mobile Network Code in string format, null if unavailable.
      */
     public String getMncString() {
         return mMncStr;
@@ -192,19 +195,19 @@
 
     /**
      * @deprecated Primary Scrambling Code is not applicable to GSM.
-     * @return Integer.MAX_VALUE, undefined for GSM
+     * @return {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} - undefined for GSM
      */
     @Deprecated
     public int getPsc() {
-        return Integer.MAX_VALUE;
+        return CellInfo.UNAVAILABLE;
     }
 
     /** @hide */
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
-        int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
         cl.setLacAndCid(lac, cid);
         cl.setPsc(-1);
         return cl;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index b44e891..04b6a6c 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -49,11 +48,11 @@
     @UnsupportedAppUsage
     public CellIdentityLte() {
         super(TAG, CellInfo.TYPE_LTE, null, null, null, null);
-        mCi = Integer.MAX_VALUE;
-        mPci = Integer.MAX_VALUE;
-        mTac = Integer.MAX_VALUE;
-        mEarfcn = Integer.MAX_VALUE;
-        mBandwidth = Integer.MAX_VALUE;
+        mCi = CellInfo.UNAVAILABLE;
+        mPci = CellInfo.UNAVAILABLE;
+        mTac = CellInfo.UNAVAILABLE;
+        mEarfcn = CellInfo.UNAVAILABLE;
+        mBandwidth = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -68,7 +67,7 @@
      */
     @UnsupportedAppUsage
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+        this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
                 String.valueOf(mnc), null, null);
     }
 
@@ -84,7 +83,7 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
-        this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+        this(ci, pci, tac, earfcn, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
                 null, null);
     }
 
@@ -122,74 +121,81 @@
     }
 
     /**
-     * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
-        return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
-        return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 28-bit Cell Identity, Integer.MAX_VALUE if unknown
+     * @return 28-bit Cell Identity,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCi() {
         return mCi;
     }
 
     /**
-     * @return Physical Cell Id 0..503, Integer.MAX_VALUE if unknown
+     * @return Physical Cell Id 0..503,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getPci() {
         return mPci;
     }
 
     /**
-     * @return 16-bit Tracking Area Code, Integer.MAX_VALUE if unknown
+     * @return 16-bit Tracking Area Code,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getTac() {
         return mTac;
     }
 
     /**
-     * @return 18-bit Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     * @return 18-bit Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getEarfcn() {
         return mEarfcn;
     }
 
     /**
-     * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+     * @return Cell bandwidth in kHz,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getBandwidth() {
         return mBandwidth;
     }
 
     /**
-     * @return Mobile Country Code in string format, null if unknown
+     * @return Mobile Country Code in string format, null if unavailable.
      */
     public String getMccString() {
         return mMccStr;
     }
 
     /**
-     * @return Mobile Network Code in string format, null if unknown
+     * @return Mobile Network Code in string format, null if unavailable.
      */
     public String getMncString() {
         return mMncStr;
     }
 
     /**
-     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
      */
     public String getMobileNetworkOperator() {
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
@@ -216,8 +222,8 @@
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int tac = mTac != Integer.MAX_VALUE ? mTac : -1;
-        int cid = mCi != Integer.MAX_VALUE ? mCi : -1;
+        int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
+        int cid = mCi != CellInfo.UNAVAILABLE ? mCi : -1;
         cl.setLacAndCid(tac, cid);
         cl.setPsc(0);
         return cl;
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index bc83de1..8b1c1b9 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -28,11 +28,12 @@
     private static final String TAG = CellIdentityTdscdma.class.getSimpleName();
     private static final boolean DBG = false;
 
-    // 16-bit Location Area Code, 0..65535, INT_MAX if unknown.
+    // 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown.
     private final int mLac;
-    // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown.
+    // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.UNAVAILABLE
+    // if unknown.
     private final int mCid;
-    // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
+    // 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE if unknown.
     private final int mCpid;
     // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
     private final int mUarfcn;
@@ -42,18 +43,20 @@
      */
     public CellIdentityTdscdma() {
         super(TAG, CellInfo.TYPE_TDSCDMA, null, null, null, null);
-        mLac = Integer.MAX_VALUE;
-        mCid = Integer.MAX_VALUE;
-        mCpid = Integer.MAX_VALUE;
-        mUarfcn = Integer.MAX_VALUE;
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mCpid = CellInfo.UNAVAILABLE;
+        mUarfcn = CellInfo.UNAVAILABLE;
     }
 
     /**
      * @param mcc 3-digit Mobile Country Code, 0..999
      * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
-     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
-     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.
+     *        UNAVAILABLE if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE
+     *        if unknown
      * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
      *
      * @hide
@@ -65,9 +68,11 @@
     /**
      * @param mcc 3-digit Mobile Country Code in string format
      * @param mnc 2 or 3-digit Mobile Network Code in string format
-     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
-     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
-     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *        CellInfo.UNAVAILABLE if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+     *        CellInfo.UNAVAILABLE if unknown
      * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
@@ -116,21 +121,24 @@
     }
 
     /**
-     * @return 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLac() {
         return mLac;
     }
 
     /**
-     * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCid() {
         return mCid;
     }
 
     /**
-     * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCpid() {
         return mCpid;
@@ -146,8 +154,8 @@
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
-        int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
         cl.setLacAndCid(lac, cid);
         cl.setPsc(-1); // There is no PSC for TD-SCDMA; not using this for CPI to stem shenanigans
         return cl;
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 727d990..3416ffe 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
@@ -46,10 +45,10 @@
      */
     public CellIdentityWcdma() {
         super(TAG, CellInfo.TYPE_WCDMA, null, null, null, null);
-        mLac = Integer.MAX_VALUE;
-        mCid = Integer.MAX_VALUE;
-        mPsc = Integer.MAX_VALUE;
-        mUarfcn = Integer.MAX_VALUE;
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mPsc = CellInfo.UNAVAILABLE;
+        mUarfcn = CellInfo.UNAVAILABLE;
     }
     /**
      * public constructor
@@ -62,7 +61,7 @@
      * @hide
      */
     public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
-        this(lac, cid, psc, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+        this(lac, cid, psc, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
                 null, null);
     }
 
@@ -113,25 +112,28 @@
     }
 
     /**
-     * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
-        return (mMccStr != null) ? Integer.valueOf(mMccStr) : Integer.MAX_VALUE;
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
-        return (mMncStr != null) ? Integer.valueOf(mMncStr) : Integer.MAX_VALUE;
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
     }
 
     /**
-     * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getLac() {
         return mLac;
@@ -139,29 +141,30 @@
 
     /**
      * @return CID
-     * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, Integer.MAX_VALUE if unknown
+     * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCid() {
         return mCid;
     }
 
     /**
-     * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511, Integer.MAX_VALUE
-     * if unknown
+     * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getPsc() {
         return mPsc;
     }
 
     /**
-     * @return Mobile Country Code in string version, null if unknown
+     * @return Mobile Country Code in string version, null if unavailable.
      */
     public String getMccString() {
         return mMccStr;
     }
 
     /**
-     * @return Mobile Network Code in string version, null if unknown
+     * @return Mobile Network Code in string version, null if unavailable.
      */
     public String getMncString() {
         return mMncStr;
@@ -180,7 +183,8 @@
     }
 
     /**
-     * @return 16-bit UMTS Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+     * @return 16-bit UMTS Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getUarfcn() {
         return mUarfcn;
@@ -196,9 +200,9 @@
     @Override
     public GsmCellLocation asCellLocation() {
         GsmCellLocation cl = new GsmCellLocation();
-        int lac = mLac != Integer.MAX_VALUE ? mLac : -1;
-        int cid = mCid != Integer.MAX_VALUE ? mCid : -1;
-        int psc = mPsc != Integer.MAX_VALUE ? mPsc : -1;
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
+        int psc = mPsc != CellInfo.UNAVAILABLE ? mPsc : -1;
         cl.setLacAndCid(lac, cid);
         cl.setPsc(psc);
 
@@ -280,4 +284,4 @@
     protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
         return new CellIdentityWcdma(in);
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 94e4293..1c63e82 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -33,6 +33,11 @@
 public abstract class CellInfo implements Parcelable {
 
     /**
+     * This value indicates that the integer field is unreported.
+     */
+    public static final int UNAVAILABLE = Integer.MAX_VALUE;
+
+    /**
      * Cell identity type
      * @hide
      */
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index aa6b207..5123052 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -51,8 +51,9 @@
      * <p>Note that this HAL is inconsistent with UMTS-based radio techs as the value indicating
      * that a field is unreported is negative, rather than a large(r) positive number.
      * <p>Also note that to keep the public-facing methods of this class consistent with others,
-     * unreported values are coerced to Integer.MAX_VALUE rather than left as -1, which is
-     * a departure from SignalStrength, which is stuck with the values it currently reports.
+     * unreported values are coerced to {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     * rather than left as -1, which is a departure from SignalStrength, which is stuck with the
+     * values it currently reports.
      *
      * @param cdmaDbm negative of the CDMA signal strength value or -1 if invalid.
      * @param cdmaEcio negative of the CDMA pilot/noise ratio or -1 if invalid.
@@ -65,12 +66,12 @@
             int evdoSnr) {
         // The values here were lifted from SignalStrength.validateInput()
         // FIXME: Combine all checking and setting logic between this and SignalStrength.
-        mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120))  ? -cdmaDbm : Integer.MAX_VALUE;
-        mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : Integer.MAX_VALUE;
+        mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120))  ? -cdmaDbm : CellInfo.UNAVAILABLE;
+        mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : CellInfo.UNAVAILABLE;
 
-        mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : Integer.MAX_VALUE;
-        mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : Integer.MAX_VALUE;
-        mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : Integer.MAX_VALUE;
+        mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : CellInfo.UNAVAILABLE;
+        mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : CellInfo.UNAVAILABLE;
+        mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : CellInfo.UNAVAILABLE;
     }
 
     /** @hide */
@@ -96,11 +97,11 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mCdmaDbm = Integer.MAX_VALUE;
-        mCdmaEcio = Integer.MAX_VALUE;
-        mEvdoDbm = Integer.MAX_VALUE;
-        mEvdoEcio = Integer.MAX_VALUE;
-        mEvdoSnr = Integer.MAX_VALUE;
+        mCdmaDbm = CellInfo.UNAVAILABLE;
+        mCdmaEcio = CellInfo.UNAVAILABLE;
+        mEvdoDbm = CellInfo.UNAVAILABLE;
+        mEvdoEcio = CellInfo.UNAVAILABLE;
+        mEvdoSnr = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -139,7 +140,7 @@
         int cdmaAsuLevel;
         int ecioAsuLevel;
 
-        if (cdmaDbm == Integer.MAX_VALUE) cdmaAsuLevel = 99;
+        if (cdmaDbm == CellInfo.UNAVAILABLE) cdmaAsuLevel = 99;
         else if (cdmaDbm >= -75) cdmaAsuLevel = 16;
         else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
         else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
@@ -148,7 +149,7 @@
         else cdmaAsuLevel = 99;
 
         // Ec/Io are in dB*10
-        if (cdmaEcio == Integer.MAX_VALUE) ecioAsuLevel = 99;
+        if (cdmaEcio == CellInfo.UNAVAILABLE) ecioAsuLevel = 99;
         else if (cdmaEcio >= -90) ecioAsuLevel = 16;
         else if (cdmaEcio >= -100) ecioAsuLevel = 8;
         else if (cdmaEcio >= -115) ecioAsuLevel = 4;
@@ -170,7 +171,7 @@
         int levelDbm;
         int levelEcio;
 
-        if (cdmaDbm == Integer.MAX_VALUE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (cdmaDbm == CellInfo.UNAVAILABLE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
         else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
         else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
@@ -178,7 +179,7 @@
         else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
         // Ec/Io are in dB*10
-        if (cdmaEcio == Integer.MAX_VALUE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (cdmaEcio == CellInfo.UNAVAILABLE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
         else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
         else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
@@ -199,14 +200,14 @@
         int levelEvdoDbm;
         int levelEvdoSnr;
 
-        if (evdoDbm == Integer.MAX_VALUE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (evdoDbm == CellInfo.UNAVAILABLE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
         else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
         else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
         else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
         else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
-        if (evdoSnr == Integer.MAX_VALUE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        if (evdoSnr == CellInfo.UNAVAILABLE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
         else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
         else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 1e8d119..e906f46 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -40,7 +40,7 @@
     @UnsupportedAppUsage
     private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
     @UnsupportedAppUsage
-    private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
+    private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown
 
     /** @hide */
     @UnsupportedAppUsage
@@ -50,7 +50,7 @@
 
     /** @hide */
     public CellSignalStrengthGsm(int ss, int ber) {
-        this(ss, ber, Integer.MAX_VALUE);
+        this(ss, ber, CellInfo.UNAVAILABLE);
     }
 
     /** @hide */
@@ -81,9 +81,9 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mBitErrorRate = Integer.MAX_VALUE;
-        mTimingAdvance = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mTimingAdvance = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -112,8 +112,9 @@
 
     /**
      * Get the GSM timing advance between 0..219 symbols (normally 0..63).
-     * Integer.MAX_VALUE is reported when there is no RR connection.
-     * Refer to 3GPP 45.010 Sec 5.8
+     * <p>{@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no RR
+     * connection. Refer to 3GPP 45.010 Sec 5.8.
+     *
      * @return the current GSM timing advance, if available.
      */
     public int getTimingAdvance() {
@@ -128,11 +129,11 @@
         int dBm;
 
         int level = mSignalStrength;
-        int asu = (level == 99 ? Integer.MAX_VALUE : level);
-        if (asu != Integer.MAX_VALUE) {
+        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+        if (asu != CellInfo.UNAVAILABLE) {
             dBm = -113 + (2 * asu);
         } else {
-            dBm = Integer.MAX_VALUE;
+            dBm = CellInfo.UNAVAILABLE;
         }
         if (DBG) log("getDbm=" + dBm);
         return dBm;
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index ed7d4b2..d6856b3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -85,12 +85,12 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mRsrp = Integer.MAX_VALUE;
-        mRsrq = Integer.MAX_VALUE;
-        mRssnr = Integer.MAX_VALUE;
-        mCqi = Integer.MAX_VALUE;
-        mTimingAdvance = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mRsrp = CellInfo.UNAVAILABLE;
+        mRsrq = CellInfo.UNAVAILABLE;
+        mRssnr = CellInfo.UNAVAILABLE;
+        mCqi = CellInfo.UNAVAILABLE;
+        mTimingAdvance = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -104,26 +104,27 @@
         int levelRsrp = 0;
         int levelRssnr = 0;
 
-        if (mRsrp == Integer.MAX_VALUE) levelRsrp = 0;
+        if (mRsrp == CellInfo.UNAVAILABLE) levelRsrp = 0;
         else if (mRsrp >= -95) levelRsrp = SIGNAL_STRENGTH_GREAT;
         else if (mRsrp >= -105) levelRsrp = SIGNAL_STRENGTH_GOOD;
         else if (mRsrp >= -115) levelRsrp = SIGNAL_STRENGTH_MODERATE;
         else levelRsrp = SIGNAL_STRENGTH_POOR;
 
         // See RIL_LTE_SignalStrength in ril.h
-        if (mRssnr == Integer.MAX_VALUE) levelRssnr = 0;
+        if (mRssnr == CellInfo.UNAVAILABLE) levelRssnr = 0;
         else if (mRssnr >= 45) levelRssnr = SIGNAL_STRENGTH_GREAT;
         else if (mRssnr >= 10) levelRssnr = SIGNAL_STRENGTH_GOOD;
         else if (mRssnr >= -30) levelRssnr = SIGNAL_STRENGTH_MODERATE;
         else levelRssnr = SIGNAL_STRENGTH_POOR;
 
         int level;
-        if (mRsrp == Integer.MAX_VALUE)
+        if (mRsrp == CellInfo.UNAVAILABLE) {
             level = levelRssnr;
-        else if (mRssnr == Integer.MAX_VALUE)
+        } else if (mRssnr == CellInfo.UNAVAILABLE) {
             level = levelRsrp;
-        else
+        } else {
             level = (levelRssnr < levelRsrp) ? levelRssnr : levelRsrp;
+        }
 
         if (DBG) log("Lte rsrp level: " + levelRsrp
                 + " snr level: " + levelRssnr + " level: " + level);
@@ -133,7 +134,8 @@
     /**
      * Get reference signal received quality
      *
-     * @return the RSRQ if available or Integer.MAX_VALUE if unavailable.
+     * @return the RSRQ if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getRsrq() {
         return mRsrq;
@@ -142,7 +144,8 @@
     /**
      * Get reference signal signal-to-noise ratio
      *
-     * @return the RSSNR if available or Integer.MAX_VALUE if unavailable.
+     * @return the RSSNR if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getRssnr() {
         return mRssnr;
@@ -160,7 +163,8 @@
     /**
      * Get channel quality indicator
      *
-     * @return the CQI if available or Integer.MAX_VALUE if unavailable.
+     * @return the CQI if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getCqi() {
         return mCqi;
@@ -184,7 +188,7 @@
     public int getAsuLevel() {
         int lteAsuLevel = 99;
         int lteDbm = getDbm();
-        if (lteDbm == Integer.MAX_VALUE) lteAsuLevel = 99;
+        if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99;
         else if (lteDbm <= -140) lteAsuLevel = 0;
         else if (lteDbm >= -43) lteAsuLevel = 97;
         else lteAsuLevel = lteDbm + 140;
@@ -194,10 +198,11 @@
 
     /**
      * Get the timing advance value for LTE, as a value in range of 0..1282.
-     * Integer.MAX_VALUE is reported when there is no active RRC
-     * connection. Refer to 3GPP 36.213 Sec 4.2.3
+     * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no
+     * active RRC connection. Refer to 3GPP 36.213 Sec 4.2.3
      *
-     * @return the LTE timing advance if available or Integer.MAX_VALUE if unavailable.
+     * @return the LTE timing advance if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
      */
     public int getTimingAdvance() {
         return mTimingAdvance;
@@ -252,8 +257,8 @@
         // Need to multiply rsrp and rsrq by -1
         // to ensure consistency when reading values written here
         // unless the values are invalid
-        dest.writeInt(mRsrp * (mRsrp != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mRsrq * (mRsrq != Integer.MAX_VALUE ? -1 : 1));
+        dest.writeInt(mRsrp * (mRsrp != CellInfo.UNAVAILABLE ? -1 : 1));
+        dest.writeInt(mRsrq * (mRsrq != CellInfo.UNAVAILABLE ? -1 : 1));
         dest.writeInt(mRssnr);
         dest.writeInt(mCqi);
         dest.writeInt(mTimingAdvance);
@@ -268,9 +273,9 @@
         // rsrp and rsrq are written into the parcel as positive values.
         // Need to convert into negative values unless the values are invalid
         mRsrp = in.readInt();
-        if (mRsrp != Integer.MAX_VALUE) mRsrp *= -1;
+        if (mRsrp != CellInfo.UNAVAILABLE) mRsrp *= -1;
         mRsrq = in.readInt();
-        if (mRsrq != Integer.MAX_VALUE) mRsrq *= -1;
+        if (mRsrq != CellInfo.UNAVAILABLE) mRsrq *= -1;
         mRssnr = in.readInt();
         mCqi = in.readInt();
         mTimingAdvance = in.readInt();
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index 41859a3..4d040cc 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -36,11 +36,11 @@
     private static final int TDSCDMA_SIGNAL_STRENGTH_MODERATE = 5;
 
     private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-                                 // or Integer.MAX_VALUE if unknown
+                                 // or CellInfo.UNAVAILABLE if unknown
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
-                               // Integer.MAX_VALUE if unknown
-    private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or Integer.MAX_VALUE
-                       // if unknown
+                               // CellInfo.UNAVAILABLE if unknown
+    private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or
+                       // CellInfo.UNAVAILABLE if unknown
 
     /** @hide */
     public CellSignalStrengthTdscdma() {
@@ -75,9 +75,9 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mBitErrorRate = Integer.MAX_VALUE;
-        mRscp = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mRscp = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -118,11 +118,11 @@
         int dBm;
 
         int level = mSignalStrength;
-        int asu = (level == 99 ? Integer.MAX_VALUE : level);
-        if (asu != Integer.MAX_VALUE) {
+        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+        if (asu != CellInfo.UNAVAILABLE) {
             dBm = -113 + (2 * asu);
         } else {
-            dBm = Integer.MAX_VALUE;
+            dBm = CellInfo.UNAVAILABLE;
         }
         if (DBG) log("getDbm=" + dBm);
         return dBm;
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 66e0882..0048cbd 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -37,14 +37,14 @@
 
     @UnsupportedAppUsage
     private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
-                                 // or Integer.MAX_VALUE if unknown
+                                 // or CellInfo.UNAVAILABLE if unknown
     @UnsupportedAppUsage
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
-                               // Integer.MAX_VALUE if unknown
+                               // CellInfo.UNAVAILABLE if unknown
     private int mRscp; // bit error rate (0-96, 255) as defined in TS 27.007 8.69 or
-                       // Integer.MAX_VALUE if unknown
+                       // CellInfo.UNAVAILABLE if unknown
     private int mEcNo; // signal to noise radio (0-49, 255) as defined in TS 27.007 8.69 or
-                       // Integer.MAX_VALUE if unknown
+                       // CellInfo.UNAVAILABLE if unknown
 
     /** @hide */
     public CellSignalStrengthWcdma() {
@@ -81,10 +81,10 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mSignalStrength = Integer.MAX_VALUE;
-        mBitErrorRate = Integer.MAX_VALUE;
-        mRscp = Integer.MAX_VALUE;
-        mEcNo = Integer.MAX_VALUE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mRscp = CellInfo.UNAVAILABLE;
+        mEcNo = CellInfo.UNAVAILABLE;
     }
 
     /**
@@ -119,11 +119,11 @@
         int dBm;
 
         int level = mSignalStrength;
-        int asu = (level == 99 ? Integer.MAX_VALUE : level);
-        if (asu != Integer.MAX_VALUE) {
+        int asu = (level == 99 ? CellInfo.UNAVAILABLE : level);
+        if (asu != CellInfo.UNAVAILABLE) {
             dBm = -113 + (2 * asu);
         } else {
-            dBm = Integer.MAX_VALUE;
+            dBm = CellInfo.UNAVAILABLE;
         }
         if (DBG) log("getDbm=" + dBm);
         return dBm;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0459667..0ba18ee 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -43,6 +43,7 @@
 import android.net.INetworkPolicyManager;
 import android.net.NetworkCapabilities;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -781,8 +782,13 @@
         IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
             @Override
             public void onSubscriptionsChanged() {
-                if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
-                mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
+                    mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
             }
         };
 
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 8f18d07..d6dbf5a 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -39,6 +39,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.NetworkStats.Entry;
 import android.os.Process;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
@@ -785,7 +786,38 @@
         ArrayMap<String, String> stackedIface = new ArrayMap<>();
         stackedIface.put(v4Iface, baseIface);
 
-        NetworkStats.Entry otherEntry = new NetworkStats.Entry(
+        // Ipv4 traffic sent/received by an app on stacked interface.
+        final NetworkStats.Entry appEntry = new NetworkStats.Entry(
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+                30501490  /* rxBytes */,
+                22401 /* rxPackets */,
+                876235 /* txBytes */,
+                13805 /* txPackets */,
+                0 /* operations */);
+
+        // Traffic measured for the root uid on the base interface if eBPF is in use.
+        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
+        // overhead (20 bytes per packet), only for TX traffic.
+        final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
+                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                163577 /* rxBytes */,
+                187 /* rxPackets */,
+                1169942 /* txBytes */,
+                13902 /* txPackets */,
+                0 /* operations */);
+
+        // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
+        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
+        // overhead (20 bytes per packet), in both directions.
+        final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
+                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                31113087 /* rxBytes */,
+                22588 /* rxPackets */,
+                1169942 /* txBytes */,
+                13902 /* txPackets */,
+                0 /* operations */);
+
+        final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
                 otherIface, appUid, SET_DEFAULT, TAG_NONE,
                 2600  /* rxBytes */,
                 2 /* rxPackets */,
@@ -793,39 +825,41 @@
                 3 /* txPackets */,
                 0 /* operations */);
 
-        NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(v4Iface, appUid, SET_DEFAULT, TAG_NONE,
-                        30501490  /* rxBytes */,
-                        22401 /* rxPackets */,
-                        876235 /* txBytes */,
-                        13805 /* txPackets */,
-                        0 /* operations */)
-                .addValues(baseIface, rootUid, SET_DEFAULT, TAG_NONE,
-                        31113087,
-                        22588,
-                        1169942,
-                        13902,
-                        0)
+        final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
+                .addValues(appEntry)
+                .addValues(xtRootUidEntry)
                 .addValues(otherEntry);
 
-        stats.apply464xlatAdjustments(stackedIface);
+        final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
+                .addValues(appEntry)
+                .addValues(ebpfRootUidEntry)
+                .addValues(otherEntry);
 
-        assertEquals(3, stats.size());
-        assertValues(stats, 0, v4Iface, appUid, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+        statsXt.apply464xlatAdjustments(stackedIface, false);
+        statsEbpf.apply464xlatAdjustments(stackedIface, true);
+
+        assertEquals(3, statsXt.size());
+        assertEquals(3, statsEbpf.size());
+        final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
                 30949510,
                 22401,
                 1152335,
                 13805,
                 0);
-        assertValues(stats, 1, baseIface, 0, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+        final NetworkStats.Entry expectedRootUid = new NetworkStats.Entry(
+                baseIface, 0, SET_DEFAULT, TAG_NONE,
                 163577,
                 187,
                 17607,
                 97,
                 0);
-        assertEquals(otherEntry, stats.getValues(2, null));
+        assertEquals(expectedAppUid, statsXt.getValues(0, null));
+        assertEquals(expectedRootUid, statsXt.getValues(1, null));
+        assertEquals(otherEntry, statsXt.getValues(2, null));
+        assertEquals(expectedAppUid, statsEbpf.getValues(0, null));
+        assertEquals(expectedRootUid, statsEbpf.getValues(1, null));
+        assertEquals(otherEntry, statsEbpf.getValues(2, null));
     }
 
     @Test
@@ -850,7 +884,7 @@
                 .addValues(secondEntry);
 
         // Empty map: no adjustment
-        stats.apply464xlatAdjustments(new ArrayMap<>());
+        stats.apply464xlatAdjustments(new ArrayMap<>(), false);
 
         assertEquals(2, stats.size());
         assertEquals(firstEntry, stats.getValues(0, null));