Merge "Use FrameworkStatsLog instead of StatsLog"
diff --git a/api/current.txt b/api/current.txt
index 32fb1e7..4533e37 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -47070,8 +47070,8 @@
   }
 
   public class MmsManager {
-    method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
-    method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
+    method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
+    method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
   }
 
   @Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 0596b3d..03229c7 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4484,11 +4484,11 @@
   }
 
   public static interface MediaSessionManager.OnMediaKeyEventDispatchedListener {
-    method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @Nullable android.media.session.MediaSession.Token);
+    method public void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @Nullable android.media.session.MediaSession.Token);
   }
 
   public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener {
-    method public default void onMediaKeyEventSessionChanged(@NonNull String, @Nullable android.media.session.MediaSession.Token);
+    method public void onMediaKeyEventSessionChanged(@NonNull String, @Nullable android.media.session.MediaSession.Token);
   }
 
   public static interface MediaSessionManager.OnMediaKeyListener {
@@ -7622,7 +7622,6 @@
     method public boolean isApMacRandomizationSupported();
     method public boolean isConnectedMacRandomizationSupported();
     method @Deprecated public boolean isDeviceToDeviceRttSupported();
-    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean isDualModeSupported();
     method public boolean isPortableHotspotSupported();
     method public boolean isVerboseLoggingEnabled();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4b336ee..0906b35 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -180,7 +180,7 @@
         PhoneServiceStateChanged phone_service_state_changed = 94 [(module) = "framework"];
         PhoneStateChanged phone_state_changed = 95 [(module) = "framework"];
         UserRestrictionChanged user_restriction_changed = 96;
-        SettingsUIChanged settings_ui_changed = 97;
+        SettingsUIChanged settings_ui_changed = 97 [(module) = "settings"];
         ConnectivityStateChanged connectivity_state_changed = 98 [(module) = "framework"];
         // TODO: service state change is very noisy shortly after boot, as well
         // as at other transitions - coming out of doze, device plugged in, etc.
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 0f33c56..463a1b6 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -255,7 +255,15 @@
             mValid = false;
             value = 0; // all primitive types can successfully cast 0
         } else {
-            value = *((T*)mBuf);
+            // When alignof(T) == 1, hopefully the compiler can optimize away
+            // this conditional as always true.
+            if ((reinterpret_cast<uintptr_t>(mBuf) % alignof(T)) == 0) {
+                // We're properly aligned, and can safely make this assignment.
+                value = *((T*)mBuf);
+            } else {
+                // We need to use memcpy.  It's slower, but safe.
+                memcpy(&value, mBuf, sizeof(T));
+            }
             mBuf += sizeof(T);
             mRemainingLen -= sizeof(T);
         }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d8b5e7f..f31c614 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2873,7 +2873,13 @@
     }
 
     /**
-     * Called by the system when picture in picture mode should be entered if supported.
+     * This method is called by the system in various cases where picture in picture mode should be
+     * entered if supported.
+     *
+     * <p>It is up to the app developer to choose whether to call
+     * {@link #enterPictureInPictureMode(PictureInPictureParams)} at this time. For example, the
+     * system will call this method when the activity is being put into the background, so the app
+     * developer might want to switch an activity into PIP mode instead.</p>
      */
     public void onPictureInPictureRequested() {
         // Previous recommendation was for apps to enter picture-in-picture in onUserLeaveHint()
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9666c12..605c585 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8711,6 +8711,14 @@
                 "back_gesture_inset_scale_right";
 
         /**
+         * Current provider of proximity-based sharing services.
+         * Default value in @string/config_defaultNearbySharingComponent.
+         * No VALIDATOR as this setting will not be backed up.
+         * @hide
+         */
+        public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component";
+
+        /**
          * Controls whether aware is enabled.
          * @hide
          */
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index c8a7c07..78d4e61 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -28,6 +28,7 @@
 import android.app.ITransientNotificationCallback;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -112,11 +113,9 @@
     /**
      * Text toasts will be rendered by SystemUI instead of in-app, so apps can't circumvent
      * background custom toast restrictions.
-     *
-     * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
      */
     @ChangeId
-    // @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long CHANGE_TEXT_TOASTS_IN_THE_SYSTEM = 147798919L;
 
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 65128e4..a284248 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -53,6 +53,7 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
@@ -81,6 +82,7 @@
 import android.provider.DocumentsContract;
 import android.provider.Downloads;
 import android.provider.OpenableColumns;
+import android.provider.Settings;
 import android.service.chooser.ChooserTarget;
 import android.service.chooser.ChooserTargetService;
 import android.service.chooser.IChooserTargetResult;
@@ -101,6 +103,7 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.Space;
 import android.widget.TextView;
@@ -131,6 +134,7 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.net.URISyntaxException;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -166,6 +170,9 @@
 
     private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions";
 
+    private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
+    private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
+
     private static final boolean DEBUG = true;
 
     private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true;
@@ -527,6 +534,15 @@
         mIsSuccessfullySelected = false;
         Intent intent = getIntent();
         Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+        if (targetParcelable instanceof Uri) {
+            try {
+                targetParcelable = Intent.parseUri(targetParcelable.toString(),
+                        Intent.URI_INTENT_SCHEME);
+            } catch (URISyntaxException ex) {
+                // doesn't parse as an intent; let the next test fail and error out
+            }
+        }
+
         if (!(targetParcelable instanceof Intent)) {
             Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable);
             finish();
@@ -965,6 +981,108 @@
         return displayContentPreview(previewType, targetIntent, getLayoutInflater(), parent);
     }
 
+    private ComponentName getNearbySharingComponent() {
+        String nearbyComponent = Settings.Secure.getString(
+                getContentResolver(),
+                Settings.Secure.NEARBY_SHARING_COMPONENT);
+        if (TextUtils.isEmpty(nearbyComponent)) {
+            nearbyComponent = getString(R.string.config_defaultNearbySharingComponent);
+        }
+        if (TextUtils.isEmpty(nearbyComponent)) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(nearbyComponent);
+    }
+
+    private TargetInfo getNearbySharingTarget(Intent originalIntent) {
+        final ComponentName cn = getNearbySharingComponent();
+        if (cn == null) return null;
+
+        final Intent resolveIntent = new Intent();
+        resolveIntent.setComponent(cn);
+        final ResolveInfo ri = getPackageManager().resolveActivity(
+                resolveIntent, PackageManager.GET_META_DATA);
+        if (ri == null || ri.activityInfo == null) {
+            Log.e(TAG, "Device-specified nearby sharing component (" + cn
+                    + ") not available");
+            return null;
+        }
+
+        // Allow the nearby sharing component to provide a more appropriate icon and label
+        // for the chip.
+        CharSequence name = null;
+        Drawable icon = null;
+        final Bundle metaData = ri.activityInfo.metaData;
+        if (metaData != null) {
+            try {
+                final Resources pkgRes = getPackageManager().getResourcesForActivity(cn);
+                final int nameResId = metaData.getInt(CHIP_LABEL_METADATA_KEY);
+                name = pkgRes.getString(nameResId);
+                final int resId = metaData.getInt(CHIP_ICON_METADATA_KEY);
+                icon = pkgRes.getDrawable(resId);
+            } catch (Resources.NotFoundException ex) {
+            } catch (NameNotFoundException ex) {
+            }
+        }
+        if (TextUtils.isEmpty(name)) {
+            name = ri.loadLabel(getPackageManager());
+        }
+        if (icon == null) {
+            icon = ri.loadIcon(getPackageManager());
+        }
+
+        final DisplayResolveInfo dri = new DisplayResolveInfo(
+                originalIntent, ri, name, "", resolveIntent, null);
+        dri.setDisplayIcon(icon);
+        return dri;
+    }
+
+    private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) {
+        Button b = (Button) LayoutInflater.from(this).inflate(R.layout.chooser_action_button, null);
+        if (icon != null) {
+            final int size = getResources()
+                    .getDimensionPixelSize(R.dimen.chooser_action_button_icon_size);
+            icon.setBounds(0, 0, size, size);
+            b.setCompoundDrawablesRelative(icon, null, null, null);
+        }
+        b.setText(title);
+        b.setOnClickListener(r);
+        return b;
+    }
+
+    private Button createCopyButton() {
+        final Button b = createActionButton(
+                getDrawable(R.drawable.ic_menu_copy_material),
+                getString(R.string.copy), this::onCopyButtonClicked);
+        b.setId(R.id.chooser_copy_button);
+        return b;
+    }
+
+    private @Nullable Button createNearbyButton(Intent originalIntent) {
+        final TargetInfo ti = getNearbySharingTarget(originalIntent);
+        if (ti == null) return null;
+
+        return createActionButton(
+                ti.getDisplayIcon(this),
+                ti.getDisplayLabel(),
+                (View unused) -> {
+                    safelyStartActivity(ti);
+                    finish();
+                }
+        );
+    }
+
+    private void addActionButton(ViewGroup parent, Button b) {
+        if (b == null) return;
+        final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
+                        LayoutParams.WRAP_CONTENT,
+                        LayoutParams.WRAP_CONTENT
+                );
+        final int gap = getResources().getDimensionPixelSize(R.dimen.resolver_icon_margin) / 2;
+        lp.setMarginsRelative(gap, 0, gap, 0);
+        parent.addView(b, lp);
+    }
+
     private ViewGroup displayContentPreview(@ContentPreviewType int previewType,
             Intent targetIntent, LayoutInflater layoutInflater, ViewGroup parent) {
         ViewGroup layout = null;
@@ -995,8 +1113,10 @@
         ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
                 R.layout.chooser_grid_preview_text, parent, false);
 
-        contentPreviewLayout.findViewById(R.id.copy_button).setOnClickListener(
-                this::onCopyButtonClicked);
+        final ViewGroup actionRow =
+                (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
+        addActionButton(actionRow, createCopyButton());
+        addActionButton(actionRow, createNearbyButton(targetIntent));
 
         CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
         if (sharingText == null) {
@@ -1154,7 +1274,8 @@
 
         // TODO(b/120417119): Disable file copy until after moving to sysui,
         // due to permissions issues
-        contentPreviewLayout.findViewById(R.id.file_copy_button).setVisibility(View.GONE);
+        //((ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row))
+        //        .addView(createCopyButton());
 
         String action = targetIntent.getAction();
         if (Intent.ACTION_SEND.equals(action)) {
@@ -1680,7 +1801,7 @@
             }
             return intentFilter;
         } catch (Exception e) {
-            Log.e(TAG, "failed to get target intent filter " + e);
+            Log.e(TAG, "failed to get target intent filter", e);
             return null;
         }
     }
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 89aa770..92e9fe4 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -23,7 +23,7 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.location.LocationManager;
+import android.location.LocationManagerInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -32,6 +32,7 @@
 
 import com.android.internal.R;
 import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.server.LocalServices;
 
 /**
  * This activity is shown to the user for him/her to accept or deny network-initiated
@@ -68,14 +69,14 @@
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             switch (msg.what) {
-            case GPS_NO_RESPONSE_TIME_OUT: {
-                if (notificationId != -1) {
-                    sendUserResponse(default_response);
+                case GPS_NO_RESPONSE_TIME_OUT: {
+                    if (notificationId != -1) {
+                        sendUserResponse(default_response);
+                    }
+                    finish();
                 }
-                finish();
-            }
-            break;
-            default:
+                break;
+                default:
             }
         }
     };
@@ -137,9 +138,8 @@
     // Respond to NI Handler under GnssLocationProvider, 1 = accept, 2 = deny
     private void sendUserResponse(int response) {
         if (DEBUG) Log.d(TAG, "sendUserResponse, response: " + response);
-        LocationManager locationManager = (LocationManager)
-            this.getSystemService(Context.LOCATION_SERVICE);
-        locationManager.sendNiResponse(notificationId, response);
+        LocationManagerInternal lm = LocalServices.getService(LocationManagerInternal.class);
+        lm.sendNiResponse(notificationId, response);
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index e0eb9af..c50ff35 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -18,10 +18,10 @@
 
 import android.util.Log;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -85,8 +85,8 @@
      */
     public void reportChange(int uid, long changeId, int state) {
         if (shouldWriteToStatsLog(uid, changeId, state)) {
-            StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
-                    state, mSource);
+            FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid,
+                    changeId, state, mSource);
         }
         if (shouldWriteToDebug(uid, changeId, state)) {
             debugLog(uid, changeId, state);
@@ -174,7 +174,7 @@
     private void debugLog(int uid, long changeId, int state) {
         String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
                 uid, stateToString(state));
-        if (mSource == StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER) {
+        if (mSource == FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER) {
             Slog.d(TAG, message);
         } else {
             Log.d(TAG, message);
@@ -183,18 +183,18 @@
     }
 
     /**
-     * Transforms StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
+     * Transforms FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
      *
      * @param state to transform
      * @return a string representing the state
      */
     private static String stateToString(int state) {
         switch (state) {
-            case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
+            case FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
                 return "LOGGED";
-            case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
+            case FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
                 return "ENABLED";
-            case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
+            case FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
                 return "DISABLED";
             default:
                 return "UNKNOWN";
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 4de6c86..39483b5 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -277,13 +277,6 @@
 
 SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
                                          jbyteArray storage) {
-    static bool gInited;
-
-    if (!gInited) {
-
-        gInited = true;
-    }
-
     return new SkJavaOutputStream(env, stream, storage);
 }
 
diff --git a/core/res/res/drawable/chooser_action_button_bg.xml b/core/res/res/drawable/chooser_action_button_bg.xml
new file mode 100644
index 0000000..a434c0b
--- /dev/null
+++ b/core/res/res/drawable/chooser_action_button_bg.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open 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
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/lighter_gray">
+    <item>
+        <inset
+            android:insetLeft="0dp"
+            android:insetTop="8dp"
+            android:insetRight="0dp"
+            android:insetBottom="8dp">
+            <shape android:shape="rectangle">
+              <corners android:radius="16dp"></corners>
+                <stroke android:width="1dp"
+                        android:color="?attr/textColorSecondary" />
+                <solid android:color="?attr/colorBackground" />
+            </shape>
+        </inset>
+    </item>
+</ripple>
diff --git a/core/res/res/drawable/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml
deleted file mode 100644
index ee58738..0000000
--- a/core/res/res/drawable/ic_content_copy_gm2.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="?android:attr/textColorSecondary"
-      android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
-</vector>
diff --git a/core/res/res/drawable/ic_menu_copy_material.xml b/core/res/res/drawable/ic_menu_copy_material.xml
index c03723b..8c9f1c5 100644
--- a/core/res/res/drawable/ic_menu_copy_material.xml
+++ b/core/res/res/drawable/ic_menu_copy_material.xml
@@ -1,26 +1,27 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
 
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
 
-         http://www.apache.org/licenses/LICENSE-2.0
+          http://www.apache.org/licenses/LICENSE-2.0
 
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
 -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:autoMirrored="true"
-        android:tint="?attr/colorControlNormal">
-    <path
-        android:pathData="M16,1L4,1C2.9,1 2,1.9 2,3l0,14l2,0L4,3l12,0L16,1zM19,5L8,5C6.9,5 6,5.9 6,7l0,14c0,1.1 0.9,2 2,2l11,0c1.1,0 2,-0.9 2,-2L21,7C21,5.9 20.1,5 19,5zM19,21L8,21L8,7l11,0L19,21z"
-        android:fillColor="@color/white"/>
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:autoMirrored="true"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@color/white"
+      android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
 </vector>
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
new file mode 100644
index 0000000..119b2e9
--- /dev/null
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -0,0 +1,30 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open 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
+  -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:gravity="center_vertical|start"
+    android:paddingStart="12dp"
+    android:paddingEnd="12dp"
+    android:drawablePadding="8dp"
+    android:textColor="?android:textColorSecondary"
+    android:textSize="12sp"
+    android:maxWidth="192dp"
+    android:singleLine="true"
+    android:clickable="true"
+    android:background="@drawable/chooser_action_button_bg"
+    android:drawableTint="?android:attr/colorControlNormal"
+    android:drawableTintMode="src_in"
+    />
diff --git a/core/res/res/layout/chooser_action_row.xml b/core/res/res/layout/chooser_action_row.xml
new file mode 100644
index 0000000..ea75611
--- /dev/null
+++ b/core/res/res/layout/chooser_action_row.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingLeft="@dimen/chooser_edge_margin_normal"
+    android:paddingRight="@dimen/chooser_edge_margin_normal"
+    android:gravity="center"
+    >
+
+</LinearLayout>
diff --git a/core/res/res/layout/chooser_grid_preview_file.xml b/core/res/res/layout/chooser_grid_preview_file.xml
index f7d60c9..2a39215 100644
--- a/core/res/res/layout/chooser_grid_preview_file.xml
+++ b/core/res/res/layout/chooser_grid_preview_file.xml
@@ -65,13 +65,15 @@
         android:gravity="start|top"
         android:paddingRight="24dp"
         android:singleLine="true"/>
-    <Button
-        android:id="@+id/file_copy_button"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:gravity="center"
-        android:layout_gravity="center_vertical"
-        android:background="@drawable/ic_content_copy_gm2"/>
   </LinearLayout>
+
+  <include
+      android:id="@+id/chooser_action_row"
+      layout="@layout/chooser_action_row"
+      android:layout_width="@dimen/chooser_preview_width"
+      android:layout_height="wrap_content"
+      android:layout_marginBottom="@dimen/chooser_view_spacing"
+      android:layout_gravity="center"
+  />
 </LinearLayout>
 
diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml
index 79a0de4..62df165 100644
--- a/core/res/res/layout/chooser_grid_preview_image.xml
+++ b/core/res/res/layout/chooser_grid_preview_image.xml
@@ -78,5 +78,15 @@
           android:scaleType="centerCrop"/>
 
   </RelativeLayout>
+
+  <include
+      android:id="@+id/chooser_action_row"
+      layout="@layout/chooser_action_row"
+      android:layout_width="@dimen/chooser_preview_width"
+      android:layout_height="wrap_content"
+      android:layout_marginBottom="@dimen/chooser_view_spacing"
+      android:layout_gravity="center"
+  />
+
 </LinearLayout>
 
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index 4d7846d..0029174 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -37,10 +37,9 @@
 
     <TextView
         android:id="@+id/content_preview_text"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
-        android:layout_toStartOf="@id/copy_button"
         android:layout_centerVertical="true"
         android:ellipsize="end"
         android:fontFamily="@android:string/config_headlineFontFamily"
@@ -48,40 +47,17 @@
         android:maxLines="2"
         android:focusable="true"/>
 
-    <LinearLayout
-        android:id="@+id/copy_button"
-        android:orientation="vertical"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_alignParentEnd="true"
-        android:layout_marginStart="@dimen/chooser_view_spacing"
-        android:gravity="center"
-        android:minWidth="48dp"
-        android:minHeight="48dp"
-        android:clickable="true"
-        style="?attr/borderlessButtonStyle">
-
-      <ImageView
-          android:layout_width="24dp"
-          android:layout_height="24dp"
-          android:gravity="top|center_horizontal"
-          android:src="@drawable/ic_content_copy_gm2" />
-
-      <TextView
-          android:layout_width="wrap_content"
-          android:layout_height="wrap_content"
-          android:layout_marginTop="4dp"
-          android:gravity="center_horizontal"
-          android:text="@string/copy"
-          android:textColor="?android:textColorSecondary"
-          android:textSize="12sp"
-          android:maxWidth="72dp"
-          android:maxLines="2"
-          android:ellipsize="end" />
-    </LinearLayout>
   </RelativeLayout>
 
+  <include
+      android:id="@+id/chooser_action_row"
+      layout="@layout/chooser_action_row"
+      android:layout_width="@dimen/chooser_preview_width"
+      android:layout_height="wrap_content"
+      android:layout_marginBottom="@dimen/chooser_view_spacing"
+      android:layout_gravity="center"
+      />
+
   <!-- Required sub-layout so we can get the nice rounded corners-->
   <!-- around this section -->
   <LinearLayout
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e98f8ba..52b92d2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4323,4 +4323,12 @@
       <item>4</item> <!-- PROCESS_STATE_FOREGROUND_SERVICE -->
       <item>12</item> <!-- PROCESS_STATE_TOP_SLEEPING -->
     </integer-array>
+
+    <!-- Component name that accepts ACTION_SEND intents for nearby (proximity-based) sharing.
+         Used by ChooserActivity. -->
+    <string translatable="false" name="config_defaultNearbySharingComponent"></string>
+
+    <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
+         check after reboot or airplane mode toggling -->
+    <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index d437aa1..0b5082c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -774,6 +774,8 @@
     <dimen name="resolver_edge_margin">24dp</dimen>
     <dimen name="resolver_elevation">1dp</dimen>
 
+    <dimen name="chooser_action_button_icon_size">18dp</dimen>
+
     <!-- Assistant handles -->
     <dimen name="assist_handle_shadow_radius">2dp</dimen>
 
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index ddd9ba4..51ed08b 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -194,6 +194,9 @@
   <!-- A tag used to save the index where the custom view is stored -->
   <item type="id" name="notification_custom_view_index_tag" />
 
+  <!-- Marks the "copy to clipboard" button in the ChooserActivity -->
+  <item type="id" name="chooser_copy_button" />
+
   <!-- Accessibility action identifier for {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK}. -->
   <item type="id" name="accessibilitySystemActionBack" />
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e4717f8..21128e3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -66,8 +66,7 @@
   <java-symbol type="id" name="content_preview_text_layout" />
   <java-symbol type="id" name="content_preview_title" />
   <java-symbol type="id" name="content_preview_title_layout" />
-  <java-symbol type="id" name="copy_button" />
-  <java-symbol type="id" name="file_copy_button" />
+  <java-symbol type="id" name="chooser_action_row" />
   <java-symbol type="id" name="current_scene" />
   <java-symbol type="id" name="scene_layoutid_cache" />
   <java-symbol type="id" name="customPanel" />
@@ -3783,6 +3782,11 @@
   <java-symbol type="string" name="config_factoryResetPackage" />
   <java-symbol type="array" name="config_highRefreshRateBlacklist" />
 
+  <java-symbol type="id" name="chooser_copy_button" />
+  <java-symbol type="layout" name="chooser_action_button" />
+  <java-symbol type="dimen" name="chooser_action_button_icon_size" />
+  <java-symbol type="string" name="config_defaultNearbySharingComponent" />
+
   <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
 
   <java-symbol type="bool" name="config_showBuiltinWirelessChargingAnim" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index d4c3621..df6b906 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -64,7 +64,6 @@
 import android.graphics.drawable.Icon;
 import android.metrics.LogMaker;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.UserHandle;
 import android.service.chooser.ChooserTarget;
 
@@ -77,9 +76,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -482,8 +478,8 @@
                 .launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
 
-        onView(withId(R.id.copy_button)).check(matches(isDisplayed()));
-        onView(withId(R.id.copy_button)).perform(click());
+        onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
+        onView(withId(R.id.chooser_copy_button)).perform(click());
         ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
                 Context.CLIPBOARD_SERVICE);
         ClipData clipData = clipboard.getPrimaryClip();
@@ -510,8 +506,8 @@
                 .launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
 
-        onView(withId(R.id.copy_button)).check(matches(isDisplayed()));
-        onView(withId(R.id.copy_button)).perform(click());
+        onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
+        onView(withId(R.id.chooser_copy_button)).perform(click());
 
         verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
         // First is  Activity shown, Second is "with preview"
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 3997371..5c89da0 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -53,10 +53,6 @@
   <hidden-api-whitelisted-app package="com.android.providers.media" />
   <hidden-api-whitelisted-app package="com.android.providers.tv" />
   <hidden-api-whitelisted-app package="com.android.providers.userdictionary" />
-  <!-- TODO (b/141954427): Remove networkstack -->
-  <hidden-api-whitelisted-app package="com.android.networkstack" />
-  <!-- TODO (b/141954427): Remove wifistack -->
-  <hidden-api-whitelisted-app package="com.android.wifi" />
   <hidden-api-whitelisted-app package="com.android.smspush" />
   <hidden-api-whitelisted-app package="com.android.spare_parts" />
   <hidden-api-whitelisted-app package="com.android.statementservice" />
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 79bec92..6a5c0ec 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -69,8 +69,6 @@
         double upperRightLatitude, double upperRightLongitude, int maxResults,
         in GeocoderParams params, out List<Address> addrs);
 
-    boolean sendNiResponse(int notifId, int userResponse);
-
     boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
              String packageName, String featureId, String listenerIdentifier);
     void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 0c5fe78..197787e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2397,21 +2397,6 @@
         }
     }
 
-    /**
-     * Used by NetInitiatedActivity to report user response
-     * for network initiated GPS fix requests.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public boolean sendNiResponse(int notifId, int userResponse) {
-        try {
-            return mService.sendNiResponse(notifId, userResponse);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     private void checkPendingIntent(PendingIntent pendingIntent) {
         Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
         if (!pendingIntent.isTargetedToPackage()) {
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 44d9d23..69162ba 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -41,4 +41,19 @@
      * @throws IllegalArgumentException if provider is null
      */
     public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed);
+
+    /**
+     * Returns true if the given package belongs to a location provider, and so should be afforded
+     * some special privileges.
+     *
+     * @param packageName The package name to check
+     * @return True is the given package belongs to a location provider, false otherwise
+     */
+    public abstract boolean isProviderPackage(@NonNull String packageName);
+
+    /**
+     * Should only be used by GNSS code.
+     */
+    // TODO: there is no reason for this to exist as part of any API. move all the logic into gnss
+    public abstract void sendNiResponse(int notifId, int userResponse);
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index aece39d..5ef466d 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -932,7 +932,7 @@
     }
 
     /**
-     * Listener to receive when the media session service
+     * Listener to be called when the media session service dispatches a media key event.
      * @hide
      */
     @SystemApi
@@ -944,15 +944,15 @@
          * is released.
          *
          * @param event Dispatched media key event.
-         * @param packageName Package
+         * @param packageName The package name
          * @param sessionToken The media session's token. Can be {@code null}.
          */
-        default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName,
-                @Nullable MediaSession.Token sessionToken) { }
+        void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName,
+                @Nullable MediaSession.Token sessionToken);
     }
 
     /**
-     * Listener to receive changes in the media key event session, which would receive the media key
+     * Listener to receive changes in the media key event session, which would receive a media key
      * event unless specified.
      * @hide
      */
@@ -964,13 +964,14 @@
          * has specified the target.
          * <p>
          * The session token can be {@link null} if the media button session is unset. In that case,
-         * framework would dispatch to the last sessions's media button receiver.
+         * framework would dispatch to the last sessions's media button receiver. If the media
+         * button receive isn't set as well, then it
          *
          * @param packageName The package name who would receive the media key event. Can be empty.
-         * @param sessionToken The media session's token. Can be {@code null.}
+         * @param sessionToken The media session's token. Can be {@code null}.
          */
-        default void onMediaKeyEventSessionChanged(@NonNull String packageName,
-                @Nullable MediaSession.Token sessionToken) { }
+        void onMediaKeyEventSessionChanged(@NonNull String packageName,
+                @Nullable MediaSession.Token sessionToken);
     }
 
     /**
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index cf55eba..f07cd5e 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -55,10 +55,12 @@
      *                        sending the message.
      * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message
      *                   is successfully sent, or failed
+     * @param messageId an id that uniquely identifies the message requested to be sent.
+     *                  Used for logging and diagnostics purposes. The id may be 0.
      */
     public void sendMultimediaMessage(int subId, @NonNull Uri contentUri,
             @Nullable String locationUrl, @Nullable Bundle configOverrides,
-            @Nullable PendingIntent sentIntent) {
+            @Nullable PendingIntent sentIntent, long messageId) {
         try {
             final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
             if (iMms == null) {
@@ -66,7 +68,7 @@
             }
 
             iMms.sendMessage(subId, ActivityThread.currentPackageName(), contentUri,
-                    locationUrl, configOverrides, sentIntent);
+                    locationUrl, configOverrides, sentIntent, messageId);
         } catch (RemoteException e) {
             // Ignore it
         }
@@ -83,18 +85,22 @@
      *  downloading the message.
      * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is downloaded, or the download is failed
+     * @param messageId an id that uniquely identifies the message requested to be downloaded.
+     *                  Used for logging and diagnostics purposes. The id may be 0.
+     *  downloaded.
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
      */
     public void downloadMultimediaMessage(int subId, @NonNull String locationUrl,
             @NonNull Uri contentUri, @Nullable Bundle configOverrides,
-            @Nullable PendingIntent downloadedIntent) {
+            @Nullable PendingIntent downloadedIntent, long messageId) {
         try {
             final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
             if (iMms == null) {
                 return;
             }
             iMms.downloadMessage(subId, ActivityThread.currentPackageName(),
-                    locationUrl, contentUri, configOverrides, downloadedIntent);
+                    locationUrl, contentUri, configOverrides, downloadedIntent,
+                    messageId);
         } catch (RemoteException e) {
             // Ignore it
         }
diff --git a/mms/java/com/android/internal/telephony/IMms.aidl b/mms/java/com/android/internal/telephony/IMms.aidl
index 8be5111..e0e0a4a 100644
--- a/mms/java/com/android/internal/telephony/IMms.aidl
+++ b/mms/java/com/android/internal/telephony/IMms.aidl
@@ -37,9 +37,11 @@
      *  sending the message. See {@link android.telephony.SmsManager} for the value names and types.
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is successfully sent, or failed
+     * @param messageId An id that uniquely identifies the message requested to be sent.
      */
     void sendMessage(int subId, String callingPkg, in Uri contentUri,
-            String locationUrl, in Bundle configOverrides, in PendingIntent sentIntent);
+            String locationUrl, in Bundle configOverrides, in PendingIntent sentIntent,
+            in long messageId);
 
     /**
      * Download an MMS message using known location and transaction id
@@ -54,10 +56,11 @@
      *  types.
      * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is downloaded, or the download is failed
-     */
+     * @param messageId An id that uniquely identifies the message requested to be downloaded.
+    */
     void downloadMessage(int subId, String callingPkg, String locationUrl,
             in Uri contentUri, in Bundle configOverrides,
-            in PendingIntent downloadedIntent);
+            in PendingIntent downloadedIntent, in long messageId);
 
     /**
      * Import a text message into system's SMS store
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 65269c9..dee1d7e 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -728,6 +728,7 @@
                  Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                  Settings.Secure.FACE_UNLOCK_RE_ENROLL,
                  Settings.Secure.TAP_GESTURE,
+                 Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable
                  Settings.Secure.WINDOW_MAGNIFICATION,
                  Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
                  Settings.Secure.SUPPRESS_DOZE);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index c243309..581cf7a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -86,6 +86,8 @@
     private SurfaceView mMirrorSurfaceView;
     private View mControlsView;
     private View mOverlayView;
+    // The boundary of magnification frame.
+    private final Rect mMagnificationFrameBoundary = new Rect();
 
     private MoveMirrorRunnable mMoveMirrorRunnable = new MoveMirrorRunnable();
 
@@ -93,7 +95,7 @@
         mContext = context;
         mHandler = handler;
         Display display = mContext.getDisplay();
-        display.getSize(mDisplaySize);
+        display.getRealSize(mDisplaySize);
         mDisplayId = mContext.getDisplayId();
 
         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -114,6 +116,7 @@
             return;
         }
         setInitialStartBounds();
+        setMagnificationFrameBoundary();
         createOverlayWindow();
     }
 
@@ -330,7 +333,7 @@
     @Override
     public void onClick(View v) {
         setMoveOffset(v, mMoveFrameAmountShort);
-        moveMirrorFromControls();
+        moveMirrorWindow(mMoveWindowOffset.x, mMoveWindowOffset.y);
     }
 
     @Override
@@ -370,10 +373,8 @@
             case MotionEvent.ACTION_MOVE:
                 int xDiff = (int) (event.getRawX() - mLastDrag.x);
                 int yDiff = (int) (event.getRawY() - mLastDrag.y);
-                mMagnificationFrame.offset(xDiff, yDiff);
+                moveMirrorWindow(xDiff, yDiff);
                 mLastDrag.set(event.getRawX(), event.getRawY());
-                modifyWindowMagnification(mTransaction);
-                mTransaction.apply();
                 return true;
         }
         return false;
@@ -393,11 +394,11 @@
         }
     }
 
-    private void moveMirrorFromControls() {
-        mMagnificationFrame.offset(mMoveWindowOffset.x, mMoveWindowOffset.y);
-
-        modifyWindowMagnification(mTransaction);
-        mTransaction.apply();
+    private void moveMirrorWindow(int xOffset, int yOffset) {
+        if (updateMagnificationFramePosition(xOffset, yOffset)) {
+            modifyWindowMagnification(mTransaction);
+            mTransaction.apply();
+        }
     }
 
     /**
@@ -414,6 +415,52 @@
         return new Rect(left, top, right, bottom);
     }
 
+    private void setMagnificationFrameBoundary() {
+        // Calculates width and height for magnification frame could exceed out the screen.
+        // TODO : re-calculating again when scale is changed.
+        // The half width of magnification frame.
+        final int halfWidth = mMagnificationFrame.width() / 2;
+        // The half height of magnification frame.
+        final int halfHeight = mMagnificationFrame.height() / 2;
+        // The scaled half width of magnified region.
+        final int scaledWidth = (int) (halfWidth / mScale);
+        // The scaled half height of magnified region.
+        final int scaledHeight = (int) (halfHeight / mScale);
+        final int exceededWidth = halfWidth - scaledWidth;
+        final int exceededHeight = halfHeight - scaledHeight;
+
+        mMagnificationFrameBoundary.set(-exceededWidth, -exceededHeight,
+                mDisplaySize.x + exceededWidth, mDisplaySize.y + exceededHeight);
+    }
+
+    /**
+     * Calculates and sets the real position of magnification frame based on the magnified region
+     * should be limited by the region of the display.
+     */
+    private boolean updateMagnificationFramePosition(int xOffset, int yOffset) {
+        mTmpRect.set(mMagnificationFrame);
+        mTmpRect.offset(xOffset, yOffset);
+
+        if (mTmpRect.left < mMagnificationFrameBoundary.left) {
+            mTmpRect.offsetTo(mMagnificationFrameBoundary.left, mTmpRect.top);
+        } else if (mTmpRect.right > mMagnificationFrameBoundary.right) {
+            final int leftOffset = mMagnificationFrameBoundary.right - mMagnificationFrame.width();
+            mTmpRect.offsetTo(leftOffset, mTmpRect.top);
+        }
+
+        if (mTmpRect.top < mMagnificationFrameBoundary.top) {
+            mTmpRect.offsetTo(mTmpRect.left, mMagnificationFrameBoundary.top);
+        } else if (mTmpRect.bottom > mMagnificationFrameBoundary.bottom) {
+            final int topOffset = mMagnificationFrameBoundary.bottom - mMagnificationFrame.height();
+            mTmpRect.offsetTo(mTmpRect.left, topOffset);
+        }
+
+        if (!mTmpRect.equals(mMagnificationFrame)) {
+            mMagnificationFrame.set(mTmpRect);
+            return true;
+        }
+        return false;
+    }
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
         createMirror();
@@ -431,7 +478,7 @@
         @Override
         public void run() {
             if (mIsPressedDown) {
-                moveMirrorFromControls();
+                moveMirrorWindow(mMoveWindowOffset.x, mMoveWindowOffset.y);
                 mHandler.postDelayed(mMoveMirrorRunnable, 100);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index ac06f95..e954163 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -866,10 +866,6 @@
                 mOverflowCallback.run();
             }
 
-            if (update.addedBubble != null) {
-                mStackView.addBubble(update.addedBubble);
-            }
-
             // Collapsing? Do this first before remaining steps.
             if (update.expandedChanged && !update.expanded) {
                 mStackView.setExpanded(false);
@@ -916,6 +912,10 @@
                 }
             }
 
+            if (update.addedBubble != null) {
+                mStackView.addBubble(update.addedBubble);
+            }
+
             if (update.updatedBubble != null) {
                 mStackView.updateBubble(update.updatedBubble);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 685bb94..6062a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -526,8 +526,13 @@
                 R.layout.bubble_expanded_view, this /* root */, false /* attachToRoot */);
         mOverflowExpandedView.setOverflow(true);
 
-        mInflater.inflate(R.layout.bubble_overflow_button, this);
-        mOverflowBtn = findViewById(R.id.bubble_overflow_button);
+        mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
+                this /* root */,
+                false /* attachToRoot */);
+
+        mBubbleContainer.addView(mOverflowBtn, 0,
+                new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+
         mOverflowBtn.setOnClickListener(v -> {
             setSelectedBubble(null);
         });
@@ -541,11 +546,13 @@
         ColorDrawable bg = new ColorDrawable(bgColor);
         AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
         mOverflowBtn.setImageDrawable(adaptiveIcon);
+
         mOverflowBtn.setVisibility(GONE);
     }
 
     void showExpandedViewContents(int displayId) {
-        if (mOverflowExpandedView.getVirtualDisplayId() == displayId) {
+        if (mOverflowExpandedView != null
+                && mOverflowExpandedView.getVirtualDisplayId() == displayId) {
             mOverflowExpandedView.setContentVisibility(true);
         } else if (mExpandedBubble != null
                 && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
@@ -714,7 +721,7 @@
     private void updateSystemGestureExcludeRects() {
         // Exclude the region occupied by the first BubbleView in the stack
         Rect excludeZone = mSystemGestureExclusionRects.get(0);
-        if (mBubbleContainer.getChildCount() > 0) {
+        if (getBubbleCount() > 0) {
             View firstBubble = mBubbleContainer.getChildAt(0);
             excludeZone.set(firstBubble.getLeft(), firstBubble.getTop(), firstBubble.getRight(),
                     firstBubble.getBottom());
@@ -775,7 +782,7 @@
             Log.d(TAG, "addBubble: " + bubble);
         }
 
-        if (mBubbleContainer.getChildCount() == 0) {
+        if (getBubbleCount() == 0) {
             mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
         }
 
@@ -817,16 +824,13 @@
         }
         if (mIsExpanded) {
             if (DEBUG_BUBBLE_STACK_VIEW) {
-                Log.d(TAG, "Expanded && overflow > 0. Show overflow button at");
-                Log.d(TAG, "x: " + mExpandedAnimationController.getOverflowBtnLeft());
-                Log.d(TAG, "y: " + mExpandedAnimationController.getExpandedY());
+                Log.d(TAG, "Show overflow button.");
             }
-            mOverflowBtn.setX(mExpandedAnimationController.getOverflowBtnLeft());
-            mOverflowBtn.setY(mExpandedAnimationController.getExpandedY());
             mOverflowBtn.setVisibility(VISIBLE);
-            mExpandedAnimationController.setShowOverflowBtn(true);
             if (apply) {
-                mExpandedAnimationController.expandFromStack(null /* after */);
+                mExpandedAnimationController.expandFromStack(() -> {
+                    updatePointerPosition();
+                } /* after */);
             }
         } else {
             if (DEBUG_BUBBLE_STACK_VIEW) {
@@ -947,7 +951,7 @@
         if (mIsExpanded) {
             if (isIntersecting(mBubbleContainer, x, y)) {
                 // Could be tapping or dragging a bubble while expanded
-                for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+                for (int i = 0; i < getBubbleCount(); i++) {
                     BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i);
                     if (isIntersecting(view, x, y)) {
                         return view;
@@ -1103,7 +1107,7 @@
 
     /** Return the BubbleView at the given index from the bubble container. */
     public BadgedImageView getBubbleAt(int i) {
-        return mBubbleContainer.getChildCount() > i
+        return getBubbleCount() > i
                 ? (BadgedImageView) mBubbleContainer.getChildAt(i)
                 : null;
     }
@@ -1567,7 +1571,7 @@
             return;
         }
         if (!mIsExpanded) {
-            if (mBubbleContainer.getChildCount() > 0) {
+            if (getBubbleCount() > 0) {
                 mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
             }
             // Increase the touch target size of the bubble
@@ -1661,7 +1665,7 @@
 
     /** Sets the appropriate Z-order and dot position for each bubble in the stack. */
     private void updateBubbleZOrdersAndDotPosition(boolean animate) {
-        int bubbleCount = mBubbleContainer.getChildCount();
+        int bubbleCount = getBubbleCount();
         for (int i = 0; i < bubbleCount; i++) {
             BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
             bv.setZ((mMaxBubbles * mBubbleElevation) - i);
@@ -1677,30 +1681,23 @@
         if (expandedBubble == null) {
             return;
         }
-
         int index = getBubbleIndex(expandedBubble);
-        if (index >= mMaxBubbles) {
-            // In between state, where extra bubble will be overflowed, and new bubble added
-            index = 0;
-        }
         float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
         float halfBubble = mBubbleSize / 2f;
         float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
         // Padding might be adjusted for insets, so get it directly from the view
         bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
-
-        if (index >= mMaxBubbles) {
-            Bubble first = mBubbleData.getBubbles().get(0);
-            first.getExpandedView().setPointerPosition(bubbleCenter);
-        } else {
-            expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
-        }
+        expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
     }
 
     /**
      * @return the number of bubbles in the stack view.
      */
     public int getBubbleCount() {
+        if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+            // Subtract 1 for the overflow button which is always in the bubble container.
+            return mBubbleContainer.getChildCount() - 1;
+        }
         return mBubbleContainer.getChildCount();
     }
 
@@ -1797,7 +1794,7 @@
     /** For debugging only */
     List<Bubble> getBubblesOnScreen() {
         List<Bubble> bubbles = new ArrayList<>();
-        for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+        for (int i = 0; i < getBubbleCount(); i++) {
             View child = mBubbleContainer.getChildAt(i);
             if (child instanceof BadgedImageView) {
                 String key = ((BadgedImageView) child).getKey();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 6d6969d..aa549dc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -31,6 +31,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleExperimentConfig;
 
 import com.google.android.collect.Sets;
 
@@ -67,13 +68,13 @@
     private float mBubblePaddingTop;
     /** Size of each bubble. */
     private float mBubbleSizePx;
-    /** Width of the overflow button. */
-    private float mOverflowBtnWidth;
+    /** Space between bubbles in row above expanded view. */
+    private float mSpaceBetweenBubbles;
     /** Height of the status bar. */
     private float mStatusBarHeight;
     /** Size of display. */
     private Point mDisplaySize;
-    /** Max number of bubbles shown in row above expanded view.*/
+    /** Max number of bubbles shown in row above expanded view. */
     private int mBubblesMaxRendered;
     /** What the current screen orientation is. */
     private int mScreenOrientation;
@@ -99,7 +100,6 @@
     private boolean mSpringingBubbleToTouch = false;
 
     private int mExpandedViewPadding;
-    private boolean mShowOverflowBtn;
 
     public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
             int orientation) {
@@ -153,14 +153,6 @@
         }
     }
 
-    public void setShowOverflowBtn(boolean showBtn) {
-        mShowOverflowBtn = showBtn;
-    }
-
-    public boolean getShowOverflowBtn() {
-        return mShowOverflowBtn;
-    }
-
     /**
      * Animates the bubbles along a curved path, either to expand them along the top or collapse
      * them back into a stack.
@@ -369,10 +361,10 @@
         }
         final WindowInsets insets = mLayout.getRootWindowInsets();
         return mBubblePaddingTop + Math.max(
-            mStatusBarHeight,
-            insets.getDisplayCutout() != null
-                ? insets.getDisplayCutout().getSafeInsetTop()
-                : 0);
+                mStatusBarHeight,
+                insets.getDisplayCutout() != null
+                        ? insets.getDisplayCutout().getSafeInsetTop()
+                        : 0);
     }
 
     /** Description of current animation controller state. */
@@ -391,11 +383,15 @@
         mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
-        mOverflowBtnWidth = mBubbleSizePx;
         mStatusBarHeight =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
         mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
 
+        // Includes overflow button.
+        float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
+                - (mBubblesMaxRendered + 1) * mBubbleSizePx;
+        mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
+
         // Ensure that all child views are at 1x scale, and visible, in case they were animating
         // in.
         mLayout.setVisibility(View.VISIBLE);
@@ -506,18 +502,10 @@
      * @return Bubble left x from left edge of screen.
      */
     public float getBubbleLeft(int index) {
-        final float bubbleFromRowLeft = index * (mBubbleSizePx + getSpaceBetweenBubbles());
+        final float bubbleFromRowLeft = index * (mBubbleSizePx + mSpaceBetweenBubbles);
         return getRowLeft() + bubbleFromRowLeft;
     }
 
-    public float getOverflowBtnLeft() {
-        if (mLayout == null || mLayout.getChildCount() == 0) {
-            return 0;
-        }
-        return getBubbleLeft(mLayout.getChildCount() - 1) + mBubbleSizePx
-                + getSpaceBetweenBubbles();
-    }
-
     /**
      * When expanded, the bubbles are centered in the screen. In portrait, all available space is
      * used. In landscape we have too much space so the value is restricted. This method accounts
@@ -539,7 +527,7 @@
      * Determines the available screen width without the cutout.
      *
      * @param subtractStableInsets Whether or not stable insets should also be removed from the
-     *                            returned width.
+     *                             returned width.
      * @return the total screen width available accounting for cutouts and insets,
      * iff {@param includeStableInsets} is true.
      */
@@ -566,38 +554,13 @@
         if (mLayout == null) {
             return 0;
         }
-
-        int bubbleCount = mLayout.getChildCount();
-
-        final float totalBubbleWidth = bubbleCount * mBubbleSizePx;
-        final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
-        float rowWidth = totalGapWidth + totalBubbleWidth;
-        if (mShowOverflowBtn) {
-            rowWidth += getSpaceBetweenBubbles();
-            rowWidth += mOverflowBtnWidth;
-        }
+        float rowWidth = (mLayout.getChildCount() * mBubbleSizePx)
+                + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
 
         // This display size we're using includes the size of the insets, we want the true
         // center of the display minus the notch here, which means we should include the
         // stable insets (e.g. status bar, nav bar) in this calculation.
         final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
-        final float halfRow = rowWidth / 2f;
-        final float rowLeft = trueCenter - halfRow;
-        return rowLeft;
-    }
-
-    /**
-     * @return Space between bubbles in row above expanded view.
-     */
-    private float getSpaceBetweenBubbles() {
-        final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx;
-        final float rowMargins = mExpandedViewPadding * 2;
-        float totalGapWidth = getWidthForDisplayingBubbles() - rowMargins - totalBubbleWidth;
-        if (mShowOverflowBtn) {
-            totalGapWidth -= mBubbleSizePx;
-        }
-        final int gapCount = mBubblesMaxRendered - 1;
-        final float gapWidth = totalGapWidth / gapCount;
-        return gapWidth;
+        return trueCenter - (rowWidth / 2f);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e14581f..e8509b3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -26,6 +26,7 @@
 import com.android.systemui.SystemUIAppComponentFactory;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.InjectionInflationController;
 
@@ -105,4 +106,9 @@
      * Member injection into the supplied argument.
      */
     void inject(ContentProvider contentProvider);
+
+    /**
+     * Member injection into the supplied argument.
+     */
+    void inject(KeyguardSliceProvider keyguardSliceProvider);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index e66a9fa..2cc3d9e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -52,13 +52,13 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIAppComponentFactory;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.wakelock.SettableWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -68,6 +68,8 @@
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
+import javax.inject.Inject;
+
 /**
  * Simple Slice provider that shows the current date.
  */
@@ -108,26 +110,31 @@
     private final Handler mHandler;
     private final Handler mMediaHandler;
     private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
-    private DozeParameters mDozeParameters;
+    @Inject
+    public DozeParameters mDozeParameters;
     @VisibleForTesting
     protected SettableWakeLock mMediaWakeLock;
-    @VisibleForTesting
-    protected ZenModeController mZenModeController;
+    @Inject
+    public ZenModeController mZenModeController;
     private String mDatePattern;
     private DateFormat mDateFormat;
     private String mLastText;
     private boolean mRegistered;
     private String mNextAlarm;
-    private NextAlarmController mNextAlarmController;
-    @VisibleForTesting
-    protected AlarmManager mAlarmManager;
-    @VisibleForTesting
-    protected ContentResolver mContentResolver;
+    @Inject
+    public NextAlarmController mNextAlarmController;
+    @Inject
+    public AlarmManager mAlarmManager;
+    @Inject
+    public ContentResolver mContentResolver;
     private AlarmManager.AlarmClockInfo mNextAlarmInfo;
     private PendingIntent mPendingIntent;
-    protected NotificationMediaManager mMediaManager;
-    private StatusBarStateController mStatusBarStateController;
-    private KeyguardBypassController mKeyguardBypassController;
+    @Inject
+    public NotificationMediaManager mMediaManager;
+    @Inject
+    public StatusBarStateController mStatusBarStateController;
+    @Inject
+    public KeyguardBypassController mKeyguardBypassController;
     private CharSequence mMediaTitle;
     private CharSequence mMediaArtist;
     protected boolean mDozing;
@@ -188,26 +195,6 @@
         mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
     }
 
-    /**
-     * Initialize dependencies that don't exist during {@link android.content.ContentProvider}
-     * instantiation.
-     *
-     * @param mediaManager {@link NotificationMediaManager} singleton.
-     * @param statusBarStateController {@link StatusBarStateController} singleton.
-     */
-    public void initDependencies(
-            NotificationMediaManager mediaManager,
-            StatusBarStateController statusBarStateController,
-            KeyguardBypassController keyguardBypassController,
-            DozeParameters dozeParameters) {
-        mMediaManager = mediaManager;
-        mMediaManager.addCallback(this);
-        mStatusBarStateController = statusBarStateController;
-        mStatusBarStateController.addCallback(this);
-        mKeyguardBypassController = keyguardBypassController;
-        mDozeParameters = dozeParameters;
-    }
-
     @AnyThread
     @Override
     public Slice onBindSlice(Uri sliceUri) {
@@ -310,25 +297,19 @@
 
     @Override
     public boolean onCreateSliceProvider() {
-        if (mContextAvailableCallback != null) {
-            mContextAvailableCallback.onContextAvailable(getContext());
-        }
+        mContextAvailableCallback.onContextAvailable(getContext());
+        inject();
         synchronized (KeyguardSliceProvider.sInstanceLock) {
             KeyguardSliceProvider oldInstance = KeyguardSliceProvider.sInstance;
             if (oldInstance != null) {
                 oldInstance.onDestroy();
             }
-
-            mAlarmManager = getContext().getSystemService(AlarmManager.class);
-            mContentResolver = getContext().getContentResolver();
-            mNextAlarmController = new NextAlarmControllerImpl(getContext());
-            mNextAlarmController.addCallback(this);
-            mZenModeController = Dependency.get(ZenModeController.class);
-            mZenModeController.addCallback(this);
             mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
             mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
-            mMediaWakeLock = new SettableWakeLock(WakeLock.createPartial(getContext(), "media"),
-                    "media");
+            mMediaManager.addCallback(this);
+            mStatusBarStateController.addCallback(this);
+            mNextAlarmController.addCallback(this);
+            mZenModeController.addCallback(this);
             KeyguardSliceProvider.sInstance = this;
             registerClockUpdate();
             updateClockLocked();
@@ -337,6 +318,13 @@
     }
 
     @VisibleForTesting
+    protected void inject() {
+        SystemUIFactory.getInstance().getRootComponent().inject(this);
+        mMediaWakeLock = new SettableWakeLock(WakeLock.createPartial(getContext(), "media"),
+                "media");
+    }
+
+    @VisibleForTesting
     protected void onDestroy() {
         synchronized (KeyguardSliceProvider.sInstanceLock) {
             mNextAlarmController.removeCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index b682cb0..3fcd1c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -31,7 +31,6 @@
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.util.Log;
-import android.util.StatsLog;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -41,6 +40,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.systemui.Dependency;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
@@ -122,7 +122,7 @@
     private void handleClick() {
         showDeviceMonitoringDialog();
         DevicePolicyEventLogger
-                .createEvent(StatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
+                .createEvent(FrameworkStatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
                 .write();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 976531d..d38f1b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -79,6 +79,7 @@
 
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final KeyguardStateController mKeyguardStateController;
+    private final Object mLock = new Object();
 
     // Lazy
     private NotificationEntryManager mEntryManager;
@@ -181,6 +182,7 @@
     protected final Context mContext;
     private final Handler mMainHandler;
     protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
+    protected final ArrayList<UserInfo> mCurrentManagedProfiles = new ArrayList<>();
 
     protected int mCurrentUserId = 0;
     protected NotificationPresenter mPresenter;
@@ -300,7 +302,7 @@
     }
 
     public boolean isCurrentProfile(int userId) {
-        synchronized (mCurrentProfiles) {
+        synchronized (mLock) {
             return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
         }
     }
@@ -417,6 +419,20 @@
         return mUsersAllowingPrivateNotifications.get(userHandle);
     }
 
+    /**
+     * If all managed profiles (work profiles) can show private data in public (secure & locked.)
+     */
+    public boolean allowsManagedPrivateNotificationsInPublic() {
+        synchronized (mLock) {
+            for (UserInfo profile : mCurrentManagedProfiles) {
+                if (!userAllowsPrivateNotificationsInPublic(profile.id)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
     private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
         if (userHandle == UserHandle.USER_ALL) {
             return true;
@@ -495,11 +511,15 @@
     }
 
     private void updateCurrentProfilesCache() {
-        synchronized (mCurrentProfiles) {
+        synchronized (mLock) {
             mCurrentProfiles.clear();
+            mCurrentManagedProfiles.clear();
             if (mUserManager != null) {
                 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
                     mCurrentProfiles.put(user.id, user);
+                    if (UserManager.USER_TYPE_PROFILE_MANAGED.equals(user.userType)) {
+                        mCurrentManagedProfiles.add(user);
+                    }
                 }
             }
         }
@@ -510,10 +530,29 @@
         });
     }
 
+    /**
+     * If any of the profiles are in public mode.
+     */
     public boolean isAnyProfilePublicMode() {
-        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
-            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
-                return true;
+        synchronized (mLock) {
+            for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+                if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * If any managed/work profiles are in public mode.
+     */
+    public boolean isAnyManagedProfilePublicMode() {
+        synchronized (mLock) {
+            for (int i = mCurrentManagedProfiles.size() - 1; i >= 0; i--) {
+                if (isLockscreenPublicMode(mCurrentManagedProfiles.get(i).id)) {
+                    return true;
+                }
             }
         }
         return false;
@@ -620,9 +659,17 @@
         pw.print("  mAllowLockscreenRemoteInput=");
         pw.println(mAllowLockscreenRemoteInput);
         pw.print("  mCurrentProfiles=");
-        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
-            final int userId = mCurrentProfiles.valueAt(i).id;
-            pw.print("" + userId + " ");
+        synchronized (mLock) {
+            for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+                final int userId = mCurrentProfiles.valueAt(i).id;
+                pw.print("" + userId + " ");
+            }
+        }
+        pw.print("  mCurrentManagedProfiles=");
+        synchronized (mLock) {
+            for (UserInfo userInfo : mCurrentManagedProfiles) {
+                pw.print("" + userInfo.id + " ");
+            }
         }
         pw.println();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index ad1aa83..b4d0d47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -33,7 +33,7 @@
 import javax.inject.Singleton
 
 @Singleton
-class KeyguardBypassController : Dumpable {
+open class KeyguardBypassController : Dumpable {
 
     private val mKeyguardStateController: KeyguardStateController
     private val statusBarStateController: StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 4e8442f..1726b488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -144,7 +144,6 @@
 import com.android.systemui.fragments.ExtensionFragmentListener;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -794,13 +793,6 @@
         mBypassHeadsUpNotifier.setUp(mEntryManager);
         mBubbleController.setExpandListener(mBubbleExpandListener);
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
-        KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
-        if (sliceProvider != null) {
-            sliceProvider.initDependencies(mMediaManager, mStatusBarStateController,
-                    mKeyguardBypassController, mDozeParameters);
-        } else {
-            Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
-        }
 
         mColorExtractor.addOnColorsChangedListener(this);
         mStatusBarStateController.addCallback(this,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index c9b6790..92c1d76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.wakelock.SettableWakeLock;
 
@@ -87,6 +88,8 @@
     private SettableWakeLock mMediaWakeLock;
     @Mock
     private DozeParameters mDozeParameters;
+    @Mock
+    private NextAlarmController mNextAlarmController;
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private TestableKeyguardSliceProvider mProvider;
     private boolean mIsZenMode;
@@ -97,9 +100,9 @@
         mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
         mIsZenMode = false;
         mProvider = new TestableKeyguardSliceProvider();
+        mProvider.setContextAvailableCallback(context -> { });
         mProvider.attachInfo(getContext(), null);
-        mProvider.initDependencies(mNotificationMediaManager, mStatusBarStateController,
-                mKeyguardBypassController, mDozeParameters);
+        reset(mContentResolver);
         SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
     }
 
@@ -254,13 +257,16 @@
         }
 
         @Override
-        public boolean onCreateSliceProvider() {
-            super.onCreateSliceProvider();
+        protected void inject() {
             mAlarmManager = KeyguardSliceProviderTest.this.mAlarmManager;
             mContentResolver = KeyguardSliceProviderTest.this.mContentResolver;
             mZenModeController = KeyguardSliceProviderTest.this.mZenModeController;
             mMediaWakeLock = KeyguardSliceProviderTest.this.mMediaWakeLock;
-            return true;
+            mDozeParameters = KeyguardSliceProviderTest.this.mDozeParameters;
+            mNextAlarmController = KeyguardSliceProviderTest.this.mNextAlarmController;
+            mStatusBarStateController = KeyguardSliceProviderTest.this.mStatusBarStateController;
+            mKeyguardBypassController = KeyguardSliceProviderTest.this.mKeyguardBypassController;
+            mMediaManager = KeyguardSliceProviderTest.this.mNotificationMediaManager;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 3a6acce..c6d57e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -95,7 +95,9 @@
     @Mock
     private KeyguardStateController mKeyguardStateController;
 
-    private int mCurrentUserId;
+    private UserInfo mCurrentUser;
+    private UserInfo mSecondaryUser;
+    private UserInfo mWorkUser;
     private TestNotificationLockscreenUserManager mLockscreenUserManager;
 
     @Before
@@ -103,10 +105,14 @@
         MockitoAnnotations.initMocks(this);
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
 
-        mCurrentUserId = ActivityManager.getCurrentUser();
+        int currentUserId = ActivityManager.getCurrentUser();
+        mCurrentUser = new UserInfo(currentUserId, "", 0);
+        mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
+        mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
+                UserManager.USER_TYPE_PROFILE_MANAGED);
 
-        when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList(
-                new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0)));
+        when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList(
+                mCurrentUser, mSecondaryUser, mWorkUser));
         mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
                 Handler.createAsync(Looper.myLooper()));
 
@@ -141,15 +147,31 @@
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
-        assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUserId));
+        assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
     }
 
     @Test
     public void testLockScreenAllowPrivateNotificationsFalse() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
-        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUserId));
+        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+    }
+
+    @Test
+    public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+        assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
+    }
+
+    @Test
+    public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+        assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
     }
 
     @Test
@@ -163,16 +185,16 @@
     public void testActionUserSwitchedCallsOnUserSwitched() {
         Intent intent = new Intent()
                 .setAction(ACTION_USER_SWITCHED)
-                .putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId + 1);
+                .putExtra(Intent.EXTRA_USER_HANDLE, mSecondaryUser.id);
         mLockscreenUserManager.getBaseBroadcastReceiverForTest().onReceive(mContext, intent);
-        verify(mPresenter, times(1)).onUserSwitched(mCurrentUserId + 1);
+        verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
     }
 
     @Test
     public void testIsLockscreenPublicMode() {
-        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUserId));
-        mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUserId);
-        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUserId));
+        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
+        mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id);
+        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
     }
 
     @Test
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index f1f2d2a..3bc93cc 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -30,7 +30,6 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -88,6 +87,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.AbstractLocationProvider.State;
+import com.android.server.location.AppForegroundHelper;
 import com.android.server.location.CallerIdentity;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
@@ -98,12 +98,12 @@
 import com.android.server.location.LocationRequestStatistics;
 import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
 import com.android.server.location.LocationRequestStatistics.PackageStatistics;
-import com.android.server.location.LocationSettingsStore;
 import com.android.server.location.LocationUsageLogger;
 import com.android.server.location.MockProvider;
 import com.android.server.location.MockableLocationProvider;
 import com.android.server.location.PassiveProvider;
-import com.android.server.location.UserInfoStore;
+import com.android.server.location.SettingsHelper;
+import com.android.server.location.UserInfoHelper;
 import com.android.server.location.gnss.GnssManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
@@ -140,7 +140,6 @@
         public Lifecycle(Context context) {
             super(context);
             mService = new LocationManagerService(context);
-            LocalServices.addService(LocationManagerInternal.class, mService.new LocalService());
         }
 
         @Override
@@ -181,9 +180,6 @@
     // maximum age of a location before it is no longer considered "current"
     private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
 
-    private static final int FOREGROUND_IMPORTANCE_CUTOFF
-            = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
-
     // Location Providers may sometimes deliver location updates
     // slightly faster that requested - provide grace period so
     // we don't unnecessarily filter events that are otherwise on
@@ -195,21 +191,23 @@
     private final Object mLock = new Object();
     private final Context mContext;
     private final Handler mHandler;
-    private final UserInfoStore mUserInfoStore;
-    private final LocationSettingsStore mSettingsStore;
+    private final LocalService mLocalService;
+    private final UserInfoHelper mUserInfoHelper;
+    private final SettingsHelper mSettingsHelper;
+    private final AppForegroundHelper mAppForegroundHelper;
     private final LocationUsageLogger mLocationUsageLogger;
 
+    @Nullable private GnssManagerService mGnssManagerService = null;
+
     private final PassiveLocationProviderManager mPassiveManager;
 
     private AppOpsManager mAppOps;
     private PackageManager mPackageManager;
     private PowerManager mPowerManager;
-    private ActivityManager mActivityManager;
 
     private GeofenceManager mGeofenceManager;
     private LocationFudger mLocationFudger;
     private GeocoderProxy mGeocodeProvider;
-    @Nullable private GnssManagerService mGnssManagerService;
 
     @GuardedBy("mLock")
     private String mExtraLocationControllerPackage;
@@ -245,8 +243,13 @@
     private LocationManagerService(Context context) {
         mContext = context;
         mHandler = FgThread.getHandler();
-        mUserInfoStore = new UserInfoStore(mContext);
-        mSettingsStore = new LocationSettingsStore(mContext, mHandler);
+        mLocalService = new LocalService();
+
+        LocalServices.addService(LocationManagerInternal.class, mLocalService);
+
+        mUserInfoHelper = new UserInfoHelper(mContext);
+        mSettingsHelper = new SettingsHelper(mContext, mHandler);
+        mAppForegroundHelper = new AppForegroundHelper(mContext);
         mLocationUsageLogger = new LocationUsageLogger();
 
         // set up passive provider -  we do this early because it has no dependencies on system
@@ -272,17 +275,23 @@
     }
 
     private void onSystemReady() {
-        mUserInfoStore.onSystemReady();
-        mSettingsStore.onSystemReady();
+        mUserInfoHelper.onSystemReady();
+        mSettingsHelper.onSystemReady();
+        mAppForegroundHelper.onSystemReady();
+
+        if (GnssManagerService.isGnssSupported()) {
+            mGnssManagerService = new GnssManagerService(mContext, mSettingsHelper,
+                    mAppForegroundHelper, mLocationUsageLogger);
+            mGnssManagerService.onSystemReady();
+        }
 
         synchronized (mLock) {
             mPackageManager = mContext.getPackageManager();
             mAppOps = mContext.getSystemService(AppOpsManager.class);
             mPowerManager = mContext.getSystemService(PowerManager.class);
-            mActivityManager = mContext.getSystemService(ActivityManager.class);
 
             mLocationFudger = new LocationFudger(mContext, mHandler);
-            mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
+            mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper);
 
             PowerManagerInternal localPowerManager =
                     LocalServices.getService(PowerManagerInternal.class);
@@ -313,17 +322,6 @@
                             }
                         });
                     });
-            mActivityManager.addOnUidImportanceListener(
-                    (uid, importance) -> {
-                        // listener invoked on ui thread, move to our thread to reduce risk of
-                        // blocking ui thread
-                        mHandler.post(() -> {
-                            synchronized (mLock) {
-                                onUidImportanceChangedLocked(uid, importance);
-                            }
-                        });
-                    },
-                    FOREGROUND_IMPORTANCE_CUTOFF);
 
             localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
                     state -> {
@@ -337,26 +335,13 @@
                     });
             mBatterySaverMode = mPowerManager.getLocationPowerSaveMode();
 
-            mSettingsStore.addOnLocationEnabledChangedListener((userId) -> {
-                synchronized (mLock) {
-                    onLocationModeChangedLocked(userId);
-                }
-            });
-            mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
-                synchronized (mLock) {
-                    onBackgroundThrottleIntervalChangedLocked();
-                }
-            });
-            mSettingsStore.addOnBackgroundThrottlePackageWhitelistChangedListener(() -> {
-                synchronized (mLock) {
-                    onBackgroundThrottleWhitelistChangedLocked();
-                }
-            });
-            mSettingsStore.addOnIgnoreSettingsPackageWhitelistChangedListener(() -> {
-                synchronized (mLock) {
-                    onIgnoreSettingsWhitelistChangedLocked();
-                }
-            });
+            mSettingsHelper.addOnLocationEnabledChangedListener(this::onLocationModeChanged);
+            mSettingsHelper.addOnBackgroundThrottleIntervalChangedListener(
+                    this::onBackgroundThrottleIntervalChanged);
+            mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener(
+                    this::onBackgroundThrottleWhitelistChanged);
+            mSettingsHelper.addOnIgnoreSettingsPackageWhitelistChangedListener(
+                    this::onIgnoreSettingsWhitelistChanged);
 
             new PackageMonitor() {
                 @Override
@@ -367,11 +352,9 @@
                 }
             }.register(mContext, mHandler.getLooper(), true);
 
-            mUserInfoStore.addListener((oldUserId, newUserId) -> {
-                synchronized (mLock) {
-                    onUserChangedLocked(oldUserId, newUserId);
-                }
-            });
+            mUserInfoHelper.addListener(this::onUserChanged);
+
+            mAppForegroundHelper.addListener(this::onAppForegroundChanged);
 
             IntentFilter intentFilter = new IntentFilter();
             intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -398,7 +381,7 @@
             // switching the user from null to current here performs the bulk of the initialization
             // work. the user being changed will cause a reload of all user specific settings, which
             // causes initialization, and propagates changes until a steady state is reached
-            onUserChangedLocked(UserHandle.USER_NULL, mUserInfoStore.getCurrentUserId());
+            onUserChanged(UserHandle.USER_NULL, mUserInfoHelper.getCurrentUserId());
         }
     }
 
@@ -455,20 +438,23 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void onLocationModeChangedLocked(int userId) {
+    private void onLocationModeChanged(int userId) {
+        boolean enabled = mSettingsHelper.isLocationEnabled(userId);
+
         if (D) {
-            Log.d(TAG, "[u" + userId + "] location enabled = " + isLocationEnabledForUser(userId));
+            Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
         }
 
-        Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
-                .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId))
-                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+        synchronized (mLock) {
+            Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
+                    .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
+                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
 
-        for (LocationProviderManager manager : mProviderManagers) {
+            for (LocationProviderManager manager : mProviderManagers) {
             manager.onEnabledChangedLocked(userId);
+            }
         }
     }
 
@@ -493,58 +479,55 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void onUidImportanceChangedLocked(int uid, int importance) {
-        boolean foreground = LocationManagerServiceUtils.isImportanceForeground(importance);
-        HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
-        for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
-            String provider = entry.getKey();
-            for (UpdateRecord record : entry.getValue()) {
-                if (record.mReceiver.mCallerIdentity.mUid == uid
-                        && record.mIsForegroundUid != foreground) {
-                    if (D) {
-                        Log.d(TAG, "request from uid " + uid + " is now "
-                                + LocationManagerServiceUtils.foregroundAsString(
-                                foreground));
-                    }
-                    record.updateForeground(foreground);
+    private void onAppForegroundChanged(int uid, boolean foreground) {
+        synchronized (mLock) {
+            HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
+            for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
+                String provider = entry.getKey();
+                for (UpdateRecord record : entry.getValue()) {
+                    if (record.mReceiver.mCallerIdentity.mUid == uid
+                            && record.mIsForegroundUid != foreground) {
+                        record.updateForeground(foreground);
 
-                    if (!isThrottlingExemptLocked(record.mReceiver.mCallerIdentity)) {
-                        affectedProviders.add(provider);
+                        if (!isThrottlingExempt(record.mReceiver.mCallerIdentity)) {
+                            affectedProviders.add(provider);
+                        }
                     }
                 }
             }
-        }
-        for (String provider : affectedProviders) {
-            applyRequirementsLocked(provider);
+            for (String provider : affectedProviders) {
+                applyRequirementsLocked(provider);
+            }
         }
     }
 
-    @GuardedBy("mLock")
-    private void onBackgroundThrottleIntervalChangedLocked() {
-        for (LocationProviderManager manager : mProviderManagers) {
-            applyRequirementsLocked(manager);
+    private void onBackgroundThrottleIntervalChanged() {
+        synchronized (mLock) {
+            for (LocationProviderManager manager : mProviderManagers) {
+                applyRequirementsLocked(manager);
+            }
         }
     }
 
-    @GuardedBy("mLock")
-    private void onBackgroundThrottleWhitelistChangedLocked() {
-        for (LocationProviderManager manager : mProviderManagers) {
-            applyRequirementsLocked(manager);
+    private void onBackgroundThrottleWhitelistChanged() {
+        synchronized (mLock) {
+            for (LocationProviderManager manager : mProviderManagers) {
+                applyRequirementsLocked(manager);
+            }
         }
     }
 
-    @GuardedBy("lock")
-    private void onIgnoreSettingsWhitelistChangedLocked() {
-        for (LocationProviderManager manager : mProviderManagers) {
-            applyRequirementsLocked(manager);
+    private void onIgnoreSettingsWhitelistChanged() {
+        synchronized (mLock) {
+            for (LocationProviderManager manager : mProviderManagers) {
+                applyRequirementsLocked(manager);
+            }
         }
     }
 
     @GuardedBy("mLock")
     private void initializeProvidersLocked() {
-        if (GnssManagerService.isGnssSupported()) {
-            mGnssManagerService = new GnssManagerService(this, mContext, mLocationUsageLogger);
+        if (mGnssManagerService != null) {
             LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER);
             mProviderManagers.add(gnssManager);
             gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
@@ -627,19 +610,20 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void onUserChangedLocked(int oldUserId, int newUserId) {
+    private void onUserChanged(int oldUserId, int newUserId) {
         if (D) {
             Log.d(TAG, "foreground user is changing to " + newUserId);
         }
 
-        for (LocationProviderManager manager : mProviderManagers) {
-            // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
-            mSettingsStore.setLocationProviderAllowed(manager.getName(),
-                    manager.isEnabled(newUserId), newUserId);
+        synchronized (mLock) {
+            for (LocationProviderManager manager : mProviderManagers) {
+                // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+                mSettingsHelper.setLocationProviderAllowed(manager.getName(),
+                        manager.isEnabled(newUserId), newUserId);
 
-            manager.onEnabledChangedLocked(oldUserId);
-            manager.onEnabledChangedLocked(newUserId);
+                manager.onEnabledChangedLocked(oldUserId);
+                manager.onEnabledChangedLocked(newUserId);
+            }
         }
     }
 
@@ -778,7 +762,7 @@
                 // it would be more correct to call this for all users, but we know this can
                 // only affect the current user since providers are disabled for non-current
                 // users
-                onEnabledChangedLocked(mUserInfoStore.getCurrentUserId());
+                onEnabledChangedLocked(mUserInfoHelper.getCurrentUserId());
             }
         }
 
@@ -787,14 +771,14 @@
         }
 
         public boolean isEnabled() {
-            return isEnabled(mUserInfoStore.getCurrentUserId());
+            return isEnabled(mUserInfoHelper.getCurrentUserId());
         }
 
         public boolean isEnabled(int userId) {
             synchronized (mLock) {
                 // normalize user id to always refer to parent since profile state is always the
                 // same as parent state
-                userId = mUserInfoStore.getParentUserId(userId);
+                userId = mUserInfoHelper.getParentUserId(userId);
                 return mEnabled.get(userId, Boolean.FALSE);
             }
         }
@@ -808,13 +792,13 @@
 
             // normalize user id to always refer to parent since profile state is always the same
             // as parent state
-            userId = mUserInfoStore.getParentUserId(userId);
+            userId = mUserInfoHelper.getParentUserId(userId);
 
             // if any property that contributes to "enabled" here changes state, it MUST result
             // in a direct or indrect call to onEnabledChangedLocked. this allows the provider to
             // guarantee that it will always eventually reach the correct state.
-            boolean enabled = (userId == mUserInfoStore.getCurrentUserId())
-                    && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().allowed;
+            boolean enabled = (userId == mUserInfoHelper.getCurrentUserId())
+                    && mSettingsHelper.isLocationEnabled(userId) && mProvider.getState().allowed;
 
             if (enabled == isEnabled(userId)) {
                 return;
@@ -829,7 +813,7 @@
             // fused and passive provider never get public updates for legacy reasons
             if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
                 // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
-                mSettingsStore.setLocationProviderAllowed(mName, enabled, userId);
+                mSettingsHelper.setLocationProviderAllowed(mName, enabled, userId);
 
                 Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
                         .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
@@ -1009,7 +993,7 @@
                     if (manager == null) {
                         continue;
                     }
-                    if (!manager.isEnabled() && !isSettingsExemptLocked(updateRecord)) {
+                    if (!manager.isEnabled() && !isSettingsExempt(updateRecord)) {
                         continue;
                     }
 
@@ -1475,13 +1459,13 @@
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
         if (records != null) {
             for (UpdateRecord record : records) {
-                if (!mUserInfoStore.isCurrentUserOrProfile(
+                if (!mUserInfoHelper.isCurrentUserOrProfile(
                         UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
                     continue;
                 }
 
                 // requests that ignore location settings will never provide notifications
-                if (isSettingsExemptLocked(record)) {
+                if (isSettingsExempt(record)) {
                     continue;
                 }
 
@@ -1520,14 +1504,7 @@
         // if provider is not active, it should not respond to requests
 
         if (mProviderManagers.contains(manager) && records != null && !records.isEmpty()) {
-            long backgroundThrottleInterval;
-
-            long identity = Binder.clearCallingIdentity();
-            try {
-                backgroundThrottleInterval = mSettingsStore.getBackgroundThrottleIntervalMs();
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
+            long backgroundThrottleInterval = mSettingsHelper.getBackgroundThrottleIntervalMs();
 
             ArrayList<LocationRequest> requests = new ArrayList<>(records.size());
 
@@ -1540,7 +1517,7 @@
             // initialize the low power mode to true and set to false if any of the records requires
             providerRequest.setLowPowerMode(true);
             for (UpdateRecord record : records) {
-                if (!mUserInfoStore.isCurrentUserOrProfile(
+                if (!mUserInfoHelper.isCurrentUserOrProfile(
                         UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
                     continue;
                 }
@@ -1554,7 +1531,7 @@
                 final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
                         || (isForegroundOnlyMode && !record.mIsForegroundUid);
                 if (!manager.isEnabled() || isBatterySaverDisablingLocation) {
-                    if (isSettingsExemptLocked(record)) {
+                    if (isSettingsExempt(record)) {
                         providerRequest.setLocationSettingsIgnored(true);
                         providerRequest.setLowPowerMode(false);
                     } else {
@@ -1567,7 +1544,7 @@
 
 
                 // if we're forcing location, don't apply any throttling
-                if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExemptLocked(
+                if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExempt(
                         record.mReceiver.mCallerIdentity)) {
                     if (!record.mIsForegroundUid) {
                         interval = Math.max(interval, backgroundThrottleInterval);
@@ -1599,7 +1576,7 @@
                 // TODO: overflow
                 long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2;
                 for (UpdateRecord record : records) {
-                    if (mUserInfoStore.isCurrentUserOrProfile(
+                    if (mUserInfoHelper.isCurrentUserOrProfile(
                             UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
                         LocationRequest locationRequest = record.mRequest;
 
@@ -1648,41 +1625,39 @@
 
     @Override
     public String[] getBackgroundThrottlingWhitelist() {
-        return mSettingsStore.getBackgroundThrottlePackageWhitelist().toArray(new String[0]);
+        return mSettingsHelper.getBackgroundThrottlePackageWhitelist().toArray(new String[0]);
     }
 
     @Override
     public String[] getIgnoreSettingsWhitelist() {
-        return mSettingsStore.getIgnoreSettingsPackageWhitelist().toArray(new String[0]);
+        return mSettingsHelper.getIgnoreSettingsPackageWhitelist().toArray(new String[0]);
     }
 
-    @GuardedBy("mLock")
-    public boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
+    private boolean isThrottlingExempt(CallerIdentity callerIdentity) {
         if (callerIdentity.mUid == Process.SYSTEM_UID) {
             return true;
         }
 
-        if (mSettingsStore.getBackgroundThrottlePackageWhitelist().contains(
+        if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
                 callerIdentity.mPackageName)) {
             return true;
         }
 
-        return isProviderPackage(callerIdentity.mPackageName);
+        return mLocalService.isProviderPackage(callerIdentity.mPackageName);
 
     }
 
-    @GuardedBy("mLock")
-    private boolean isSettingsExemptLocked(UpdateRecord record) {
+    private boolean isSettingsExempt(UpdateRecord record) {
         if (!record.mRealRequest.isLocationSettingsIgnored()) {
             return false;
         }
 
-        if (mSettingsStore.getIgnoreSettingsPackageWhitelist().contains(
+        if (mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains(
                 record.mReceiver.mCallerIdentity.mPackageName)) {
             return true;
         }
 
-        return isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName);
+        return mLocalService.isProviderPackage(record.mReceiver.mCallerIdentity.mPackageName);
 
     }
 
@@ -1705,10 +1680,7 @@
             mRealRequest = request;
             mRequest = request;
             mReceiver = receiver;
-            mIsForegroundUid =
-                    LocationManagerServiceUtils.isImportanceForeground(
-                            mActivityManager.getPackageImportance(
-                                    mReceiver.mCallerIdentity.mPackageName));
+            mIsForegroundUid = mAppForegroundHelper.isAppForeground(mReceiver.mCallerIdentity.mUid);
 
             if (D && receiver.mCallerIdentity.mPid == Process.myPid()) {
                 mStackTrace = new Throwable();
@@ -1753,7 +1725,7 @@
                     mReceiver.isListener(),
                     mReceiver.isPendingIntent(),
                     /* geofence= */ null,
-                    mActivityManager.getPackageImportance(packageName));
+                    mAppForegroundHelper.getImportance(mReceiver.mCallerIdentity.mUid));
 
             // remove from mRecordsByProvider
             ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
@@ -1923,7 +1895,7 @@
                         LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
                         packageName, request, listener != null, intent != null,
                         /* geofence= */ null,
-                        mActivityManager.getPackageImportance(packageName));
+                        mAppForegroundHelper.getImportance(uid));
 
                 Receiver receiver;
                 if (intent != null) {
@@ -1961,7 +1933,7 @@
             Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
                     + " " + name + " " + request + " from " + packageName + "(" + uid + " "
                     + (record.mIsForegroundUid ? "foreground" : "background")
-                    + (isThrottlingExemptLocked(receiver.mCallerIdentity)
+                    + (isThrottlingExempt(receiver.mCallerIdentity)
                     ? " [whitelisted]" : "") + ")");
         }
 
@@ -1970,7 +1942,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        if (!manager.isEnabled() && !isSettingsExemptLocked(record)) {
+        if (!manager.isEnabled() && !isSettingsExempt(record)) {
             // Notify the listener that updates are currently disabled - but only if the request
             // does not ignore location settings
             receiver.callProviderEnabledLocked(name, false);
@@ -2060,7 +2032,7 @@
             final int uid = Binder.getCallingUid();
             final long identity = Binder.clearCallingIdentity();
             try {
-                if (mSettingsStore.isLocationPackageBlacklisted(UserHandle.getUserId(uid),
+                if (mSettingsHelper.isLocationPackageBlacklisted(UserHandle.getUserId(uid),
                         packageName)) {
                     if (D) {
                         Log.d(TAG, "not returning last loc for blacklisted app: "
@@ -2077,8 +2049,8 @@
                 if (manager == null) return null;
 
                 // only the current user or location providers may get location this way
-                if (!mUserInfoStore.isCurrentUserOrProfile(UserHandle.getUserId(uid))
-                        && !isProviderPackage(packageName)) {
+                if (!mUserInfoHelper.isCurrentUserOrProfile(UserHandle.getUserId(uid))
+                        && !mLocalService.isProviderPackage(packageName)) {
                     return null;
                 }
 
@@ -2102,7 +2074,7 @@
                 String op = resolutionLevelToOpStr(allowedResolutionLevel);
                 long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
                         SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos());
-                if (locationAgeMs > mSettingsStore.getMaxLastLocationAgeMs()
+                if (locationAgeMs > mSettingsHelper.getMaxLastLocationAgeMs()
                         && (mAppOps.unsafeCheckOp(op, uid, packageName)
                         == AppOpsManager.MODE_FOREGROUND)) {
                     return null;
@@ -2145,29 +2117,21 @@
             long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
                     SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos());
 
-            long identity = Binder.clearCallingIdentity();
-            try {
-                if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
-                    try {
-                        listener.onLocationChanged(lastLocation);
-                        return true;
-                    } catch (RemoteException e) {
-                        Log.w(TAG, e);
-                        return false;
-                    }
+            if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
+                try {
+                    listener.onLocationChanged(lastLocation);
+                    return true;
+                } catch (RemoteException e) {
+                    Log.w(TAG, e);
+                    return false;
                 }
+            }
 
-                // packageName already validated by getLastLocation() call above
-                boolean foreground = LocationManagerServiceUtils.isImportanceForeground(
-                        mActivityManager.getPackageImportance(packageName));
-                if (!foreground) {
-                    if (locationAgeMs < mSettingsStore.getBackgroundThrottleIntervalMs()) {
-                        // not allowed to request new locations, so we can't return anything
-                        return false;
-                    }
+            if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) {
+                if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) {
+                    // not allowed to request new locations, so we can't return anything
+                    return false;
                 }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
 
@@ -2252,20 +2216,19 @@
             Log.w(TAG, "proximity alerts are currently available only to the primary user");
             return;
         }
+
+        mLocationUsageLogger.logLocationApiUsage(
+                LocationStatsEnums.USAGE_STARTED,
+                LocationStatsEnums.API_REQUEST_GEOFENCE,
+                packageName,
+                request,
+                /* hasListener= */ false,
+                true,
+                geofence,
+                mAppForegroundHelper.getImportance(uid));
+
         long identity = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                mLocationUsageLogger.logLocationApiUsage(
-                        LocationStatsEnums.USAGE_STARTED,
-                        LocationStatsEnums.API_REQUEST_GEOFENCE,
-                        packageName,
-                        request,
-                        /* hasListener= */ false,
-                        true,
-                        geofence,
-                        mActivityManager.getPackageImportance(packageName));
-            }
-
             mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
                     uid, packageName, featureId, listenerIdentifier);
         } finally {
@@ -2282,20 +2245,19 @@
 
         if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
 
+        mLocationUsageLogger.logLocationApiUsage(
+                LocationStatsEnums.USAGE_ENDED,
+                LocationStatsEnums.API_REQUEST_GEOFENCE,
+                packageName,
+                /* LocationRequest= */ null,
+                /* hasListener= */ false,
+                true,
+                geofence,
+                mAppForegroundHelper.getImportance(Binder.getCallingUid()));
+
         // geo-fence manager uses the public location API, need to clear identity
         long identity = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                mLocationUsageLogger.logLocationApiUsage(
-                        LocationStatsEnums.USAGE_ENDED,
-                        LocationStatsEnums.API_REQUEST_GEOFENCE,
-                        packageName,
-                        /* LocationRequest= */ null,
-                        /* hasListener= */ false,
-                        true,
-                        geofence,
-                        mActivityManager.getPackageImportance(packageName));
-            }
             mGeofenceManager.removeFence(geofence, intent);
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -2392,12 +2354,6 @@
     }
 
     @Override
-    public boolean sendNiResponse(int notifId, int userResponse) {
-        return mGnssManagerService != null && mGnssManagerService.sendNiResponse(notifId,
-                userResponse);
-    }
-
-    @Override
     public ProviderProperties getProviderProperties(String providerName) {
         LocationProviderManager manager = getLocationProviderManager(providerName);
         if (manager == null) {
@@ -2408,20 +2364,13 @@
 
     @Override
     public boolean isProviderPackage(String packageName) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
-                Manifest.permission.READ_DEVICE_CONFIG + " permission required");
-        for (LocationProviderManager manager : mProviderManagers) {
-            if (manager.getPackages().contains(packageName)) {
-                return true;
-            }
-        }
-        return false;
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, null);
+        return mLocalService.isProviderPackage(packageName);
     }
 
     @Override
     public List<String> getProviderPackages(String providerName) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
-                Manifest.permission.READ_DEVICE_CONFIG + " permission required");
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, null);
         LocationProviderManager manager = getLocationProviderManager(providerName);
         return manager == null ? Collections.emptyList() : new ArrayList<>(manager.getPackages());
     }
@@ -2461,28 +2410,19 @@
 
     @Override
     public boolean isLocationEnabledForUser(int userId) {
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS,
-                    "Requires INTERACT_ACROSS_USERS permission");
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+                    null);
         }
 
-        long identity = Binder.clearCallingIdentity();
-        try {
-            return mSettingsStore.isLocationEnabled(userId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        return mSettingsHelper.isLocationEnabled(userId);
     }
 
     @Override
     public boolean isProviderEnabledForUser(String providerName, int userId) {
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
         if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS,
-                    "Requires INTERACT_ACROSS_USERS permission");
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+                    null);
         }
 
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
@@ -2593,12 +2533,12 @@
             Receiver receiver = r.mReceiver;
             boolean receiverDead = false;
 
-            if (!manager.isEnabled() && !isSettingsExemptLocked(r)) {
+            if (!manager.isEnabled() && !isSettingsExempt(r)) {
                 continue;
             }
 
             int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid);
-            if (!mUserInfoStore.isCurrentUserOrProfile(receiverUserId)
+            if (!mUserInfoHelper.isCurrentUserOrProfile(receiverUserId)
                     && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) {
                 if (D) {
                     Log.d(TAG, "skipping loc update for background user " + receiverUserId +
@@ -2607,7 +2547,7 @@
                 continue;
             }
 
-            if (mSettingsStore.isLocationPackageBlacklisted(receiverUserId,
+            if (mSettingsHelper.isLocationPackageBlacklisted(receiverUserId,
                     receiver.mCallerIdentity.mPackageName)) {
                 if (D) {
                     Log.d(TAG, "skipping loc update for blacklisted app: " +
@@ -2855,12 +2795,12 @@
 
             ipw.println("User Info:");
             ipw.increaseIndent();
-            mUserInfoStore.dump(fd, ipw, args);
+            mUserInfoHelper.dump(fd, ipw, args);
             ipw.decreaseIndent();
 
             ipw.println("Location Settings:");
             ipw.increaseIndent();
-            mSettingsStore.dump(fd, ipw, args);
+            mSettingsHelper.dump(fd, ipw, args);
             ipw.decreaseIndent();
 
             ipw.println("Battery Saver Location Mode: "
@@ -2960,5 +2900,22 @@
                 }
             }
         }
+
+        @Override
+        public boolean isProviderPackage(String packageName) {
+            for (LocationProviderManager manager : mProviderManagers) {
+                if (manager.getPackages().contains(packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void sendNiResponse(int notifId, int userResponse) {
+            if (mGnssManagerService != null) {
+                mGnssManagerService.sendNiResponse(notifId, userResponse);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/LocationManagerServiceUtils.java b/services/core/java/com/android/server/LocationManagerServiceUtils.java
index 9c8ac19..372e91e 100644
--- a/services/core/java/com/android/server/LocationManagerServiceUtils.java
+++ b/services/core/java/com/android/server/LocationManagerServiceUtils.java
@@ -17,8 +17,6 @@
 package com.android.server;
 
 import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.content.Context;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -91,12 +89,8 @@
 
         /**
          * Link listener (i.e. callback) to a binder, so that it will be called upon binder's death.
-         *
-         * @param binder that calls listener upon death
-         * @return true if listener is successfully linked to binder, false otherwise
          */
-        public boolean linkToListenerDeathNotificationLocked(
-                IBinder binder) {
+        public boolean linkToListenerDeathNotificationLocked(IBinder binder) {
             try {
                 binder.linkToDeath(this, 0 /* flags */);
                 return true;
@@ -110,54 +104,13 @@
 
         /**
          * Unlink death listener (i.e. callback) from binder.
-         *
-         * @param binder that calls listener upon death
-         * @return true if binder is successfully unlinked from binder, false otherwise
          */
-        public boolean unlinkFromListenerDeathNotificationLocked(
-                IBinder binder) {
+        public void unlinkFromListenerDeathNotificationLocked(IBinder binder) {
             try {
                 binder.unlinkToDeath(this, 0 /* flags */);
-                return true;
             } catch (NoSuchElementException e) {
-                // if the death callback isn't connected (it should be...), log error,
-                // swallow the exception and return
                 Log.w(TAG, "Could not unlink " + mListenerName + " death callback.", e);
-                return false;
             }
         }
-
-    }
-
-    /**
-     * Convert boolean foreground into "foreground" or "background" string.
-     *
-     * @param foreground boolean indicating foreground
-     * @return "foreground" string if true, false otherwise
-     */
-    public static String foregroundAsString(boolean foreground) {
-        return foreground ? "foreground" : "background";
-    }
-
-
-    /**
-     * Classifies importance level as foreground or not.
-     *
-     * @param importance level as int
-     * @return boolean indicating if importance level is foreground or greater
-     */
-    public static boolean isImportanceForeground(int importance) {
-        return importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
-    }
-
-    /**
-     * Get package importance level.
-     *
-     * @param packageName package name
-     * @return package importance level as int
-     */
-    public static int getPackageImportance(String packageName, Context context) {
-        return ((ActivityManager) context.getSystemService(
-                Context.ACTIVITY_SERVICE)).getPackageImportance(packageName);
     }
 }
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 9b1326b..043f657 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -128,13 +128,15 @@
 
         @Override
         public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl,
-                Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
+                Bundle configOverrides, PendingIntent sentIntent, long messageId)
+                throws RemoteException {
             returnPendingIntentWithError(sentIntent);
         }
 
         @Override
         public void downloadMessage(int subId, String callingPkg, String locationUrl,
-                Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)
+                Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent,
+                long messageId)
                 throws RemoteException {
             returnPendingIntentWithError(downloadedIntent);
         }
@@ -329,7 +331,8 @@
 
         @Override
         public void sendMessage(int subId, String callingPkg, Uri contentUri,
-                String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
+                String locationUrl, Bundle configOverrides, PendingIntent sentIntent,
+                long messageId)
                 throws RemoteException {
             Slog.d(TAG, "sendMessage() by " + callingPkg);
             mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
@@ -343,13 +346,13 @@
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     subId);
             getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
-                    configOverrides, sentIntent);
+                    configOverrides, sentIntent, messageId);
         }
 
         @Override
         public void downloadMessage(int subId, String callingPkg, String locationUrl,
                 Uri contentUri, Bundle configOverrides,
-                PendingIntent downloadedIntent) throws RemoteException {
+                PendingIntent downloadedIntent, long messageId) throws RemoteException {
             Slog.d(TAG, "downloadMessage() by " + callingPkg);
             mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
                     "Download MMS message");
@@ -364,7 +367,7 @@
                     subId);
 
             getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
-                    configOverrides, downloadedIntent);
+                    configOverrides, downloadedIntent, messageId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 8451d6b..d720105 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -58,6 +58,8 @@
     private static final String KEY_CONTENT_PROVIDER_RETAIN_TIME = "content_provider_retain_time";
     private static final String KEY_GC_TIMEOUT = "gc_timeout";
     private static final String KEY_GC_MIN_INTERVAL = "gc_min_interval";
+    private static final String KEY_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS =
+            "force_bg_check_on_restricted";
     private static final String KEY_FULL_PSS_MIN_INTERVAL = "full_pss_min_interval";
     private static final String KEY_FULL_PSS_LOWERED_INTERVAL = "full_pss_lowered_interval";
     private static final String KEY_POWER_CHECK_INTERVAL = "power_check_interval";
@@ -96,6 +98,7 @@
     private static final long DEFAULT_GC_TIMEOUT = 5*1000;
     private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000;
     private static final long DEFAULT_FULL_PSS_MIN_INTERVAL = 20*60*1000;
+    private static final boolean DEFAULT_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS = true;
     private static final long DEFAULT_FULL_PSS_LOWERED_INTERVAL = 5*60*1000;
     private static final long DEFAULT_POWER_CHECK_INTERVAL = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
     private static final int DEFAULT_POWER_CHECK_MAX_CPU_1 = 25;
@@ -177,6 +180,14 @@
     // The minimum amount of time between successive GC requests for a process.
     long GC_MIN_INTERVAL = DEFAULT_GC_MIN_INTERVAL;
 
+    /**
+     * Whether or not Background Check should be forced on any apps in the
+     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket,
+     * regardless of target SDK version.
+     */
+    boolean FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS =
+            DEFAULT_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS;
+
     // The minimum amount of time between successive PSS requests for a process.
     long FULL_PSS_MIN_INTERVAL = DEFAULT_FULL_PSS_MIN_INTERVAL;
 
@@ -380,6 +391,9 @@
                             case KEY_IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES:
                                 updateImperceptibleKillExemptions();
                                 break;
+                            case KEY_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS:
+                                updateForceRestrictedBackgroundCheck();
+                                break;
                             default:
                                 break;
                         }
@@ -427,6 +441,7 @@
         updateMaxCachedProcesses();
         updateActivityStartsLoggingEnabled();
         updateBackgroundActivityStarts();
+        updateForceRestrictedBackgroundCheck();
         updateForegroundServiceStartsLoggingEnabled();
         updateBackgroundFgsStartsRestriction();
         updateOomAdjUpdatePolicy();
@@ -571,6 +586,13 @@
                 == OOMADJ_UPDATE_POLICY_QUICK;
     }
 
+    private void updateForceRestrictedBackgroundCheck() {
+        FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS,
+                DEFAULT_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS);
+    }
+
     private void updateImperceptibleKillExemptions() {
         IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear();
         IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
@@ -658,6 +680,8 @@
         pw.println(GC_TIMEOUT);
         pw.print("  "); pw.print(KEY_GC_MIN_INTERVAL); pw.print("=");
         pw.println(GC_MIN_INTERVAL);
+        pw.print("  "); pw.print(KEY_FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS); pw.print("=");
+        pw.println(FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS);
         pw.print("  "); pw.print(KEY_FULL_PSS_MIN_INTERVAL); pw.print("=");
         pw.println(FULL_PSS_MIN_INTERVAL);
         pw.print("  "); pw.print(KEY_FULL_PSS_LOWERED_INTERVAL); pw.print("=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4c5f705..2b160dd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -184,6 +184,7 @@
 import android.app.backup.IBackupManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.content.AutofillOptions;
@@ -6051,6 +6052,11 @@
         }
     }
 
+    private boolean isInRestrictedBucket(int userId, String packageName, long nowElapsed) {
+        return UsageStatsManager.STANDBY_BUCKET_RESTRICTED
+                <= mUsageStatsService.getAppStandbyBucket(packageName, userId, nowElapsed);
+    }
+
     // Unified app-op and target sdk check
     int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
         // Apps that target O+ are always subject to background check
@@ -6060,7 +6066,17 @@
             }
             return ActivityManager.APP_START_MODE_DELAYED_RIGID;
         }
-        // ...and legacy apps get an AppOp check
+        // It's a legacy app. If it's in the RESTRICTED bucket, always restrict on battery.
+        if (mOnBattery // Short-circuit in common case.
+                && mConstants.FORCE_BACKGROUND_CHECK_ON_RESTRICTED_APPS
+                && isInRestrictedBucket(
+                        UserHandle.getUserId(uid), packageName, SystemClock.elapsedRealtime())) {
+            if (DEBUG_BACKGROUND_CHECK) {
+                Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " in RESTRICTED bucket");
+            }
+            return ActivityManager.APP_START_MODE_DELAYED;
+        }
+        // Not in the RESTRICTED bucket so policy is based on AppOp check.
         int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
                 uid, packageName, null, false, "");
         if (DEBUG_BACKGROUND_CHECK) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 8dd3242..be5350f 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -60,12 +60,12 @@
 import android.text.TextUtils;
 import android.util.Pair;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
@@ -1142,17 +1142,17 @@
                         + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
                         + ", RequireConfirmation: "
                         + mCurrentAuthSession.mRequireConfirmation
-                        + ", State: " + StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
+                        + ", State: " + FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
                         + ", Latency: " + latency);
             }
 
-            StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
                     statsModality(),
                     mCurrentAuthSession.mUserId,
                     mCurrentAuthSession.isCrypto(),
                     BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                     mCurrentAuthSession.mRequireConfirmation,
-                    StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
+                    FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
                     latency,
                     mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
         } else {
@@ -1174,7 +1174,7 @@
                         + ", Latency: " + latency);
             }
             // Auth canceled
-            StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
                     statsModality(),
                     mCurrentAuthSession.mUserId,
                     mCurrentAuthSession.isCrypto(),
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 60f0e8e..687d935 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -51,10 +51,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
@@ -371,7 +371,7 @@
                         + identifier.getName());
                 mUtils.removeBiometricForUser(getContext(),
                         getTargetUserId(), identifier.getBiometricId());
-                StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                         statsModality(),
                         BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
             }
@@ -551,7 +551,7 @@
                     + " failed to respond to cancel, starting client "
                     + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
 
-            StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                     statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
 
             mCurrentClient = null;
@@ -673,8 +673,8 @@
                     0 /*vendorCode */);
         });
 
-        StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(),
-                BiometricsProtoEnums.ISSUE_HAL_DEATH);
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                statsModality(), BiometricsProtoEnums.ISSUE_HAL_DEATH);
     }
 
     protected ClientMonitor getCurrentClient() {
@@ -1249,7 +1249,7 @@
                     template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
                     restricted, getContext().getPackageName());
             removeInternal(client);
-            StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                     statsModality(),
                     BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
         } else {
diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
index ecf3864..c03c77f 100644
--- a/services/core/java/com/android/server/biometrics/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
@@ -21,7 +21,8 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.face.FaceManager;
 import android.util.Slog;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
 
 /**
  * Abstract class that adds logging functionality to the ClientMonitor classes.
@@ -86,7 +87,7 @@
                     + ", AcquiredInfo: " + acquiredInfo
                     + ", VendorCode: " + vendorCode);
         }
-        StatsLog.write(StatsLog.BIOMETRIC_ACQUIRED,
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
                 statsModality(),
                 targetUserId,
                 isCryptoOperation(),
@@ -114,7 +115,7 @@
         } else {
             Slog.v(TAG, "Error latency: " + latency);
         }
-        StatsLog.write(StatsLog.BIOMETRIC_ERROR_OCCURRED,
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
                 statsModality(),
                 targetUserId,
                 isCryptoOperation(),
@@ -128,15 +129,15 @@
 
     protected final void logOnAuthenticated(Context context, boolean authenticated,
             boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) {
-        int authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
+        int authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__UNKNOWN;
         if (!authenticated) {
-            authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
+            authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__REJECTED;
         } else {
             // Authenticated
             if (isBiometricPrompt && requireConfirmation) {
-                authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__PENDING_CONFIRMATION;
+                authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__PENDING_CONFIRMATION;
             } else {
-                authState = StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED;
+                authState = FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED;
             }
         }
 
@@ -157,7 +158,7 @@
             Slog.v(TAG, "Authentication latency: " + latency);
         }
 
-        StatsLog.write(StatsLog.BIOMETRIC_AUTHENTICATED,
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
                 statsModality(),
                 targetUserId,
                 isCryptoOperation(),
@@ -179,7 +180,7 @@
             Slog.v(TAG, "Enroll latency: " + latency);
         }
 
-        StatsLog.write(StatsLog.BIOMETRIC_ENROLLED,
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
                 statsModality(),
                 targetUserId,
                 latency,
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 4d5af9a..47bb5f3 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -30,7 +30,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.AndroidBuildClassifier;
@@ -40,6 +39,7 @@
 import com.android.internal.compat.IOverrideValidator;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 
 import java.io.FileDescriptor;
@@ -59,7 +59,7 @@
     public PlatformCompat(Context context) {
         mContext = context;
         mChangeReporter = new ChangeReporter(
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+                FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
         mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
     }
 
@@ -67,7 +67,7 @@
     PlatformCompat(Context context, CompatConfig compatConfig) {
         mContext = context;
         mChangeReporter = new ChangeReporter(
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+                FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
         mCompatConfig = compatConfig;
     }
 
@@ -75,7 +75,7 @@
     public void reportChange(long changeId, ApplicationInfo appInfo) {
         checkCompatChangeLogPermission();
         reportChange(changeId, appInfo.uid,
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+                FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
     }
 
     @Override
@@ -91,7 +91,8 @@
     @Override
     public void reportChangeByUid(long changeId, int uid) {
         checkCompatChangeLogPermission();
-        reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+        reportChange(changeId, uid,
+                FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
     }
 
     @Override
@@ -99,11 +100,11 @@
         checkCompatChangeReadPermission();
         if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
             reportChange(changeId, appInfo.uid,
-                    StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
+                    FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
             return true;
         }
         reportChange(changeId, appInfo.uid,
-                StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
+                FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
         return false;
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 9c42152..9f76e1e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -34,6 +34,16 @@
  */
 public abstract class InputMethodManagerInternal {
     /**
+     * Listener for input method list changed events.
+     */
+    public interface InputMethodListListener {
+        /**
+         * Called with the list of the installed IMEs when it's updated.
+         */
+        void onInputMethodListUpdated(List<InputMethodInfo> info, @UserIdInt int userId);
+    }
+
+    /**
      * Called by the power manager to tell the input method manager whether it
      * should start watching for wake events.
      */
@@ -85,6 +95,11 @@
     public abstract boolean switchToInputMethod(String imeId, @UserIdInt int userId);
 
     /**
+     * Registers a new {@link InputMethodListListener}.
+     */
+    public abstract void registerInputMethodListListener(InputMethodListListener listener);
+
+    /**
      * Fake implementation of {@link InputMethodManagerInternal}.  All the methods do nothing.
      */
     private static final InputMethodManagerInternal NOP =
@@ -117,6 +132,10 @@
                 public boolean switchToInputMethod(String imeId, int userId) {
                     return false;
                 }
+
+                @Override
+                public void registerInputMethodListListener(InputMethodListListener listener) {
+                }
             };
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 15698e9..47622f3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -154,6 +154,7 @@
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
 import com.android.server.statusbar.StatusBarManagerService;
@@ -172,6 +173,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -214,6 +216,7 @@
     static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
 
     static final int MSG_SYSTEM_UNLOCK_USER = 5000;
+    static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
 
     static final int MSG_INLINE_SUGGESTIONS_REQUEST = 6000;
 
@@ -689,6 +692,13 @@
     private final String mSlotIme;
 
     /**
+     * Registered {@link InputMethodListListeners}.
+     * This variable can be accessed from both of MainThread and BinderThread.
+     */
+    private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners =
+            new CopyOnWriteArrayList<>();
+
+    /**
      * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
      * internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
      * will not affect those tasks that are already posted.
@@ -3946,10 +3956,18 @@
             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
                 mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
                 return true;
-            case MSG_SYSTEM_UNLOCK_USER:
+            case MSG_SYSTEM_UNLOCK_USER: {
                 final int userId = msg.arg1;
                 onUnlockUser(userId);
                 return true;
+            }
+            case MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED: {
+                final int userId = msg.arg1;
+                final List<InputMethodInfo> imes = (List<InputMethodInfo>) msg.obj;
+                mInputMethodListListeners.forEach(
+                        listener -> listener.onInputMethodListUpdated(imes, userId));
+                return true;
+            }
 
             // ---------------------------------------------------------------
             case MSG_INLINE_SUGGESTIONS_REQUEST:
@@ -4142,6 +4160,11 @@
         // TODO: Make sure that mSwitchingController and mSettings are sharing the
         // the same enabled IMEs list.
         mSwitchingController.resetCircularListLocked(mContext);
+
+        // Notify InputMethodListListeners of the new installed InputMethods.
+        final List<InputMethodInfo> inputMethodList = new ArrayList<>(mMethodList);
+        mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
+                mSettings.getCurrentUserId(), 0 /* unused */, inputMethodList).sendToTarget();
     }
 
     // ----------------------------------------------------------------------
@@ -4606,6 +4629,11 @@
         public boolean switchToInputMethod(String imeId, int userId) {
             return mService.switchToInputMethod(imeId, userId);
         }
+
+        @Override
+        public void registerInputMethodListListener(InputMethodListListener listener) {
+            mService.mInputMethodListListeners.addIfAbsent(listener);
+        }
     }
 
     @BinderThread
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index d09c478..54af694 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -207,6 +207,12 @@
                             reportNotSupported();
                             return false;
                         }
+
+                        @Override
+                        public void registerInputMethodListListener(
+                                InputMethodListListener listener) {
+                            reportNotSupported();
+                        }
                     });
         }
 
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 5afa48a..64bca78 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -21,6 +21,7 @@
 import android.location.Location;
 import android.os.Binder;
 import android.os.Bundle;
+import android.util.ArraySet;
 
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -120,7 +121,8 @@
             if (providerPackageNames.equals(this.providerPackageNames)) {
                 return this;
             } else {
-                return new State(allowed, properties, providerPackageNames);
+                return new State(allowed, properties,
+                        Collections.unmodifiableSet(new ArraySet<>(providerPackageNames)));
             }
         }
 
diff --git a/services/core/java/com/android/server/location/AppForegroundHelper.java b/services/core/java/com/android/server/location/AppForegroundHelper.java
new file mode 100644
index 0000000..8ddfc65
--- /dev/null
+++ b/services/core/java/com/android/server/location/AppForegroundHelper.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.RunningAppProcessInfo.Importance;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Binder;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for all application foreground status. An application is
+ * considered foreground if it's uid's importance level is at or more important than
+ * {@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_FOREGROUND_SERVICE}.
+ */
+public class AppForegroundHelper {
+
+    /**
+     * Listener for application foreground state changes.
+     */
+    public interface AppForegroundListener {
+        /**
+         * Called when an application's foreground state changes.
+         */
+        void onAppForegroundChanged(int uid, boolean foreground);
+    }
+
+    // importance constants decrement with increasing importance - this is our limit for an
+    // importance level we consider foreground.
+    private static final int FOREGROUND_IMPORTANCE_CUTOFF = IMPORTANCE_FOREGROUND_SERVICE;
+
+    private static boolean isForeground(@Importance int importance) {
+        return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
+    }
+
+    private final Context mContext;
+    private final CopyOnWriteArrayList<AppForegroundListener> mListeners;
+
+    @GuardedBy("this")
+    @Nullable private ActivityManager mActivityManager;
+
+    public AppForegroundHelper(Context context) {
+        mContext = context;
+        mListeners = new CopyOnWriteArrayList<>();
+    }
+
+    /** Called when system is ready. */
+    public synchronized void onSystemReady() {
+        if (mActivityManager != null) {
+            return;
+        }
+
+        mActivityManager = Objects.requireNonNull(mContext.getSystemService(ActivityManager.class));
+        mActivityManager.addOnUidImportanceListener(this::onAppForegroundChanged,
+                FOREGROUND_IMPORTANCE_CUTOFF);
+    }
+
+    /**
+     * Adds a listener for app foreground changed events. Callbacks occur on an unspecified thread.
+     */
+    public void addListener(AppForegroundListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener for app foreground changed events.
+     */
+    public void removeListener(AppForegroundListener listener) {
+        mListeners.remove(listener);
+    }
+
+    private void onAppForegroundChanged(int uid, @Importance int importance) {
+        // invoked on ui thread, move to fg thread so we don't block the ui thread
+        boolean foreground = isForeground(importance);
+        FgThread.getHandler().post(() -> {
+            for (AppForegroundListener listener : mListeners) {
+                listener.onAppForegroundChanged(uid, foreground);
+            }
+        });
+    }
+
+    /**
+     * Whether the given uid is currently foreground.
+     */
+    public boolean isAppForeground(int uid) {
+        return isForeground(getImportance(uid));
+    }
+
+    /**
+     * Retrieves the current importance of the given uid.
+     *
+     * @deprecated Prefer {@link #isAppForeground(int)}.
+     */
+    @Deprecated
+    @Importance
+    public int getImportance(int uid) {
+        synchronized (this) {
+            Preconditions.checkState(mActivityManager != null);
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return mActivityManager.getUidImportance(uid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index 81c06d7..4e9c067 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -77,7 +77,7 @@
     private final AppOpsManager mAppOps;
     private final PowerManager.WakeLock mWakeLock;
 
-    private final LocationSettingsStore mSettingsStore;
+    private final SettingsHelper mSettingsStore;
 
     private final Object mLock = new Object();
 
@@ -111,7 +111,7 @@
      */
     private boolean mPendingUpdate;
 
-    public GeofenceManager(Context context, LocationSettingsStore settingsStore) {
+    public GeofenceManager(Context context, SettingsHelper settingsStore) {
         mContext = context;
         mHandler = new GeofenceHandler(FgThread.getHandler().getLooper());
 
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index 86a84e3..b3546dc 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -23,7 +23,8 @@
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
 
 import libcore.io.IoUtils;
 
@@ -283,7 +284,7 @@
     }
 
     private void logConfigurations() {
-        StatsLog.write(StatsLog.GNSS_CONFIGURATION_REPORTED,
+        FrameworkStatsLog.write(FrameworkStatsLog.GNSS_CONFIGURATION_REPORTED,
                 getSuplHost(),
                 getSuplPort(0),
                 getC2KHost(),
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 306e1e3..a7fd57a 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -43,7 +43,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
@@ -64,7 +63,6 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.StatsLog;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
@@ -75,7 +73,9 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.location.gnssmetrics.GnssMetrics;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.DeviceIdleInternal;
+import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
 import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
@@ -624,12 +624,12 @@
         }
     }
 
-    public GnssLocationProvider(Context context, Handler handler) {
-        super(context, new HandlerExecutor(handler));
+    public GnssLocationProvider(Context context) {
+        super(context, FgThread.getExecutor());
 
         ensureInitialized();
 
-        mLooper = handler.getLooper();
+        mLooper = FgThread.getHandler().getLooper();
 
         // Create a wake lock
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -1825,8 +1825,8 @@
             }
             native_send_ni_response(notificationId, userResponse);
 
-            StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
-                    StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE,
+            FrameworkStatsLog.write(FrameworkStatsLog.GNSS_NI_EVENT_REPORTED,
+                    FrameworkStatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE,
                     notificationId,
                     /* niType= */ 0,
                     /* needNotify= */ false,
@@ -1891,8 +1891,8 @@
         notification.textEncoding = textEncoding;
 
         mNIHandler.handleNiNotification(notification);
-        StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
-                StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_REQUEST,
+        FrameworkStatsLog.write(FrameworkStatsLog.GNSS_NI_EVENT_REPORTED,
+                FrameworkStatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_REQUEST,
                 notification.notificationId,
                 notification.niType,
                 notification.needNotify,
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index dd522b9..2b5fc79 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -36,11 +36,11 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.StatsLog;
 
 import com.android.internal.R;
 import com.android.internal.location.GpsNetInitiatedHandler;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.util.Arrays;
 import java.util.List;
@@ -650,7 +650,7 @@
     }
 
     private void logEvent(NfwNotification notification, boolean isPermissionMismatched) {
-        StatsLog.write(StatsLog.GNSS_NFW_NOTIFICATION_REPORTED,
+        FrameworkStatsLog.write(FrameworkStatsLog.GNSS_NFW_NOTIFICATION_REPORTED,
                 notification.mProxyAppPackageName,
                 notification.mProtocolStack,
                 notification.mOtherProtocolStackName,
diff --git a/services/core/java/com/android/server/location/LocationUsageLogger.java b/services/core/java/com/android/server/location/LocationUsageLogger.java
index 755438b..93e19df 100644
--- a/services/core/java/com/android/server/location/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/LocationUsageLogger.java
@@ -24,9 +24,9 @@
 import android.location.LocationRequest;
 import android.stats.location.LocationStatsEnums;
 import android.util.Log;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.time.Instant;
 
@@ -61,8 +61,8 @@
             boolean isLocationRequestNull = locationRequest == null;
             boolean isGeofenceNull = geofence == null;
 
-            StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType,
-                    apiInUse, packageName,
+            FrameworkStatsLog.write(FrameworkStatsLog.LOCATION_MANAGER_API_USAGE_REPORTED,
+                    usageType, apiInUse, packageName,
                     isLocationRequestNull
                         ? LocationStatsEnums.PROVIDER_UNKNOWN
                         : bucketizeProvider(locationRequest.getProvider()),
@@ -101,7 +101,8 @@
                 return;
             }
 
-            StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType, apiInUse,
+            FrameworkStatsLog.write(FrameworkStatsLog.LOCATION_MANAGER_API_USAGE_REPORTED,
+                    usageType, apiInUse,
                     /* package_name= */ null,
                     bucketizeProvider(providerName),
                     LocationStatsEnums.QUALITY_UNKNOWN,
diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/SettingsHelper.java
similarity index 88%
rename from services/core/java/com/android/server/location/LocationSettingsStore.java
rename to services/core/java/com/android/server/location/SettingsHelper.java
index 0e8720e..9163490 100644
--- a/services/core/java/com/android/server/location/LocationSettingsStore.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -54,7 +54,7 @@
 /**
  * Provides accessors and listeners for all location related settings.
  */
-public class LocationSettingsStore {
+public class SettingsHelper {
 
     /**
      * Listener for user-specific settings changes.
@@ -99,7 +99,7 @@
     private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
 
     // TODO: get rid of handler
-    public LocationSettingsStore(Context context, Handler handler) {
+    public SettingsHelper(Context context, Handler handler) {
         mContext = context;
 
         mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
@@ -118,7 +118,7 @@
     }
 
     /** Called when system is ready. */
-    public synchronized void onSystemReady() {
+    public void onSystemReady() {
         mLocationMode.register();
         mBackgroundThrottleIntervalMs.register();
         mLocationPackageBlacklist.register();
@@ -135,7 +135,8 @@
     }
 
     /**
-     * Add a listener for changes to the location enabled setting.
+     * Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified
+     * thread.
      */
     public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
         mLocationMode.addListener(listener);
@@ -156,7 +157,8 @@
     }
 
     /**
-     * Add a listener for changes to the background throttle interval.
+     * Add a listener for changes to the background throttle interval. Callbacks occur on an
+     * unspecified thread.
      */
     public void addOnBackgroundThrottleIntervalChangedListener(
             GlobalSettingChangedListener listener) {
@@ -204,7 +206,8 @@
     }
 
     /**
-     * Add a listener for changes to the background throttle package whitelist.
+     * Add a listener for changes to the background throttle package whitelist. Callbacks occur on
+     * an unspecified thread.
      */
     public void addOnBackgroundThrottlePackageWhitelistChangedListener(
             GlobalSettingChangedListener listener) {
@@ -227,7 +230,8 @@
     }
 
     /**
-     * Add a listener for changes to the ignore settings package whitelist.
+     * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an
+     * unspecified thread.
      */
     public void addOnIgnoreSettingsPackageWhitelistChangedListener(
             GlobalSettingChangedListener listener) {
@@ -340,6 +344,8 @@
     private abstract static class ObservingSetting extends ContentObserver {
 
         private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners;
+
+        @GuardedBy("this")
         private boolean mRegistered;
 
         private ObservingSetting(Handler handler) {
@@ -347,11 +353,11 @@
             mListeners = new CopyOnWriteArrayList<>();
         }
 
-        protected boolean isRegistered() {
+        protected synchronized boolean isRegistered() {
             return mRegistered;
         }
 
-        protected void register(Context context, Uri uri) {
+        protected synchronized void register(Context context, Uri uri) {
             if (mRegistered) {
                 return;
             }
@@ -393,8 +399,13 @@
         }
 
         public int getValueForUser(int defaultValue, int userId) {
-            return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
-                    defaultValue, userId);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
+                        defaultValue, userId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -417,7 +428,7 @@
             mCachedUserId = UserHandle.USER_NULL;
         }
 
-        public synchronized void register() {
+        public void register() {
             register(mContext, Settings.Secure.getUriFor(mSettingName));
         }
 
@@ -426,12 +437,17 @@
 
             List<String> value = mCachedValue;
             if (userId != mCachedUserId) {
-                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                        mSettingName, userId);
-                if (TextUtils.isEmpty(setting)) {
-                    value = Collections.emptyList();
-                } else {
-                    value = Arrays.asList(setting.split(","));
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                            mSettingName, userId);
+                    if (TextUtils.isEmpty(setting)) {
+                        value = Collections.emptyList();
+                    } else {
+                        value = Arrays.asList(setting.split(","));
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
                 }
 
                 if (isRegistered()) {
@@ -473,8 +489,13 @@
         }
 
         public long getValue(long defaultValue) {
-            return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
-                    defaultValue);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
+                        defaultValue);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -499,18 +520,23 @@
             mValid = false;
         }
 
-        public synchronized void register() {
+        public void register() {
             register(mContext, Settings.Global.getUriFor(mSettingName));
         }
 
         public synchronized Set<String> getValue() {
             ArraySet<String> value = mCachedValue;
             if (!mValid) {
-                value = new ArraySet<>(mBaseValuesSupplier.get());
-                String setting = Settings.Global.getString(mContext.getContentResolver(),
-                        mSettingName);
-                if (!TextUtils.isEmpty(setting)) {
-                    value.addAll(Arrays.asList(setting.split(",")));
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    value = new ArraySet<>(mBaseValuesSupplier.get());
+                    String setting = Settings.Global.getString(mContext.getContentResolver(),
+                            mSettingName);
+                    if (!TextUtils.isEmpty(setting)) {
+                        value.addAll(Arrays.asList(setting.split(",")));
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
                 }
 
                 if (isRegistered()) {
diff --git a/services/core/java/com/android/server/location/UserInfoStore.java b/services/core/java/com/android/server/location/UserInfoHelper.java
similarity index 70%
rename from services/core/java/com/android/server/location/UserInfoStore.java
rename to services/core/java/com/android/server/location/UserInfoHelper.java
index f282ed2..94f3a88 100644
--- a/services/core/java/com/android/server/location/UserInfoStore.java
+++ b/services/core/java/com/android/server/location/UserInfoHelper.java
@@ -42,7 +42,7 @@
 /**
  * Provides accessors and listeners for all user info.
  */
-public class UserInfoStore {
+public class UserInfoHelper {
 
     /**
      * Listener for current user changes.
@@ -58,20 +58,16 @@
     private final CopyOnWriteArrayList<UserChangedListener> mListeners;
 
     @GuardedBy("this")
-    @Nullable
-    private UserManager mUserManager;
+    @Nullable private UserManager mUserManager;
+
+    @UserIdInt private volatile int mCurrentUserId;
 
     @GuardedBy("this")
-    @UserIdInt
-    private int mCurrentUserId;
-
-    @GuardedBy("this")
-    @UserIdInt
-    private int mCachedParentUserId;
+    @UserIdInt private int mCachedParentUserId;
     @GuardedBy("this")
     private int[] mCachedProfileUserIds;
 
-    public UserInfoStore(Context context) {
+    public UserInfoHelper(Context context) {
         mContext = context;
         mListeners = new CopyOnWriteArrayList<>();
 
@@ -120,7 +116,7 @@
     }
 
     /**
-     * Adds a listener for user changed events.
+     * Adds a listener for user changed events. Callbacks occur on an unspecified thread.
      */
     public void addListener(UserChangedListener listener) {
         mListeners.add(listener);
@@ -134,16 +130,13 @@
     }
 
     private void onUserChanged(@UserIdInt int newUserId) {
-        int oldUserId;
-        synchronized (this) {
-            if (newUserId == mCurrentUserId) {
-                return;
-            }
-
-            oldUserId = mCurrentUserId;
-            mCurrentUserId = newUserId;
+        if (newUserId == mCurrentUserId) {
+            return;
         }
 
+        int oldUserId = mCurrentUserId;
+        mCurrentUserId = newUserId;
+
         for (UserChangedListener listener : mListeners) {
             listener.onUserChanged(oldUserId, newUserId);
         }
@@ -161,7 +154,7 @@
      * Returns the user id of the current user.
      */
     @UserIdInt
-    public synchronized int getCurrentUserId() {
+    public int getCurrentUserId() {
         return mCurrentUserId;
     }
 
@@ -169,9 +162,10 @@
      * Returns true if the given user id is either the current user or a profile of the current
      * user.
      */
-    public synchronized boolean isCurrentUserOrProfile(@UserIdInt int userId) {
-        return userId == mCurrentUserId || ArrayUtils.contains(
-                getProfileUserIdsForParentUser(mCurrentUserId), userId);
+    public boolean isCurrentUserOrProfile(@UserIdInt int userId) {
+        int currentUserId = mCurrentUserId;
+        return userId == currentUserId || ArrayUtils.contains(
+                getProfileUserIdsForParentUser(currentUserId), userId);
     }
 
     /**
@@ -179,50 +173,44 @@
      * is a parent or has no profiles.
      */
     @UserIdInt
-    public synchronized int getParentUserId(@UserIdInt int userId) {
-        int parentUserId;
-        if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, userId)) {
-            parentUserId = mCachedParentUserId;
-        } else {
-            Preconditions.checkState(mUserManager != null);
-
-            long identity = Binder.clearCallingIdentity();
-            try {
-                UserInfo userInfo = mUserManager.getProfileParent(userId);
-                if (userInfo != null) {
-                    parentUserId = userInfo.id;
-                } else {
-                    // getProfileParent() returns null if the userId is already the parent...
-                    parentUserId = userId;
-                }
-
-                // force profiles into cache
-                getProfileUserIdsForParentUser(parentUserId);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+    public int getParentUserId(@UserIdInt int userId) {
+        synchronized (this) {
+            if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds,
+                    userId)) {
+                return mCachedParentUserId;
             }
+
+            Preconditions.checkState(mUserManager != null);
         }
 
+        int parentUserId;
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            UserInfo userInfo = mUserManager.getProfileParent(userId);
+            parentUserId = userInfo != null ? userInfo.id : userId;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        // force profiles into cache
+        getProfileUserIdsForParentUser(parentUserId);
         return parentUserId;
     }
 
     @GuardedBy("this")
-    private int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
-        Preconditions.checkState(mUserManager != null);
-
-        // only assert on debug builds as this is a more expensive check
-        if (Build.IS_DEBUGGABLE) {
-            long identity = Binder.clearCallingIdentity();
-            try {
-                Preconditions.checkArgument(mUserManager.getProfileParent(parentUserId) == null);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
+    private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
         if (parentUserId != mCachedParentUserId) {
             long identity = Binder.clearCallingIdentity();
             try {
+                Preconditions.checkState(mUserManager != null);
+
+                // more expensive check - check that argument really is a parent user id
+                if (Build.IS_DEBUGGABLE) {
+                    Preconditions.checkArgument(
+                            mUserManager.getProfileParent(parentUserId) == null);
+                }
+
                 mCachedParentUserId = parentUserId;
                 mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId);
             } finally {
@@ -236,8 +224,9 @@
     /**
      * Dump info for debugging.
      */
-    public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("Current User: " + mCurrentUserId + " " + Arrays.toString(
-                getProfileUserIdsForParentUser(mCurrentUserId)));
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        int currentUserId = mCurrentUserId;
+        pw.println("Current User: " + currentUserId + " " + Arrays.toString(
+                getProfileUserIdsForParentUser(currentUserId)));
     }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 1eb2c52..2bab9fa 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -16,10 +16,11 @@
 
 package com.android.server.location.gnss;
 
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.location.GnssCapabilities;
@@ -31,8 +32,8 @@
 import android.location.IGpsGeofenceHardware;
 import android.location.INetInitiatedListener;
 import android.location.Location;
+import android.location.LocationManagerInternal;
 import android.os.Binder;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Process;
@@ -43,13 +44,12 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.FgThread;
-import com.android.server.LocationManagerService;
-import com.android.server.LocationManagerServiceUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 import com.android.server.LocationManagerServiceUtils.LinkedListener;
 import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
+import com.android.server.location.AppForegroundHelper;
 import com.android.server.location.CallerIdentity;
 import com.android.server.location.GnssBatchingProvider;
 import com.android.server.location.GnssCapabilitiesProvider;
@@ -60,6 +60,7 @@
 import com.android.server.location.GnssStatusListenerHelper;
 import com.android.server.location.LocationUsageLogger;
 import com.android.server.location.RemoteListenerHelper;
+import com.android.server.location.SettingsHelper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -70,10 +71,18 @@
 
 /** Manages Gnss providers and related Gnss functions for LocationManagerService. */
 public class GnssManagerService {
-    private static final String TAG = "GnssManagerService";
-    private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
-    // Providers
+    private static final String TAG = "GnssManagerService";
+
+    public static boolean isGnssSupported() {
+        return GnssLocationProvider.isSupported();
+    }
+
+    private final Context mContext;
+    private final SettingsHelper mSettingsHelper;
+    private final AppForegroundHelper mAppForegroundHelper;
+    private final LocationUsageLogger mLocationUsageLogger;
+
     private final GnssLocationProvider mGnssLocationProvider;
     private final GnssStatusListenerHelper mGnssStatusProvider;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -83,58 +92,59 @@
     private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
     private final GnssCapabilitiesProvider mGnssCapabilitiesProvider;
     private final GnssBatchingProvider mGnssBatchingProvider;
-
     private final INetInitiatedListener mNetInitiatedListener;
     private final IGpsGeofenceHardware mGpsGeofenceProxy;
-    private final LocationManagerService mLocationManagerService;
-    private final LocationUsageLogger mLocationUsageLogger;
 
     @GuardedBy("mGnssMeasurementsListeners")
-    private final ArrayMap<IBinder,
-            LinkedListener<IGnssMeasurementsListener>>
+    private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>>
             mGnssMeasurementsListeners = new ArrayMap<>();
 
     @GuardedBy("mGnssNavigationMessageListeners")
-    private final ArrayMap<
-            IBinder, LinkedListener<IGnssNavigationMessageListener>>
+    private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>>
             mGnssNavigationMessageListeners = new ArrayMap<>();
 
     @GuardedBy("mGnssStatusListeners")
     private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
             mGnssStatusListeners = new ArrayMap<>();
 
-    @GuardedBy("mGnssBatchingLock")
-    private IBatchedLocationCallback mGnssBatchingCallback;
+    @GuardedBy("this")
+    @Nullable private LocationManagerInternal mLocationManagerInternal;
+    @GuardedBy("this")
+    @Nullable private AppOpsManager mAppOpsManager;
+
+    private final Object mGnssBatchingLock = new Object();
 
     @GuardedBy("mGnssBatchingLock")
-    private LinkedListener<IBatchedLocationCallback>
-            mGnssBatchingDeathCallback;
+    @Nullable private IBatchedLocationCallback mGnssBatchingCallback;
+
+    @GuardedBy("mGnssBatchingLock")
+    @Nullable private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback;
 
     @GuardedBy("mGnssBatchingLock")
     private boolean mGnssBatchingInProgress = false;
 
-    private final Object mGnssBatchingLock = new Object();
-    private final Context mContext;
-    private final Handler mHandler;
-
-    public GnssManagerService(LocationManagerService locationManagerService,
-            Context context, LocationUsageLogger locationUsageLogger) {
-        this(locationManagerService, context,
-                new GnssLocationProvider(context, FgThread.getHandler()), locationUsageLogger);
+    public GnssManagerService(Context context, SettingsHelper settingsHelper,
+            AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger) {
+        this(context, settingsHelper, appForegroundHelper, locationUsageLogger, null);
     }
 
     // Can use this constructor to inject GnssLocationProvider for testing
     @VisibleForTesting
-    public GnssManagerService(LocationManagerService locationManagerService,
-            Context context,
-            GnssLocationProvider gnssLocationProvider,
-            LocationUsageLogger locationUsageLogger) {
+    GnssManagerService(Context context, SettingsHelper settingsHelper,
+            AppForegroundHelper appForegroundHelper, LocationUsageLogger locationUsageLogger,
+            GnssLocationProvider gnssLocationProvider) {
+        Preconditions.checkState(isGnssSupported());
+
         mContext = context;
-        mHandler = FgThread.getHandler();
+        mSettingsHelper = settingsHelper;
+        mAppForegroundHelper = appForegroundHelper;
+        mLocationUsageLogger = locationUsageLogger;
 
-        mGnssLocationProvider =
-                gnssLocationProvider;
+        if (gnssLocationProvider == null) {
+            gnssLocationProvider = new GnssLocationProvider(mContext);
+        }
 
+        mGnssLocationProvider = gnssLocationProvider;
         mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider();
         mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider();
         mGnssMeasurementCorrectionsProvider =
@@ -144,108 +154,73 @@
         mGnssMetricsProvider = mGnssLocationProvider.getGnssMetricsProvider();
         mGnssCapabilitiesProvider = mGnssLocationProvider.getGnssCapabilitiesProvider();
         mGnssBatchingProvider = mGnssLocationProvider.getGnssBatchingProvider();
-
         mNetInitiatedListener = mGnssLocationProvider.getNetInitiatedListener();
         mGpsGeofenceProxy = mGnssLocationProvider.getGpsGeofenceProxy();
-        mLocationManagerService = locationManagerService;
-        mLocationUsageLogger = locationUsageLogger;
-
-        registerUidListener();
     }
 
-    public static boolean isGnssSupported() {
-        return GnssLocationProvider.isSupported();
-    }
-
-    private boolean hasGnssPermissions(String packageName) {
-        mContext.enforceCallingPermission(
-                Manifest.permission.ACCESS_FINE_LOCATION,
-                "Fine location permission not granted.");
-
-        int uid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
-        try {
-            return mContext.getSystemService(
-                    AppOpsManager.class).checkOp(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
-                    == AppOpsManager.MODE_ALLOWED;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+    /** Called when system is ready. */
+    public synchronized void onSystemReady() {
+        if (mLocationManagerInternal != null) {
+            return;
         }
+
+        mSettingsHelper.onSystemReady();
+        mAppForegroundHelper.onSystemReady();
+
+        mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
+        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+
+        mAppForegroundHelper.addListener(this::onAppForegroundChanged);
     }
 
+    /** Retrieve the GnssLocationProvider. */
     public GnssLocationProvider getGnssLocationProvider() {
         return mGnssLocationProvider;
     }
 
+    /** Retrieve the IGpsGeofenceHardware. */
     public IGpsGeofenceHardware getGpsGeofenceProxy() {
         return mGpsGeofenceProxy;
     }
 
     /**
      * Get year of GNSS hardware.
-     *
-     * @return year of GNSS hardware as an int if possible, otherwise zero
      */
     public int getGnssYearOfHardware() {
-        if (mGnssSystemInfoProvider != null) {
-            return mGnssSystemInfoProvider.getGnssYearOfHardware();
-        } else {
-            return 0;
-        }
+        return mGnssSystemInfoProvider.getGnssYearOfHardware();
     }
 
     /**
      * Get model name of GNSS hardware.
-     *
-     * @return GNSS hardware model name as a string if possible, otherwise null
      */
+    @Nullable
     public String getGnssHardwareModelName() {
-        if (mGnssSystemInfoProvider != null) {
-            return mGnssSystemInfoProvider.getGnssHardwareModelName();
-        } else {
-            return null;
-        }
+        return mGnssSystemInfoProvider.getGnssHardwareModelName();
     }
 
     /**
-     * Get GNSS hardware capabilities. The capabilities are described in {@link
-     * android.location.GnssCapabilities} and their integer values correspond to the
-     * bit positions in the returned {@code long} value.
-     *
-     * @param packageName name of requesting package
-     * @return capabilities supported by the GNSS chipset
+     * Get GNSS hardware capabilities. The capabilities returned are a bitfield as described in
+     * {@link android.location.GnssCapabilities}.
      */
     public long getGnssCapabilities(String packageName) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to obtain GNSS chipset capabilities.");
-        if (!hasGnssPermissions(packageName) || mGnssCapabilitiesProvider == null) {
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
+
+        if (!checkLocationAppOp(packageName)) {
             return GnssCapabilities.INVALID_CAPABILITIES;
         }
+
         return mGnssCapabilitiesProvider.getGnssCapabilities();
     }
 
     /**
      * Get size of GNSS batch (GNSS location results are batched together for power savings).
-     * Requires LOCATION_HARDWARE and GNSS permissions.
-     *
-     * @param packageName name of requesting package
-     * @return size of the GNSS batch collection
      */
     public int getGnssBatchSize(String packageName) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to access hardware batching");
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
-        if (!hasGnssPermissions(packageName)) {
-            Log.e(TAG, "getGnssBatchSize called without GNSS permissions");
-            return 0;
-        }
-        if (mGnssBatchingProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not get GNSS batch size. GNSS batching provider "
-                            + "not available.");
+        if (!checkLocationAppOp(packageName)) {
             return 0;
         }
 
@@ -257,27 +232,12 @@
     /**
      * Starts GNSS batch collection. GNSS positions are collected in a batch before being delivered
      * as a collection.
-     *
-     * @param periodNanos    duration over which to collect GPS positions before delivering as a
-     *                       batch
-     * @param wakeOnFifoFull specifying whether to wake on full queue
-     * @param packageName    name of requesting package
-     * @return true of batch started successfully, false otherwise
      */
     public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to access hardware batching");
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
-        if (!hasGnssPermissions(packageName)) {
-            Log.e(TAG, "startGnssBatch called without GNSS permissions");
-            return false;
-        }
-        if (mGnssBatchingProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not start GNSS batching. GNSS batching provider "
-                            + "not available.");
+        if (!checkLocationAppOp(packageName)) {
             return false;
         }
 
@@ -285,7 +245,6 @@
             if (mGnssBatchingInProgress) {
                 // Current design does not expect multiple starts to be called repeatedly
                 Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
-                // Try to clean up anyway, and continue
                 stopGnssBatch();
             }
 
@@ -296,26 +255,13 @@
 
     /**
      * Adds a GNSS batching callback for delivering GNSS location batch results.
-     *
-     * @param callback    called when batching operation is complete to deliver GPS positions
-     * @param packageName name of requesting package
-     * @return true if callback is successfully added, false otherwise
      */
     public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
             @Nullable String featureId, @NonNull String listenerIdentity) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to access hardware batching");
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
-        if (!hasGnssPermissions(packageName)) {
-            Log.e(TAG, "addGnssBatchingCallback called without GNSS permissions");
-            return false;
-        }
-        if (mGnssBatchingProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not add GNSS batching callback. GNSS batching provider "
-                            + "not available.");
+        if (!checkLocationAppOp(packageName)) {
             return false;
         }
 
@@ -333,11 +279,9 @@
                                 stopGnssBatch();
                                 removeGnssBatchingCallback();
                             });
-            if (!mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked(
-                    callback.asBinder())) {
-                return false;
-            }
-            return true;
+
+            return mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked(
+                    callback.asBinder());
         }
     }
 
@@ -347,27 +291,14 @@
      * @param packageName name of requesting package
      */
     public void flushGnssBatch(String packageName) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to access hardware batching");
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
-        if (!hasGnssPermissions(packageName)) {
-            Log.e(TAG, "flushGnssBatch called without GNSS permissions");
-            return;
-        }
-
-        if (mGnssBatchingProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not flush GNSS batch. GNSS batching provider "
-                            + "not available.");
+        if (!checkLocationAppOp(packageName)) {
             return;
         }
 
         synchronized (mGnssBatchingLock) {
-            if (!mGnssBatchingInProgress) {
-                Log.w(TAG, "flushGnssBatch called with no batch in progress");
-            }
             mGnssBatchingProvider.flush();
         }
     }
@@ -376,17 +307,7 @@
      * Removes GNSS batching callback.
      */
     public void removeGnssBatchingCallback() {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to access hardware batching");
-
-        if (mGnssBatchingProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not add GNSS batching callback. GNSS batching provider "
-                            + "not available.");
-            return;
-        }
+        mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
 
         synchronized (mGnssBatchingLock) {
             mGnssBatchingDeathCallback.unlinkFromListenerDeathNotificationLocked(
@@ -398,44 +319,17 @@
 
     /**
      * Stop GNSS batch collection.
-     *
-     * @return true if GNSS batch successfully stopped, false otherwise
      */
     public boolean stopGnssBatch() {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to access hardware batching");
+        mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
 
-        if (mGnssBatchingProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not stop GNSS batch. GNSS batching provider "
-                            + "not available.");
-            return false;
-        }
         synchronized (mGnssBatchingLock) {
             mGnssBatchingInProgress = false;
             return mGnssBatchingProvider.stop();
         }
     }
 
-    private void registerUidListener() {
-        mContext.getSystemService(
-                ActivityManager.class).addOnUidImportanceListener(
-                    (uid, importance) -> {
-                        // listener invoked on ui thread, move to our thread to reduce risk
-                        // of blocking ui thread
-                        mHandler.post(
-                                () -> {
-                                    onForegroundChanged(uid,
-                                            LocationManagerServiceUtils.isImportanceForeground(
-                                                    importance));
-                                });
-                    },
-                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
-    }
-
-    private void onForegroundChanged(int uid, boolean foreground) {
+    private void onAppForegroundChanged(int uid, boolean foreground) {
         synchronized (mGnssMeasurementsListeners) {
             updateListenersOnForegroundChangedLocked(
                     mGnssMeasurementsListeners,
@@ -463,8 +357,7 @@
     }
 
     private <TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
-            ArrayMap<IBinder, ? extends LinkedListenerBase>
-                    gnssDataListeners,
+            Map<IBinder, ? extends LinkedListenerBase> gnssDataListeners,
             RemoteListenerHelper<TListener> gnssDataProvider,
             Function<IBinder, TListener> mapBinderToListener,
             int uid,
@@ -477,18 +370,8 @@
                 continue;
             }
 
-            if (D) {
-                Log.d(
-                        TAG,
-                        linkedListener.getListenerName()
-                                + " from uid "
-                                + uid
-                                + " is now "
-                                + LocationManagerServiceUtils.foregroundAsString(foreground));
-            }
-
             TListener listener = mapBinderToListener.apply(entry.getKey());
-            if (foreground || mLocationManagerService.isThrottlingExemptLocked(callerIdentity)) {
+            if (foreground || isThrottlingExempt(callerIdentity)) {
                 gnssDataProvider.addListener(listener, callerIdentity);
             } else {
                 gnssDataProvider.removeListener(listener);
@@ -502,67 +385,49 @@
             @Nullable String featureId,
             @NonNull String listenerIdentifier,
             RemoteListenerHelper<TListener> gnssDataProvider,
-            ArrayMap<IBinder,
-                    LinkedListener<TListener>> gnssDataListeners,
+            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
             Consumer<TListener> binderDeathCallback) {
-        if (!hasGnssPermissions(packageName)) {
-            Log.e(TAG, "addGnssDataListenerLocked called without GNSS permissions");
+        mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
+
+        if (!checkLocationAppOp(packageName)) {
             return false;
         }
 
-        if (gnssDataProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not add GNSS data listener. GNSS data provider "
-                            + "not available.");
-            return false;
-        }
-
-        CallerIdentity callerIdentity =
-                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
-                        featureId, listenerIdentifier);
-        LinkedListener<TListener> linkedListener =
-                new LocationManagerServiceUtils.LinkedListener<>(
-                        listener, listenerIdentifier, callerIdentity, binderDeathCallback);
+        CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
+                Binder.getCallingPid(), packageName, featureId, listenerIdentifier);
+        LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
+                listenerIdentifier, callerIdentity, binderDeathCallback);
         IBinder binder = listener.asBinder();
         if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
             return false;
         }
 
         gnssDataListeners.put(binder, linkedListener);
-        long identity = Binder.clearCallingIdentity();
-        try {
-            if (gnssDataProvider == mGnssMeasurementsProvider
-                    || gnssDataProvider == mGnssStatusProvider) {
-                mLocationUsageLogger.logLocationApiUsage(
-                        LocationStatsEnums.USAGE_STARTED,
-                        gnssDataProvider == mGnssMeasurementsProvider
-                                ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
-                                : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
-                        packageName,
-                        /* LocationRequest= */ null,
-                        /* hasListener= */ true,
-                        /* hasIntent= */ false,
-                        /* geofence= */ null,
-                        LocationManagerServiceUtils.getPackageImportance(packageName,
-                                mContext));
-            }
-            if (mLocationManagerService.isThrottlingExemptLocked(callerIdentity)
-                    || LocationManagerServiceUtils.isImportanceForeground(
-                    LocationManagerServiceUtils.getPackageImportance(packageName, mContext))) {
-                gnssDataProvider.addListener(listener, callerIdentity);
-            }
-            return true;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        if (gnssDataProvider == mGnssMeasurementsProvider
+                || gnssDataProvider == mGnssStatusProvider) {
+            mLocationUsageLogger.logLocationApiUsage(
+                    LocationStatsEnums.USAGE_STARTED,
+                    gnssDataProvider == mGnssMeasurementsProvider
+                            ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
+                            : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
+                    packageName,
+                    /* LocationRequest= */ null,
+                    /* hasListener= */ true,
+                    /* hasIntent= */ false,
+                    /* geofence= */ null,
+                    mAppForegroundHelper.getImportance(callerIdentity.mUid));
         }
+        if (mAppForegroundHelper.isAppForeground(callerIdentity.mUid)
+                || isThrottlingExempt(callerIdentity)) {
+            gnssDataProvider.addListener(listener, callerIdentity);
+        }
+        return true;
     }
 
-    private <TListener extends IInterface> void removeGnssDataListener(
+    private <TListener extends IInterface> void removeGnssDataListenerLocked(
             TListener listener,
             RemoteListenerHelper<TListener> gnssDataProvider,
-            ArrayMap<IBinder,
-                    LinkedListener<TListener>> gnssDataListeners) {
+            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
         if (gnssDataProvider == null) {
             Log.e(
                     TAG,
@@ -577,25 +442,19 @@
         if (linkedListener == null) {
             return;
         }
-        long identity = Binder.clearCallingIdentity();
-        try {
-            if (gnssDataProvider == mGnssMeasurementsProvider
-                    || gnssDataProvider == mGnssStatusProvider) {
-                mLocationUsageLogger.logLocationApiUsage(
-                        LocationStatsEnums.USAGE_ENDED,
-                        gnssDataProvider == mGnssMeasurementsProvider
-                                ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
-                                : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
-                        linkedListener.getCallerIdentity().mPackageName,
-                        /* LocationRequest= */ null,
-                        /* hasListener= */ true,
-                        /* hasIntent= */ false,
-                        /* geofence= */ null,
-                        LocationManagerServiceUtils.getPackageImportance(
-                                linkedListener.getCallerIdentity().mPackageName, mContext));
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        if (gnssDataProvider == mGnssMeasurementsProvider
+                || gnssDataProvider == mGnssStatusProvider) {
+            mLocationUsageLogger.logLocationApiUsage(
+                    LocationStatsEnums.USAGE_ENDED,
+                    gnssDataProvider == mGnssMeasurementsProvider
+                            ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
+                            : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
+                    linkedListener.getCallerIdentity().mPackageName,
+                    /* LocationRequest= */ null,
+                    /* hasListener= */ true,
+                    /* hasIntent= */ false,
+                    /* geofence= */ null,
+                    mAppForegroundHelper.getImportance(Binder.getCallingUid()));
         }
         linkedListener.unlinkFromListenerDeathNotificationLocked(binder);
         gnssDataProvider.removeListener(listener);
@@ -603,10 +462,6 @@
 
     /**
      * Registers listener for GNSS status changes.
-     *
-     * @param listener    called when GNSS status changes
-     * @param packageName name of requesting package
-     * @return true if listener is successfully registered, false otherwise
      */
     public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
             @Nullable String featureId) {
@@ -624,21 +479,15 @@
 
     /**
      * Unregisters listener for GNSS status changes.
-     *
-     * @param listener called when GNSS status changes
      */
     public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
         synchronized (mGnssStatusListeners) {
-            removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners);
+            removeGnssDataListenerLocked(listener, mGnssStatusProvider, mGnssStatusListeners);
         }
     }
 
     /**
      * Adds a GNSS measurements listener.
-     *
-     * @param listener    called when GNSS measurements are received
-     * @param packageName name of requesting package
-     * @return true if listener is successfully added, false otherwise
      */
     public boolean addGnssMeasurementsListener(
             IGnssMeasurementsListener listener, String packageName, @Nullable String featureId,
@@ -657,47 +506,32 @@
 
     /**
      * Injects GNSS measurement corrections.
-     *
-     * @param measurementCorrections GNSS measurement corrections
-     * @param packageName            name of requesting package
      */
     public void injectGnssMeasurementCorrections(
             GnssMeasurementCorrections measurementCorrections, String packageName) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to inject GNSS measurement corrections.");
-        if (!hasGnssPermissions(packageName)) {
-            Log.e(TAG, "Can not inject GNSS corrections due to no permission.");
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
+
+        if (!checkLocationAppOp(packageName)) {
             return;
         }
-        if (mGnssMeasurementCorrectionsProvider == null) {
-            Log.e(
-                    TAG,
-                    "Can not inject GNSS corrections. GNSS measurement corrections provider "
-                            + "not available.");
-            return;
-        }
+
         mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections(
                 measurementCorrections);
     }
 
     /**
      * Removes a GNSS measurements listener.
-     *
-     * @param listener called when GNSS measurements are received
      */
     public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
         synchronized (mGnssMeasurementsListeners) {
-            removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners);
+            removeGnssDataListenerLocked(listener, mGnssMeasurementsProvider,
+                    mGnssMeasurementsListeners);
         }
     }
 
     /**
      * Adds a GNSS navigation message listener.
-     *
-     * @param listener    called when navigation message is received
-     * @param packageName name of requesting package
-     * @return true if listener is successfully added, false otherwise
      */
     public boolean addGnssNavigationMessageListener(
             IGnssNavigationMessageListener listener, String packageName,
@@ -716,12 +550,10 @@
 
     /**
      * Removes a GNSS navigation message listener.
-     *
-     * @param listener called when navigation message is received
      */
     public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
         synchronized (mGnssNavigationMessageListeners) {
-            removeGnssDataListener(
+            removeGnssDataListenerLocked(
                     listener, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners);
         }
     }
@@ -729,43 +561,62 @@
     /**
      * Send Ni Response, indicating a location request initiated by a network carrier.
      */
-    public boolean sendNiResponse(int notifId, int userResponse) {
-        if (Binder.getCallingUid() != Process.myUid()) {
-            throw new SecurityException(
-                    "calling sendNiResponse from outside of the system is not allowed");
-        }
+    public void sendNiResponse(int notifId, int userResponse) {
         try {
-            return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
+            mNetInitiatedListener.sendNiResponse(notifId, userResponse);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
-            return false;
         }
     }
 
     /**
      * Report location results to GNSS batching listener.
-     *
-     * @param locations batch of locations to report to GNSS batching callback
      */
     public void onReportLocation(List<Location> locations) {
-        if (mGnssBatchingCallback == null) {
-            Log.e(TAG, "reportLocationBatch() called without active Callback");
+        IBatchedLocationCallback gnssBatchingCallback;
+        synchronized (mGnssBatchingLock) {
+            gnssBatchingCallback = mGnssBatchingCallback;
+        }
+
+        if (gnssBatchingCallback == null) {
             return;
         }
 
         try {
-            mGnssBatchingCallback.onLocationBatch(locations);
+            gnssBatchingCallback.onLocationBatch(locations);
         } catch (RemoteException e) {
             Log.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
         }
     }
 
+    private boolean isThrottlingExempt(CallerIdentity callerIdentity) {
+        if (callerIdentity.mUid == Process.SYSTEM_UID) {
+            return true;
+        }
+
+        if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
+                callerIdentity.mPackageName)) {
+            return true;
+        }
+
+        synchronized (this) {
+            Preconditions.checkState(mLocationManagerInternal != null);
+        }
+        return mLocationManagerInternal.isProviderPackage(callerIdentity.mPackageName);
+    }
+
+    private boolean checkLocationAppOp(String packageName) {
+        synchronized (this) {
+            Preconditions.checkState(mAppOpsManager != null);
+        }
+        return mAppOpsManager.checkOp(OP_FINE_LOCATION, Binder.getCallingUid(), packageName)
+                == AppOpsManager.MODE_ALLOWED;
+    }
+
     /**
-     * Dump for debugging.
+     * Dump info for debugging.
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
 
         if (args.length > 0 && args[0].equals("--gnssmetrics")) {
@@ -778,11 +629,8 @@
         ipw.println("GnssMeasurement Listeners:");
         ipw.increaseIndent();
         synchronized (mGnssMeasurementsListeners) {
-            for (LinkedListenerBase listener :
-                    mGnssMeasurementsListeners
-                            .values()) {
-                ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
-                        listener.getCallerIdentity()));
+            for (LinkedListenerBase listener : mGnssMeasurementsListeners.values()) {
+                ipw.println(listener);
             }
         }
         ipw.decreaseIndent();
@@ -790,10 +638,8 @@
         ipw.println("GnssNavigationMessage Listeners:");
         ipw.increaseIndent();
         synchronized (mGnssNavigationMessageListeners) {
-            for (LinkedListenerBase listener :
-                    mGnssNavigationMessageListeners.values()) {
-                ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
-                        listener.getCallerIdentity()));
+            for (LinkedListenerBase listener : mGnssNavigationMessageListeners.values()) {
+                ipw.println(listener);
             }
         }
         ipw.decreaseIndent();
@@ -801,10 +647,8 @@
         ipw.println("GnssStatus Listeners:");
         ipw.increaseIndent();
         synchronized (mGnssStatusListeners) {
-            for (LinkedListenerBase listener :
-                    mGnssStatusListeners.values()) {
-                ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
-                        listener.getCallerIdentity()));
+            for (LinkedListenerBase listener : mGnssStatusListeners.values()) {
+                ipw.println(listener);
             }
         }
         ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index c6d2b33..90afeff 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -96,7 +96,7 @@
      * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
      * evaluates to {@code true}.
      */
-    static ApexManager getInstance() {
+    public static ApexManager getInstance() {
         return sApexManagerSingleton.get();
     }
 
@@ -272,6 +272,21 @@
     public abstract String getApexModuleNameForPackageName(String apexPackageName);
 
     /**
+     * Copies the CE apex data directory for the given {@code userId} to a backup location, for use
+     * in case of rollback.
+     *
+     * @return long inode for the snapshot directory if the snapshot was successful, or -1 if not
+     */
+    public abstract long snapshotCeData(int userId, int rollbackId, String apexPackageName);
+
+    /**
+     * Restores the snapshot of the CE apex data directory for the given {@code userId}.
+     *
+     * @return boolean true if the restore was successful
+     */
+    public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName);
+
+    /**
      * Dumps various state information to the provided {@link PrintWriter} object.
      *
      * @param pw the {@link PrintWriter} object to send information to.
@@ -662,6 +677,45 @@
             }
         }
 
+        @Override
+        public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
+            populatePackageNameToApexModuleNameIfNeeded();
+            String apexModuleName;
+            synchronized (mLock) {
+                apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
+            }
+            if (apexModuleName == null) {
+                Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
+                return -1;
+            }
+            try {
+                return mApexService.snapshotCeData(userId, rollbackId, apexModuleName);
+            } catch (Exception e) {
+                Slog.e(TAG, e.getMessage(), e);
+                return -1;
+            }
+        }
+
+        @Override
+        public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
+            populatePackageNameToApexModuleNameIfNeeded();
+            String apexModuleName;
+            synchronized (mLock) {
+                apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
+            }
+            if (apexModuleName == null) {
+                Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
+                return false;
+            }
+            try {
+                mApexService.restoreCeData(userId, rollbackId, apexModuleName);
+                return true;
+            } catch (Exception e) {
+                Slog.e(TAG, e.getMessage(), e);
+                return false;
+            }
+        }
+
         /**
          * Dump information about the packages contained in a particular cache
          * @param packagesCache the cache to print information about.
@@ -865,6 +919,16 @@
         }
 
         @Override
+        public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
         void dump(PrintWriter pw, String packageName) {
             // No-op
         }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 5c17bec..7f73bae 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -68,9 +68,8 @@
 
     // Logs all filtering instead of enforcing
     private static final boolean DEBUG_ALLOW_ALL = false;
-
-    @SuppressWarnings("ConstantExpression")
-    private static final boolean DEBUG_LOGGING = false | DEBUG_ALLOW_ALL;
+    private static final boolean DEBUG_LOGGING = false;
+    private static final boolean FEATURE_ENABLED_BY_DEFAULT = false;
 
     /**
      * This contains a list of app UIDs that are implicitly queryable because another app explicitly
@@ -136,7 +135,7 @@
     private static class FeatureConfigImpl implements FeatureConfig {
         private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
         private final PackageManagerService.Injector mInjector;
-        private volatile boolean mFeatureEnabled = false;
+        private volatile boolean mFeatureEnabled = FEATURE_ENABLED_BY_DEFAULT;
 
         private FeatureConfigImpl(PackageManagerService.Injector injector) {
             mInjector = injector;
@@ -145,14 +144,15 @@
         @Override
         public void onSystemReady() {
             mFeatureEnabled = DeviceConfig.getBoolean(
-                    NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME, false);
+                    NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
+                    FEATURE_ENABLED_BY_DEFAULT);
             DeviceConfig.addOnPropertiesChangedListener(
                     NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
                     properties -> {
                         if (properties.getKeyset().contains(FILTERING_ENABLED_NAME)) {
                             synchronized (FeatureConfigImpl.this) {
                                 mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME,
-                                        false);
+                                        FEATURE_ENABLED_BY_DEFAULT);
                             }
                         }
                     });
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 43ed25f..431b4dc 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -154,10 +154,9 @@
     private static final boolean LOGD = true;
     private static final String REMOVE_MARKER_EXTENSION = ".removed";
 
-    private static final int MSG_COMMIT = 1;
-    private static final int MSG_ON_PACKAGE_INSTALLED = 2;
-    private static final int MSG_SEAL = 3;
-    private static final int MSG_STREAM_AND_VALIDATE = 4;
+    private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 1;
+    private static final int MSG_INSTALL = 2;
+    private static final int MSG_ON_PACKAGE_INSTALLED = 3;
 
     /** XML constants used for persisting a session */
     static final String TAG_SESSION = "session";
@@ -415,14 +414,11 @@
         @Override
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_SEAL:
-                    handleSeal((IntentSender) msg.obj);
+                case MSG_STREAM_VALIDATE_AND_COMMIT:
+                    handleStreamValidateAndCommit();
                     break;
-                case MSG_STREAM_AND_VALIDATE:
-                    handleStreamAndValidate();
-                    break;
-                case MSG_COMMIT:
-                    handleCommit();
+                case MSG_INSTALL:
+                    handleInstall();
                     break;
                 case MSG_ON_PACKAGE_INSTALLED:
                     final SomeArgs args = (SomeArgs) msg.obj;
@@ -1011,24 +1007,7 @@
                             + mParentSessionId +  " and may not be committed directly.");
         }
 
-        assertCanBeCommitted(forTransfer);
-
-        if (isMultiPackage()) {
-            for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
-                final int childSessionId = mChildSessionIds.keyAt(i);
-                mSessionProvider.getSession(childSessionId).assertCanBeCommitted(forTransfer);
-            }
-        }
-
-        if (mIncrementalFileStorages != null) {
-            mIncrementalFileStorages.finishSetUp();
-        }
-
-        mHandler.obtainMessage(MSG_SEAL, statusReceiver).sendToTarget();
-    }
-
-    private void handleSeal(@NonNull IntentSender statusReceiver) {
-        if (!markAsSealed(statusReceiver)) {
+        if (!markAsSealed(statusReceiver, forTransfer)) {
             return;
         }
         if (isMultiPackage()) {
@@ -1042,7 +1021,7 @@
                 // seal all children, regardless if any of them fail; we'll throw/return
                 // as appropriate once all children have been processed
                 if (!mSessionProvider.getSession(childSessionId)
-                        .markAsSealed(childIntentSender)) {
+                        .markAsSealed(childIntentSender, forTransfer)) {
                     sealFailed = true;
                 }
             }
@@ -1051,20 +1030,24 @@
             }
         }
 
-        dispatchStreamAndValidate();
+        if (mIncrementalFileStorages != null) {
+            mIncrementalFileStorages.finishSetUp();
+        }
+
+        dispatchStreamValidateAndCommit();
     }
 
-    private void dispatchStreamAndValidate() {
-        mHandler.obtainMessage(MSG_STREAM_AND_VALIDATE).sendToTarget();
+    private void dispatchStreamValidateAndCommit() {
+        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
     }
 
-    private void handleStreamAndValidate() {
+    private void handleStreamValidateAndCommit() {
         // TODO(b/136132412): update with new APIs
         if (mIncrementalFileStorages != null) {
             mIncrementalFileStorages.startLoading();
         }
 
-        boolean commitFailed = !markAsCommitted();
+        boolean success = streamValidateAndCommit();
 
         if (isMultiPackage()) {
             for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
@@ -1072,17 +1055,17 @@
                 // commit all children, regardless if any of them fail; we'll throw/return
                 // as appropriate once all children have been processed
                 if (!mSessionProvider.getSession(childSessionId)
-                        .markAsCommitted()) {
-                    commitFailed = true;
+                        .streamValidateAndCommit()) {
+                    success = false;
                 }
             }
         }
 
-        if (commitFailed) {
+        if (!success) {
             return;
         }
 
-        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
+        mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
     }
 
     private final class FileSystemConnector extends
@@ -1201,9 +1184,16 @@
     }
 
     /**
-     * Sanity checks to make sure it's ok to commit the session.
+     * If this was not already called, the session will be sealed.
+     *
+     * This method may be called multiple times to update the status receiver validate caller
+     * permissions.
      */
-    private void assertCanBeCommitted(boolean forTransfer) {
+    private boolean markAsSealed(@NonNull IntentSender statusReceiver, boolean forTransfer) {
+        Objects.requireNonNull(statusReceiver);
+
+        List<PackageInstallerSession> childSessions = getChildSessions();
+
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotDestroyedLocked("commit");
@@ -1226,21 +1216,7 @@
                     throw new IllegalArgumentException("Session has been transferred");
                 }
             }
-        }
-    }
 
-    /**
-     * If this was not already called, the session will be sealed.
-     *
-     * This method may be called multiple times to update the status receiver validate caller
-     * permissions.
-     */
-    private boolean markAsSealed(@NonNull IntentSender statusReceiver) {
-        Objects.requireNonNull(statusReceiver);
-
-        List<PackageInstallerSession> childSessions = getChildSessions();
-
-        synchronized (mLock) {
             mRemoteStatusReceiver = statusReceiver;
 
             // After updating the observer, we can skip re-sealing.
@@ -1263,10 +1239,8 @@
         return true;
     }
 
-    private boolean markAsCommitted() {
+    private boolean streamValidateAndCommit() {
         synchronized (mLock) {
-            Objects.requireNonNull(mRemoteStatusReceiver);
-
             if (mCommitted) {
                 return true;
             }
@@ -1519,7 +1493,7 @@
         mCallback.onSessionSealedBlocking(this);
     }
 
-    private void handleCommit() {
+    private void handleInstall() {
         if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
@@ -1548,7 +1522,7 @@
 
         try {
             synchronized (mLock) {
-                commitNonStagedLocked(childSessions);
+                installNonStagedLocked(childSessions);
             }
         } catch (PackageManagerException e) {
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
@@ -1559,25 +1533,25 @@
     }
 
     @GuardedBy("mLock")
-    private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
+    private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
             throws PackageManagerException {
-        final PackageManagerService.ActiveInstallSession committingSession =
+        final PackageManagerService.ActiveInstallSession installingSession =
                 makeSessionActiveLocked();
-        if (committingSession == null) {
+        if (installingSession == null) {
             return;
         }
         if (isMultiPackage()) {
-            List<PackageManagerService.ActiveInstallSession> activeChildSessions =
+            List<PackageManagerService.ActiveInstallSession> installingChildSessions =
                     new ArrayList<>(childSessions.size());
             boolean success = true;
             PackageManagerException failure = null;
             for (int i = 0; i < childSessions.size(); ++i) {
                 final PackageInstallerSession session = childSessions.get(i);
                 try {
-                    final PackageManagerService.ActiveInstallSession activeSession =
+                    final PackageManagerService.ActiveInstallSession installingChildSession =
                             session.makeSessionActiveLocked();
-                    if (activeSession != null) {
-                        activeChildSessions.add(activeSession);
+                    if (installingChildSession != null) {
+                        installingChildSessions.add(installingChildSession);
                     }
                 } catch (PackageManagerException e) {
                     failure = e;
@@ -1591,9 +1565,9 @@
                         failure.error, failure.getLocalizedMessage(), null);
                 return;
             }
-            mPm.installStage(activeChildSessions);
+            mPm.installStage(installingChildSessions);
         } else {
-            mPm.installStage(committingSession);
+            mPm.installStage(installingSession);
         }
     }
 
@@ -2306,7 +2280,7 @@
             // Mark and kick off another install pass
             synchronized (mLock) {
                 mPermissionsManuallyAccepted = true;
-                mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
+                mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
             }
         } else {
             destroyInternal();
@@ -2536,9 +2510,9 @@
                             mDataLoaderFinished = true;
                             if (hasParentSessionId()) {
                                 mSessionProvider.getSession(
-                                        mParentSessionId).dispatchStreamAndValidate();
+                                        mParentSessionId).dispatchStreamValidateAndCommit();
                             } else {
-                                dispatchStreamAndValidate();
+                                dispatchStreamValidateAndCommit();
                             }
                             dataLoader.destroy();
                             break;
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index ae28019..8935453 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -344,12 +344,12 @@
     }
 
     /**
+     * Perform snapshot and restore as required both for APEXes themselves and for apks in APEX.
      * Apks inside apex are not installed using apk-install flow. They are scanned from the system
      * directory directly by PackageManager, as such, RollbackManager need to handle their data
      * separately here.
      */
-    private void snapshotAndRestoreApkInApexUserData(PackageInstallerSession session) {
-        // We want to process apks inside apex. So current session needs to contain apex.
+    private void snapshotAndRestoreForApexSession(PackageInstallerSession session) {
         if (!sessionContainsApex(session)) {
             return;
         }
@@ -382,19 +382,37 @@
             apexSessions.add(session);
         }
 
-        // For each apex, process the apks inside it
+        final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+        final int[] allUsers = um.getUserIds();
+        IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                ServiceManager.getService(Context.ROLLBACK_SERVICE));
+
         for (PackageInstallerSession apexSession : apexSessions) {
-            List<String> apksInApex = mApexManager.getApksInApex(apexSession.getPackageName());
+            String packageName = apexSession.getPackageName();
+            // Perform any snapshots or restores for the APEX itself
+            snapshotAndRestoreApexUserData(packageName, allUsers, rm);
+
+            // Process the apks inside the APEX
+            List<String> apksInApex = mApexManager.getApksInApex(packageName);
             for (String apk: apksInApex) {
-                snapshotAndRestoreApkInApexUserData(apk);
+                snapshotAndRestoreApkInApexUserData(apk, allUsers, rm);
             }
         }
     }
 
-    private void snapshotAndRestoreApkInApexUserData(String packageName) {
-        IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
+    private void snapshotAndRestoreApexUserData(
+            String packageName, int[] allUsers, IRollbackManager rm) {
+        try {
+            // appId, ceDataInode, and seInfo are not needed for APEXes
+            rm.snapshotAndRestoreUserData(packageName, allUsers, 0, 0,
+                    null, 0 /*token*/);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Error snapshotting/restoring user data: " + re);
+        }
+    }
 
+    private void snapshotAndRestoreApkInApexUserData(
+            String packageName, int[] allUsers, IRollbackManager rm) {
         PackageManagerInternal mPmi = LocalServices.getService(PackageManagerInternal.class);
         AndroidPackage pkg = mPmi.getPackage(packageName);
         if (pkg == null) {
@@ -403,13 +421,11 @@
             return;
         }
         final String seInfo = pkg.getSeInfo();
-        final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
-        final int[] allUsers = um.getUserIds();
 
         int appId = -1;
         long ceDataInode = -1;
         final PackageSetting ps = (PackageSetting) mPmi.getPackageSetting(packageName);
-        if (ps != null && rm != null) {
+        if (ps != null) {
             appId = ps.appId;
             ceDataInode = ps.getCeDataInode(UserHandle.USER_SYSTEM);
             // NOTE: We ignore the user specified in the InstallParam because we know this is
@@ -498,7 +514,7 @@
                 abortCheckpoint();
                 return;
             }
-            snapshotAndRestoreApkInApexUserData(session);
+            snapshotAndRestoreForApexSession(session);
             Slog.i(TAG, "APEX packages in session " + session.sessionId
                     + " were successfully activated. Proceeding with APK packages, if any");
         }
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index f3a6018..81ec466 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -18,12 +18,14 @@
 
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import static java.lang.Integer.min;
 
@@ -114,6 +116,7 @@
                 boolean shouldApplyRestriction;
                 final int targetSDK;
                 final boolean hasRequestedLegacyExternalStorage;
+                final boolean hasWriteMediaStorageGrantedForUid;
 
                 if (appInfo != null) {
                     PackageManager pm = context.getPackageManager();
@@ -123,11 +126,14 @@
                     targetSDK = getMinimumTargetSDK(context, appInfo, user);
                     hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
                             appInfo.uid, context);
+                    hasWriteMediaStorageGrantedForUid = hasWriteMediaStorageGrantedForUid(
+                            appInfo.uid, context);
                 } else {
                     isWhiteListed = false;
                     shouldApplyRestriction = false;
                     targetSDK = 0;
                     hasRequestedLegacyExternalStorage = false;
+                    hasWriteMediaStorageGrantedForUid = false;
                 }
 
                 // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode
@@ -145,8 +151,9 @@
                     }
                     @Override
                     public boolean mayAllowExtraAppOp() {
-                        return !shouldApplyRestriction && hasRequestedLegacyExternalStorage
-                                && targetSDK <= Build.VERSION_CODES.Q;
+                        return !shouldApplyRestriction && targetSDK <= Build.VERSION_CODES.Q
+                                && (hasRequestedLegacyExternalStorage
+                                        || hasWriteMediaStorageGrantedForUid);
                     }
                     @Override
                     public boolean mayDenyExtraAppOpIfGranted() {
@@ -201,6 +208,22 @@
         return false;
     }
 
+    private static boolean hasWriteMediaStorageGrantedForUid(int uid, @NonNull Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        String[] packageNames = packageManager.getPackagesForUid(uid);
+        if (packageNames == null) {
+            return false;
+        }
+
+        for (String packageName : packageNames) {
+            if (packageManager.checkPermission(WRITE_MEDIA_STORAGE, packageName)
+                    == PERMISSION_GRANTED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * @return If the permission can be granted
      */
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index e6e6e23..e77839c 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.ApexManager;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
 
@@ -42,9 +43,17 @@
     private static final String TAG = "RollbackManager";
 
     private final Installer mInstaller;
+    private final ApexManager mApexManager;
 
     public AppDataRollbackHelper(Installer installer) {
         mInstaller = installer;
+        mApexManager = ApexManager.getInstance();
+    }
+
+    @VisibleForTesting
+    AppDataRollbackHelper(Installer installer, ApexManager apexManager) {
+        mInstaller = installer;
+        mApexManager = apexManager;
     }
 
     /**
@@ -55,7 +64,7 @@
     @GuardedBy("rollback.mLock")
     // TODO(b/136241838): Move into Rollback and synchronize there.
     public void snapshotAppData(
-            int snapshotId, PackageRollbackInfo packageRollbackInfo, int[] userIds) {
+            int rollbackId, PackageRollbackInfo packageRollbackInfo, int[] userIds) {
         for (int user : userIds) {
             final int storageFlags;
             if (isUserCredentialLocked(user)) {
@@ -68,16 +77,7 @@
                 storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE;
             }
 
-            try {
-                long ceSnapshotInode = mInstaller.snapshotAppData(
-                        packageRollbackInfo.getPackageName(), user, snapshotId, storageFlags);
-                if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) {
-                    packageRollbackInfo.putCeSnapshotInode(user, ceSnapshotInode);
-                }
-            } catch (InstallerException ie) {
-                Slog.e(TAG, "Unable to create app data snapshot for: "
-                        + packageRollbackInfo.getPackageName() + ", userId: " + user, ie);
-            }
+            doSnapshot(packageRollbackInfo, user, rollbackId, storageFlags);
         }
     }
 
@@ -119,26 +119,82 @@
             }
         }
 
-        try {
+        doRestoreOrWipe(packageRollbackInfo, userId, rollbackId, appId, seInfo, storageFlags);
+
+        return changedRollback;
+    }
+
+    private boolean doSnapshot(
+            PackageRollbackInfo packageRollbackInfo, int userId, int rollbackId, int flags) {
+        if (packageRollbackInfo.isApex()) {
+            // For APEX, only snapshot CE here
+            if ((flags & Installer.FLAG_STORAGE_CE) != 0) {
+                long ceSnapshotInode = mApexManager.snapshotCeData(
+                        userId, rollbackId, packageRollbackInfo.getPackageName());
+                if (ceSnapshotInode > 0) {
+                    packageRollbackInfo.putCeSnapshotInode(userId, ceSnapshotInode);
+                } else {
+                    return false;
+                }
+            }
+        } else {
+            // APK
+            try {
+                long ceSnapshotInode = mInstaller.snapshotAppData(
+                        packageRollbackInfo.getPackageName(), userId, rollbackId, flags);
+                if ((flags & Installer.FLAG_STORAGE_CE) != 0) {
+                    packageRollbackInfo.putCeSnapshotInode(userId, ceSnapshotInode);
+                }
+            } catch (InstallerException ie) {
+                Slog.e(TAG, "Unable to create app data snapshot for: "
+                        + packageRollbackInfo.getPackageName() + ", userId: " + userId, ie);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean doRestoreOrWipe(PackageRollbackInfo packageRollbackInfo, int userId,
+            int rollbackId, int appId, String seInfo, int flags) {
+        if (packageRollbackInfo.isApex()) {
             switch (packageRollbackInfo.getRollbackDataPolicy()) {
                 case PackageManager.RollbackDataPolicy.WIPE:
-                    mInstaller.clearAppData(null, packageRollbackInfo.getPackageName(),
-                            userId, storageFlags, 0);
+                    // TODO: Implement WIPE for apex CE data
                     break;
                 case PackageManager.RollbackDataPolicy.RESTORE:
-                    mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(), appId,
-                            seInfo, userId, rollbackId, storageFlags);
+                    // For APEX, only restore of CE may be done here.
+                    if ((flags & Installer.FLAG_STORAGE_CE) != 0) {
+                        mApexManager.restoreCeData(
+                                userId, rollbackId, packageRollbackInfo.getPackageName());
+                    }
                     break;
                 default:
                     break;
             }
-        } catch (InstallerException ie) {
-            Slog.e(TAG, "Unable to restore/wipe app data: "
-                    + packageRollbackInfo.getPackageName() + " policy="
-                    + packageRollbackInfo.getRollbackDataPolicy(), ie);
-        }
+        } else {
+            // APK
+            try {
+                switch (packageRollbackInfo.getRollbackDataPolicy()) {
+                    case PackageManager.RollbackDataPolicy.WIPE:
+                        mInstaller.clearAppData(null, packageRollbackInfo.getPackageName(),
+                                userId, flags, 0);
+                        break;
+                    case PackageManager.RollbackDataPolicy.RESTORE:
 
-        return changedRollback;
+                        mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(),
+                                appId, seInfo, userId, rollbackId, flags);
+                        break;
+                    default:
+                        break;
+                }
+            } catch (InstallerException ie) {
+                Slog.e(TAG, "Unable to restore/wipe app data: "
+                        + packageRollbackInfo.getPackageName() + " policy="
+                        + packageRollbackInfo.getRollbackDataPolicy(), ie);
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -204,40 +260,15 @@
 
             if (hasPendingBackup) {
                 int idx = pendingBackupUsers.indexOf(userId);
-                try {
-                    long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
-                            userId, rollback.info.getRollbackId(),
-                            Installer.FLAG_STORAGE_CE);
-                    info.putCeSnapshotInode(userId, ceSnapshotInode);
+                if (doSnapshot(
+                        info, userId, rollback.info.getRollbackId(), Installer.FLAG_STORAGE_CE)) {
                     pendingBackupUsers.remove(idx);
-                } catch (InstallerException ie) {
-                    Slog.e(TAG,
-                            "Unable to create app data snapshot for: "
-                                    + info.getPackageName() + ", userId: " + userId, ie);
                 }
             }
 
-            if (hasPendingRestore) {
-                try {
-                    switch (info.getRollbackDataPolicy()) {
-                        case PackageManager.RollbackDataPolicy.WIPE:
-                            mInstaller.clearAppData(null, info.getPackageName(), userId,
-                                    Installer.FLAG_STORAGE_CE, 0);
-                            break;
-                        case PackageManager.RollbackDataPolicy.RESTORE:
-                            mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
-                                    ri.seInfo, userId, rollback.info.getRollbackId(),
-                                    Installer.FLAG_STORAGE_CE);
-                            break;
-                        default:
-                            break;
-                    }
-                    info.removeRestoreInfo(ri);
-                } catch (InstallerException ie) {
-                    Slog.e(TAG, "Unable to restore/wipe app data for: "
-                            + info.getPackageName() + " policy="
-                            + info.getRollbackDataPolicy(), ie);
-                }
+            if (hasPendingRestore && doRestoreOrWipe(info, userId, rollback.info.getRollbackId(),
+                    ri.appId, ri.seInfo, Installer.FLAG_STORAGE_CE)) {
+                info.removeRestoreInfo(ri);
             }
         }
         return foundBackupOrRestore;
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index f3197cf..a1d4f42 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -95,7 +95,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.StatsEvent;
-import android.util.StatsLog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
@@ -119,6 +118,7 @@
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.StoragedUidIoStatsReader;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -263,115 +263,115 @@
         @Override
         public int onPullAtom(int atomTag, List<StatsEvent> data) {
             switch(atomTag) {
-                case StatsLog.WIFI_BYTES_TRANSFER:
+                case FrameworkStatsLog.WIFI_BYTES_TRANSFER:
                     return pullWifiBytesTransfer(atomTag, data);
-                case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
+                case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
                     return pullWifiBytesTransferBackground(atomTag, data);
-                case StatsLog.MOBILE_BYTES_TRANSFER:
+                case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
                     return pullMobileBytesTransfer(atomTag, data);
-                case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
+                case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
                     return pullMobileBytesTransferBackground(atomTag, data);
-                case StatsLog.BLUETOOTH_BYTES_TRANSFER:
+                case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER:
                     return pullBluetoothBytesTransfer(atomTag, data);
-                case StatsLog.KERNEL_WAKELOCK:
+                case FrameworkStatsLog.KERNEL_WAKELOCK:
                     return pullKernelWakelock(atomTag, data);
-                case StatsLog.CPU_TIME_PER_FREQ:
+                case FrameworkStatsLog.CPU_TIME_PER_FREQ:
                     return pullCpuTimePerFreq(atomTag, data);
-                case StatsLog.CPU_TIME_PER_UID:
+                case FrameworkStatsLog.CPU_TIME_PER_UID:
                     return pullCpuTimePerUid(atomTag, data);
-                case StatsLog.CPU_TIME_PER_UID_FREQ:
+                case FrameworkStatsLog.CPU_TIME_PER_UID_FREQ:
                     return pullCpuTimeperUidFreq(atomTag, data);
-                case StatsLog.CPU_ACTIVE_TIME:
+                case FrameworkStatsLog.CPU_ACTIVE_TIME:
                     return pullCpuActiveTime(atomTag, data);
-                case StatsLog.CPU_CLUSTER_TIME:
+                case FrameworkStatsLog.CPU_CLUSTER_TIME:
                     return pullCpuClusterTime(atomTag, data);
-                case StatsLog.WIFI_ACTIVITY_INFO:
+                case FrameworkStatsLog.WIFI_ACTIVITY_INFO:
                     return pullWifiActivityInfo(atomTag, data);
-                case StatsLog.MODEM_ACTIVITY_INFO:
+                case FrameworkStatsLog.MODEM_ACTIVITY_INFO:
                     return pullModemActivityInfo(atomTag, data);
-                case StatsLog.BLUETOOTH_ACTIVITY_INFO:
+                case FrameworkStatsLog.BLUETOOTH_ACTIVITY_INFO:
                     return pullBluetoothActivityInfo(atomTag, data);
-                case StatsLog.SYSTEM_ELAPSED_REALTIME:
+                case FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME:
                     return pullSystemElapsedRealtime(atomTag, data);
-                case StatsLog.SYSTEM_UPTIME:
+                case FrameworkStatsLog.SYSTEM_UPTIME:
                     return pullSystemUptime(atomTag, data);
-                case StatsLog.PROCESS_MEMORY_STATE:
+                case FrameworkStatsLog.PROCESS_MEMORY_STATE:
                     return pullProcessMemoryState(atomTag, data);
-                case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK:
+                case FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK:
                     return pullProcessMemoryHighWaterMark(atomTag, data);
-                case StatsLog.PROCESS_MEMORY_SNAPSHOT:
+                case FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT:
                     return pullProcessMemorySnapshot(atomTag, data);
-                case StatsLog.SYSTEM_ION_HEAP_SIZE:
+                case FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE:
                     return pullSystemIonHeapSize(atomTag, data);
-                case StatsLog.ION_HEAP_SIZE:
+                case FrameworkStatsLog.ION_HEAP_SIZE:
                     return pullIonHeapSize(atomTag, data);
-                case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE:
+                case FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE:
                     return pullProcessSystemIonHeapSize(atomTag, data);
-                case StatsLog.TEMPERATURE:
+                case FrameworkStatsLog.TEMPERATURE:
                     return pullTemperature(atomTag, data);
-                case StatsLog.COOLING_DEVICE:
+                case FrameworkStatsLog.COOLING_DEVICE:
                     return pullCooldownDevice(atomTag, data);
-                case StatsLog.BINDER_CALLS:
+                case FrameworkStatsLog.BINDER_CALLS:
                     return pullBinderCallsStats(atomTag, data);
-                case StatsLog.BINDER_CALLS_EXCEPTIONS:
+                case FrameworkStatsLog.BINDER_CALLS_EXCEPTIONS:
                     return pullBinderCallsStatsExceptions(atomTag, data);
-                case StatsLog.LOOPER_STATS:
+                case FrameworkStatsLog.LOOPER_STATS:
                     return pullLooperStats(atomTag, data);
-                case StatsLog.DISK_STATS:
+                case FrameworkStatsLog.DISK_STATS:
                     return pullDiskStats(atomTag, data);
-                case StatsLog.DIRECTORY_USAGE:
+                case FrameworkStatsLog.DIRECTORY_USAGE:
                     return pullDirectoryUsage(atomTag, data);
-                case StatsLog.APP_SIZE:
+                case FrameworkStatsLog.APP_SIZE:
                     return pullAppSize(atomTag, data);
-                case StatsLog.CATEGORY_SIZE:
+                case FrameworkStatsLog.CATEGORY_SIZE:
                     return pullCategorySize(atomTag, data);
-                case StatsLog.NUM_FINGERPRINTS_ENROLLED:
+                case FrameworkStatsLog.NUM_FINGERPRINTS_ENROLLED:
                     return pullNumBiometricsEnrolled(
                             BiometricsProtoEnums.MODALITY_FINGERPRINT, atomTag, data);
-                case StatsLog.NUM_FACES_ENROLLED:
+                case FrameworkStatsLog.NUM_FACES_ENROLLED:
                     return pullNumBiometricsEnrolled(
                             BiometricsProtoEnums.MODALITY_FACE, atomTag, data);
-                case StatsLog.PROC_STATS:
+                case FrameworkStatsLog.PROC_STATS:
                     return pullProcStats(ProcessStats.REPORT_ALL, atomTag, data);
-                case StatsLog.PROC_STATS_PKG_PROC:
+                case FrameworkStatsLog.PROC_STATS_PKG_PROC:
                     return pullProcStats(ProcessStats.REPORT_PKG_PROC_STATS, atomTag, data);
-                case StatsLog.DISK_IO:
+                case FrameworkStatsLog.DISK_IO:
                     return pullDiskIO(atomTag, data);
-                case StatsLog.POWER_PROFILE:
+                case FrameworkStatsLog.POWER_PROFILE:
                     return pullPowerProfile(atomTag, data);
-                case StatsLog.PROCESS_CPU_TIME:
+                case FrameworkStatsLog.PROCESS_CPU_TIME:
                     return pullProcessCpuTime(atomTag, data);
-                case StatsLog.CPU_TIME_PER_THREAD_FREQ:
+                case FrameworkStatsLog.CPU_TIME_PER_THREAD_FREQ:
                     return pullCpuTimePerThreadFreq(atomTag, data);
-                case StatsLog.DEVICE_CALCULATED_POWER_USE:
+                case FrameworkStatsLog.DEVICE_CALCULATED_POWER_USE:
                     return pullDeviceCalculatedPowerUse(atomTag, data);
-                case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID:
+                case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_UID:
                     return pullDeviceCalculatedPowerBlameUid(atomTag, data);
-                case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER:
+                case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER:
                     return pullDeviceCalculatedPowerBlameOther(atomTag, data);
-                case StatsLog.DEBUG_ELAPSED_CLOCK:
+                case FrameworkStatsLog.DEBUG_ELAPSED_CLOCK:
                     return pullDebugElapsedClock(atomTag, data);
-                case StatsLog.DEBUG_FAILING_ELAPSED_CLOCK:
+                case FrameworkStatsLog.DEBUG_FAILING_ELAPSED_CLOCK:
                     return pullDebugFailingElapsedClock(atomTag, data);
-                case StatsLog.BUILD_INFORMATION:
+                case FrameworkStatsLog.BUILD_INFORMATION:
                     return pullBuildInformation(atomTag, data);
-                case StatsLog.ROLE_HOLDER:
+                case FrameworkStatsLog.ROLE_HOLDER:
                     return pullRoleHolder(atomTag, data);
-                case StatsLog.DANGEROUS_PERMISSION_STATE:
+                case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE:
                     return pullDangerousPermissionState(atomTag, data);
-                case StatsLog.TIME_ZONE_DATA_INFO:
+                case FrameworkStatsLog.TIME_ZONE_DATA_INFO:
                     return pullTimeZoneDataInfo(atomTag, data);
-                case StatsLog.EXTERNAL_STORAGE_INFO:
+                case FrameworkStatsLog.EXTERNAL_STORAGE_INFO:
                     return pullExternalStorageInfo(atomTag, data);
-                case StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO:
+                case FrameworkStatsLog.APPS_ON_EXTERNAL_STORAGE_INFO:
                     return pullAppsOnExternalStorageInfo(atomTag, data);
-                case StatsLog.FACE_SETTINGS:
+                case FrameworkStatsLog.FACE_SETTINGS:
                     return pullFaceSettings(atomTag, data);
-                case StatsLog.APP_OPS:
+                case FrameworkStatsLog.APP_OPS:
                     return pullAppOps(atomTag, data);
-                case StatsLog.NOTIFICATION_REMOTE_VIEWS:
+                case FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS:
                     return pullNotificationRemoteViews(atomTag, data);
-                case StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED:
+                case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED:
                     return pullDangerousPermissionState(atomTag, data);
                 default:
                     throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
@@ -633,7 +633,7 @@
     }
 
     private void registerWifiBytesTransfer() {
-        int tagId = StatsLog.WIFI_BYTES_TRANSFER;
+        int tagId = FrameworkStatsLog.WIFI_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3, 4, 5})
                 .build();
@@ -725,7 +725,7 @@
     }
 
     private void registerWifiBytesTransferBackground() {
-        int tagId = StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG;
+        int tagId = FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3, 4, 5, 6})
                 .build();
@@ -763,7 +763,7 @@
     }
 
     private void registerMobileBytesTransfer() {
-        int tagId = StatsLog.MOBILE_BYTES_TRANSFER;
+        int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3, 4, 5})
                 .build();
@@ -801,7 +801,7 @@
     }
 
     private void registerMobileBytesTransferBackground() {
-        int tagId = StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG;
+        int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3, 4, 5, 6})
                 .build();
@@ -839,7 +839,7 @@
     }
 
     private void registerBluetoothBytesTransfer() {
-        int tagId = StatsLog.BLUETOOTH_BYTES_TRANSFER;
+        int tagId = FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3})
                 .build();
@@ -912,7 +912,7 @@
     }
 
     private void registerKernelWakelock() {
-        int tagId = StatsLog.KERNEL_WAKELOCK;
+        int tagId = FrameworkStatsLog.KERNEL_WAKELOCK;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* PullAtomMetadata */ null,
@@ -940,7 +940,7 @@
     }
 
     private void registerCpuTimePerFreq() {
-        int tagId = StatsLog.CPU_TIME_PER_FREQ;
+        int tagId = FrameworkStatsLog.CPU_TIME_PER_FREQ;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3})
                 .build();
@@ -971,7 +971,7 @@
     }
 
     private void registerCpuTimePerUid() {
-        int tagId = StatsLog.CPU_TIME_PER_UID;
+        int tagId = FrameworkStatsLog.CPU_TIME_PER_UID;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3})
                 .build();
@@ -1000,7 +1000,7 @@
     private void registerCpuTimePerUidFreq() {
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
-        int tagId = StatsLog.CPU_TIME_PER_UID_FREQ;
+        int tagId = FrameworkStatsLog.CPU_TIME_PER_UID_FREQ;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {4})
                 .build();
@@ -1032,7 +1032,7 @@
     private void registerCpuActiveTime() {
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
-        int tagId = StatsLog.CPU_ACTIVE_TIME;
+        int tagId = FrameworkStatsLog.CPU_ACTIVE_TIME;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2})
                 .build();
@@ -1059,7 +1059,7 @@
     private void registerCpuClusterTime() {
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
-        int tagId = StatsLog.CPU_CLUSTER_TIME;
+        int tagId = FrameworkStatsLog.CPU_CLUSTER_TIME;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {3})
                 .build();
@@ -1087,7 +1087,7 @@
     }
 
     private void registerWifiActivityInfo() {
-        int tagId = StatsLog.WIFI_ACTIVITY_INFO;
+        int tagId = FrameworkStatsLog.WIFI_ACTIVITY_INFO;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1139,7 +1139,7 @@
     }
 
     private void registerModemActivityInfo() {
-        int tagId = StatsLog.MODEM_ACTIVITY_INFO;
+        int tagId = FrameworkStatsLog.MODEM_ACTIVITY_INFO;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1177,7 +1177,7 @@
     }
 
     private void registerBluetoothActivityInfo() {
-        int tagId = StatsLog.BLUETOOTH_ACTIVITY_INFO;
+        int tagId = FrameworkStatsLog.BLUETOOTH_ACTIVITY_INFO;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* metadata */ null,
@@ -1205,7 +1205,7 @@
     }
 
     private void registerSystemElapsedRealtime() {
-        int tagId = StatsLog.SYSTEM_ELAPSED_REALTIME;
+        int tagId = FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setCoolDownNs(NS_PER_SEC)
                 .setTimeoutNs(NS_PER_SEC / 2)
@@ -1228,7 +1228,7 @@
     }
 
     private void registerSystemUptime() {
-        int tagId = StatsLog.SYSTEM_UPTIME;
+        int tagId = FrameworkStatsLog.SYSTEM_UPTIME;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1247,7 +1247,7 @@
     }
 
     private void registerProcessMemoryState() {
-        int tagId = StatsLog.PROCESS_MEMORY_STATE;
+        int tagId = FrameworkStatsLog.PROCESS_MEMORY_STATE;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {4, 5, 6, 7, 8})
                 .build();
@@ -1293,7 +1293,7 @@
     }
 
     private void registerProcessMemoryHighWaterMark() {
-        int tagId = StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;
+        int tagId = FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1346,7 +1346,7 @@
     }
 
     private void registerProcessMemorySnapshot() {
-        int tagId = StatsLog.PROCESS_MEMORY_SNAPSHOT;
+        int tagId = FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1406,7 +1406,7 @@
     }
 
     private void registerSystemIonHeapSize() {
-        int tagId = StatsLog.SYSTEM_ION_HEAP_SIZE;
+        int tagId = FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1426,7 +1426,7 @@
     }
 
     private void registerIonHeapSize() {
-        int tagId = StatsLog.ION_HEAP_SIZE;
+        int tagId = FrameworkStatsLog.ION_HEAP_SIZE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* PullAtomMetadata */ null,
@@ -1446,7 +1446,7 @@
     }
 
     private void registerProcessSystemIonHeapSize() {
-        int tagId = StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE;
+        int tagId = FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1472,7 +1472,7 @@
     }
 
     private void registerTemperature() {
-        int tagId = StatsLog.TEMPERATURE;
+        int tagId = FrameworkStatsLog.TEMPERATURE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1510,7 +1510,7 @@
     }
 
     private void registerCoolingDevice() {
-        int tagId = StatsLog.COOLING_DEVICE;
+        int tagId = FrameworkStatsLog.COOLING_DEVICE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1547,7 +1547,7 @@
     }
 
     private void registerBinderCallsStats() {
-        int tagId = StatsLog.BINDER_CALLS;
+        int tagId = FrameworkStatsLog.BINDER_CALLS;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {4, 5, 6, 8, 12})
                 .build();
@@ -1593,7 +1593,7 @@
     }
 
     private void registerBinderCallsStatsExceptions() {
-        int tagId = StatsLog.BINDER_CALLS_EXCEPTIONS;
+        int tagId = FrameworkStatsLog.BINDER_CALLS_EXCEPTIONS;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1625,7 +1625,7 @@
     }
 
     private void registerLooperStats() {
-        int tagId = StatsLog.LOOPER_STATS;
+        int tagId = FrameworkStatsLog.LOOPER_STATS;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {5, 6, 7, 8, 9})
                 .build();
@@ -1670,7 +1670,7 @@
     }
 
     private void registerDiskStats() {
-        int tagId = StatsLog.DISK_STATS;
+        int tagId = FrameworkStatsLog.DISK_STATS;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1736,7 +1736,7 @@
     }
 
     private void registerDirectoryUsage() {
-        int tagId = StatsLog.DIRECTORY_USAGE;
+        int tagId = FrameworkStatsLog.DIRECTORY_USAGE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1752,7 +1752,7 @@
 
         StatsEvent e = StatsEvent.newBuilder()
                 .setAtomId(atomTag)
-                .writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA)
+                .writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__DATA)
                 .writeLong(statFsData.getAvailableBytes())
                 .writeLong(statFsData.getTotalBytes())
                 .build();
@@ -1760,7 +1760,7 @@
 
         e = StatsEvent.newBuilder()
                 .setAtomId(atomTag)
-                .writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE)
+                .writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE)
                 .writeLong(statFsCache.getAvailableBytes())
                 .writeLong(statFsCache.getTotalBytes())
                 .build();
@@ -1768,7 +1768,7 @@
 
         e = StatsEvent.newBuilder()
                 .setAtomId(atomTag)
-                .writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM)
+                .writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM)
                 .writeLong(statFsSystem.getAvailableBytes())
                 .writeLong(statFsSystem.getTotalBytes())
                 .build();
@@ -1777,7 +1777,7 @@
     }
 
     private void registerAppSize() {
-        int tagId = StatsLog.APP_SIZE;
+        int tagId = FrameworkStatsLog.APP_SIZE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1821,7 +1821,7 @@
     }
 
     private void registerCategorySize() {
-        int tagId = StatsLog.CATEGORY_SIZE;
+        int tagId = FrameworkStatsLog.CATEGORY_SIZE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1839,7 +1839,7 @@
 
             StatsEvent e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE)
                     .writeLong(json.optLong(
                             DiskStatsFileLogger.APP_SIZE_AGG_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1848,7 +1848,7 @@
 
             e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE)
                     .writeLong(json.optLong(
                             DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1857,7 +1857,7 @@
 
             e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE)
                     .writeLong(json.optLong(
                             DiskStatsFileLogger.APP_CACHE_AGG_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1866,7 +1866,7 @@
 
             e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS)
                     .writeLong(json.optLong(
                             DiskStatsFileLogger.PHOTOS_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1875,7 +1875,7 @@
 
             e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS)
                     .writeLong(
                             json.optLong(DiskStatsFileLogger.VIDEOS_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1884,7 +1884,7 @@
 
             e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__AUDIO)
                     .writeLong(json.optLong(
                             DiskStatsFileLogger.AUDIO_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1893,7 +1893,7 @@
 
             e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS)
                     .writeLong(
                             json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1901,7 +1901,8 @@
             pulledData.add(e);
 
             e = StatsEvent.newBuilder()
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM)
+                    .setAtomId(atomTag)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM)
                     .writeLong(json.optLong(
                             DiskStatsFileLogger.SYSTEM_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1910,7 +1911,7 @@
 
             e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER)
+                    .writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__OTHER)
                     .writeLong(json.optLong(
                             DiskStatsFileLogger.MISC_KEY, /* fallback */ -1L))
                     .writeLong(cacheTime)
@@ -1924,7 +1925,7 @@
     }
 
     private void registerNumFingerprintsEnrolled() {
-        int tagId = StatsLog.NUM_FINGERPRINTS_ENROLLED;
+        int tagId = FrameworkStatsLog.NUM_FINGERPRINTS_ENROLLED;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1934,7 +1935,7 @@
     }
 
     private void registerNumFacesEnrolled() {
-        int tagId = StatsLog.NUM_FACES_ENROLLED;
+        int tagId = FrameworkStatsLog.NUM_FACES_ENROLLED;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -1992,7 +1993,7 @@
     }
 
     private void registerProcStats() {
-        int tagId = StatsLog.PROC_STATS;
+        int tagId = FrameworkStatsLog.PROC_STATS;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2002,7 +2003,7 @@
     }
 
     private void registerProcStatsPkgProc() {
-        int tagId = StatsLog.PROC_STATS_PKG_PROC;
+        int tagId = FrameworkStatsLog.PROC_STATS_PKG_PROC;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2071,7 +2072,7 @@
     }
 
     private void registerDiskIO() {
-        int tagId = StatsLog.DISK_IO;
+        int tagId = FrameworkStatsLog.DISK_IO;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11})
                 .setCoolDownNs(3 * NS_PER_SEC)
@@ -2108,7 +2109,7 @@
     }
 
     private void registerPowerProfile() {
-        int tagId = StatsLog.POWER_PROFILE;
+        int tagId = FrameworkStatsLog.POWER_PROFILE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 /* PullAtomMetadata */ null,
@@ -2131,7 +2132,7 @@
     }
 
     private void registerProcessCpuTime() {
-        int tagId = StatsLog.PROCESS_CPU_TIME;
+        int tagId = FrameworkStatsLog.PROCESS_CPU_TIME;
         // Min cool-down is 5 sec, in line with what ActivityManagerService uses.
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setCoolDownNs(5 * NS_PER_SEC)
@@ -2167,7 +2168,7 @@
     }
 
     private void registerCpuTimePerThreadFreq() {
-        int tagId = StatsLog.CPU_TIME_PER_THREAD_FREQ;
+        int tagId = FrameworkStatsLog.CPU_TIME_PER_THREAD_FREQ;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {7, 9, 11, 13, 15, 17, 19, 21})
                 .build();
@@ -2263,7 +2264,7 @@
     }
 
     private void registerDeviceCalculatedPowerUse() {
-        int tagId = StatsLog.DEVICE_CALCULATED_POWER_USE;
+        int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_USE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2283,7 +2284,7 @@
     }
 
     private void registerDeviceCalculatedPowerBlameUid() {
-        int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID;
+        int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_UID;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2313,7 +2314,7 @@
     }
 
     private void registerDeviceCalculatedPowerBlameOther() {
-        int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER;
+        int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2346,7 +2347,7 @@
     }
 
     private void registerDebugElapsedClock() {
-        int tagId = StatsLog.DEBUG_ELAPSED_CLOCK;
+        int tagId = FrameworkStatsLog.DEBUG_ELAPSED_CLOCK;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {1, 2, 3, 4})
                 .build();
@@ -2397,7 +2398,7 @@
     }
 
     private void registerDebugFailingElapsedClock() {
-        int tagId = StatsLog.DEBUG_FAILING_ELAPSED_CLOCK;
+        int tagId = FrameworkStatsLog.DEBUG_FAILING_ELAPSED_CLOCK;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
                 .setAdditiveFields(new int[] {1, 2, 3, 4})
                 .build();
@@ -2438,7 +2439,7 @@
     }
 
     private void registerBuildInformation() {
-        int tagId = StatsLog.BUILD_INFORMATION;
+        int tagId = FrameworkStatsLog.BUILD_INFORMATION;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2465,7 +2466,7 @@
     }
 
     private void registerRoleHolder() {
-        int tagId = StatsLog.ROLE_HOLDER;
+        int tagId = FrameworkStatsLog.ROLE_HOLDER;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2523,7 +2524,7 @@
     }
 
     private void registerDangerousPermissionState() {
-        int tagId = StatsLog.DANGEROUS_PERMISSION_STATE;
+        int tagId = FrameworkStatsLog.DANGEROUS_PERMISSION_STATE;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2561,7 +2562,7 @@
                     }
                     reportedUids.add(pkg.applicationInfo.uid);
 
-                    if (atomTag == StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED
+                    if (atomTag == FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED
                             && ThreadLocalRandom.current().nextFloat() > 0.01f) {
                         continue;
                     }
@@ -2588,7 +2589,7 @@
                         e.setAtomId(atomTag);
                         e.writeString(permName);
                         e.writeInt(pkg.applicationInfo.uid);
-                        if (atomTag == StatsLog.DANGEROUS_PERMISSION_STATE) {
+                        if (atomTag == FrameworkStatsLog.DANGEROUS_PERMISSION_STATE) {
                             e.writeString("");
                         }
                         e.writeBoolean((pkg.requestedPermissionsFlags[permNum]
@@ -2609,7 +2610,7 @@
     }
 
     private void registerTimeZoneDataInfo() {
-        int tagId = StatsLog.TIME_ZONE_DATA_INFO;
+        int tagId = FrameworkStatsLog.TIME_ZONE_DATA_INFO;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2636,7 +2637,7 @@
     }
 
     private void registerExternalStorageInfo() {
-        int tagId = StatsLog.EXTERNAL_STORAGE_INFO;
+        int tagId = FrameworkStatsLog.EXTERNAL_STORAGE_INFO;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2656,11 +2657,11 @@
             final DiskInfo diskInfo = vol.getDisk();
             if (diskInfo != null && envState.equals(Environment.MEDIA_MOUNTED)) {
                 // Get the type of the volume, if it is adoptable or portable.
-                int volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER;
+                int volumeType = FrameworkStatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER;
                 if (vol.getType() == TYPE_PUBLIC) {
-                    volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC;
+                    volumeType = FrameworkStatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC;
                 } else if (vol.getType() == TYPE_PRIVATE) {
-                    volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE;
+                    volumeType = FrameworkStatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE;
                 }
 
                 // Get the type of external storage inserted in the device (sd cards, usb, etc.)
@@ -2686,7 +2687,7 @@
     }
 
     private void registerAppsOnExternalStorageInfo() {
-        int tagId = StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO;
+        int tagId = FrameworkStatsLog.APPS_ON_EXTERNAL_STORAGE_INFO;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2742,7 +2743,7 @@
     }
 
     private void registerFaceSettings() {
-        int tagId = StatsLog.FACE_SETTINGS;
+        int tagId = FrameworkStatsLog.FACE_SETTINGS;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2796,7 +2797,7 @@
     }
 
     private void registerAppOps() {
-        int tagId = StatsLog.APP_OPS;
+        int tagId = FrameworkStatsLog.APP_OPS;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2907,7 +2908,7 @@
     }
 
     private void registerNotificationRemoteViews() {
-        int tagId = StatsLog.NOTIFICATION_REMOTE_VIEWS;
+        int tagId = FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2951,7 +2952,7 @@
     }
 
     private void registerDangerousPermissionStateSampled() {
-        int tagId = StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED;
+        int tagId = FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED;
         mStatsManager.registerPullAtomCallback(
                 tagId,
                 null, // use default PullAtomMetadata values
@@ -2965,8 +2966,8 @@
     private static final class ThermalEventListener extends IThermalEventListener.Stub {
         @Override
         public void notifyThrottling(Temperature temp) {
-            StatsLog.write(StatsLog.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED, temp.getType(),
-                    temp.getName(), (int) (temp.getValue() * 10), temp.getStatus());
+            FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED,
+                    temp.getType(), temp.getName(), (int) (temp.getValue() * 10), temp.getStatus());
         }
     }
 
@@ -2974,14 +2975,14 @@
             ConnectivityManager.NetworkCallback {
         @Override
         public void onAvailable(Network network) {
-            StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
-                    StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
+            FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+                    FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
         }
 
         @Override
         public void onLost(Network network) {
-            StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
-                    StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
+            FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+                    FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 23b94bd..2ef0015 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -83,13 +83,13 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
-import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 
@@ -768,8 +768,8 @@
         builder.setType(type);
         builder.addTaggedData(FIELD_CLASS_NAME, activity.info.name);
         mMetricsLogger.write(builder);
-        StatsLog.write(
-                StatsLog.APP_START_CANCELED,
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.APP_START_CANCELED,
                 activity.info.applicationInfo.uid,
                 activity.packageName,
                 convertAppStartTransitionType(type),
@@ -844,8 +844,8 @@
         builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
                 packageOptimizationInfo.getCompilationFilter());
         mMetricsLogger.write(builder);
-        StatsLog.write(
-                StatsLog.APP_START_OCCURRED,
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.APP_START_OCCURRED,
                 info.applicationInfo.uid,
                 info.packageName,
                 convertAppStartTransitionType(info.type),
@@ -895,15 +895,15 @@
 
     private int convertAppStartTransitionType(int tronType) {
         if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
-            return StatsLog.APP_START_OCCURRED__TYPE__COLD;
+            return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD;
         }
         if (tronType == TYPE_TRANSITION_WARM_LAUNCH) {
-            return StatsLog.APP_START_OCCURRED__TYPE__WARM;
+            return FrameworkStatsLog.APP_START_OCCURRED__TYPE__WARM;
         }
         if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
-            return StatsLog.APP_START_OCCURRED__TYPE__HOT;
+            return FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
         }
-        return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
+        return FrameworkStatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
     }
 
     /** @return the last known window drawn delay of the given activity. */
@@ -957,13 +957,13 @@
         builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
                 info.mProcessRunning ? 1 : 0);
         mMetricsLogger.write(builder);
-        StatsLog.write(
-                StatsLog.APP_START_FULLY_DRAWN,
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.APP_START_FULLY_DRAWN,
                 info.mLastLaunchedActivity.info.applicationInfo.uid,
                 info.mLastLaunchedActivity.packageName,
                 restoredFromBundle
-                        ? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
-                        : StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
+                        ? FrameworkStatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
+                        : FrameworkStatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
                 info.mLastLaunchedActivity.info.name,
                 info.mProcessRunning,
                 startupTimeMs);
@@ -1068,8 +1068,8 @@
             return;
         }
 
-        StatsLog.write(
-                StatsLog.APP_START_MEMORY_STATE_CAPTURED,
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.APP_START_MEMORY_STATE_CAPTURED,
                 uid,
                 info.processName,
                 info.launchedActivityName,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 976fbdb..f019013 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -222,7 +222,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationRunner;
@@ -248,6 +247,7 @@
 import com.android.internal.policy.KeyguardDismissCallback;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -5380,7 +5380,7 @@
         if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
                 "Updating global configuration to: " + values);
         writeConfigurationChanged(changes);
-        StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
+        FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_CONFIGURATION_CHANGED,
                 values.colorMode,
                 values.densityDpi,
                 values.fontScale,
@@ -5699,8 +5699,8 @@
             // will wake up stacks or put them to sleep as appropriate.
             if (wasSleeping) {
                 mSleeping = false;
-                StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
-                        StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__AWAKE);
+                FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+                        FrameworkStatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__AWAKE);
                 startTimeTrackingFocusedActivityLocked();
                 mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
                 Slog.d(TAG, "Top Process State changed to PROCESS_STATE_TOP");
@@ -5712,8 +5712,8 @@
             }
         } else if (!mSleeping && shouldSleep) {
             mSleeping = true;
-            StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
-                    StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__ASLEEP);
+            FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+                    FrameworkStatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__ASLEEP);
             if (mCurAppTimeTracker != null) {
                 mCurAppTimeTracker.stop();
             }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c50048e..8574672 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1244,6 +1244,11 @@
         return mDisplayRotation;
     }
 
+    void setInsetProvider(@InternalInsetsType int type, WindowState win,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider){
+        setInsetProvider(type, win, frameProvider, null /* imeFrameProvider */);
+    }
+
     /**
      * Marks a window as providing insets for the rest of the windows in the system.
      *
@@ -1251,10 +1256,14 @@
      * @param win The window.
      * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of
      *                      the window should be taken.
+     * @param imeFrameProvider Function to compute the frame when dispatching insets to the IME, or
+     *                         {@code null} if the normal frame should be taken.
      */
     void setInsetProvider(@InternalInsetsType int type, WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
-        mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
+        mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider,
+                imeFrameProvider);
     }
 
     InsetsStateController getInsetsStateController() {
@@ -3283,7 +3292,7 @@
         }
         computeImeTarget(true /* updateImeTarget */);
         mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
-                null /* frameProvider */);
+                null /* frameProvider */, null /* imeFrameProvider */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c6ccd4a..67d8acf 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1026,7 +1026,12 @@
                                         - getNavigationBarHeight(displayFrames.mRotation,
                                                 mDisplayContent.getConfiguration().uiMode);
                             }
-                        });
+                        },
+
+                        // For IME we use regular frame.
+                        (displayFrames, windowState, inOutFrame) ->
+                                inOutFrame.set(windowState.getFrameLw()));
+
                 mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
                         (displayFrames, windowState, inOutFrame) -> {
                             inOutFrame.top -= mBottomGestureAdditionalInset;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 2bb58dd..d540179 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -34,6 +34,7 @@
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
@@ -61,6 +62,8 @@
 
     private @Nullable ControlAdapter mAdapter;
     private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+    private TriConsumer<DisplayFrames, WindowState, Rect> mImeFrameProvider;
+    private final Rect mImeOverrideFrame = new Rect();
 
     /** The visibility override from the current controlling window. */
     private boolean mClientVisible;
@@ -111,9 +114,12 @@
      * @param win The window that links to this source.
      * @param frameProvider Based on display frame state and the window, calculates the resulting
      *                      frame that should be reported to clients.
+     * @param imeFrameProvider Based on display frame state and the window, calculates the resulting
+     *                         frame that should be reported to IME.
      */
     void setWindow(@Nullable WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
         if (mWin != null) {
             if (mControllable) {
                 mWin.setControllableInsetProvider(null);
@@ -126,6 +132,7 @@
         }
         mWin = win;
         mFrameProvider = frameProvider;
+        mImeFrameProvider = imeFrameProvider;
         if (win == null) {
             setServerVisible(false);
             mSource.setFrame(new Rect());
@@ -162,6 +169,12 @@
         }
         mSource.setFrame(mTmpRect);
 
+        if (mImeFrameProvider != null) {
+            mImeOverrideFrame.set(mWin.getFrameLw());
+            mImeFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin,
+                    mImeOverrideFrame);
+        }
+
         if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
                 || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
             mTmpRect.set(mWin.getFrameLw());
@@ -303,6 +316,21 @@
         return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
     }
 
+    /**
+     * @return Whether this provider uses a different frame to dispatch to the IME.
+     */
+    boolean overridesImeFrame() {
+        return mImeFrameProvider != null;
+    }
+
+    /**
+     * @return Rect to dispatch to the IME as frame. Only valid if {@link #overridesImeFrame()}
+     *         returns {@code true}.
+     */
+    Rect getImeOverrideFrame() {
+        return mImeOverrideFrame;
+    }
+
     private class ControlAdapter implements AnimationAdapter {
 
         private SurfaceControl mCapturedLeash;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index b2234d1..3e698da 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -88,6 +88,20 @@
             state.removeSource(ITYPE_IME);
             state.removeSource(ITYPE_STATUS_BAR);
         }
+
+        // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
+        if (type == ITYPE_IME) {
+            for (int i = mProviders.size() - 1; i >= 0; i--) {
+                InsetsSourceProvider otherProvider = mProviders.valueAt(i);
+                if (otherProvider.overridesImeFrame()) {
+                    InsetsSource override =
+                            new InsetsSource(state.getSource(otherProvider.getSource().getType()));
+                    override.setFrame(otherProvider.getImeOverrideFrame());
+                    state.addSource(override);
+                }
+            }
+        }
+
         return state;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 874a25e..aab6b510 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -196,7 +196,6 @@
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
@@ -227,6 +226,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.KeyInterceptionInfo;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
@@ -3154,7 +3154,7 @@
         final int requested = mLastRequestedExclusionHeight[side];
         final int granted = mLastGrantedExclusionHeight[side];
 
-        StatsLog.write(StatsLog.EXCLUSION_RECT_STATE_CHANGED,
+        FrameworkStatsLog.write(FrameworkStatsLog.EXCLUSION_RECT_STATE_CHANGED,
                 mAttrs.packageName, requested, requested - granted /* rejected */,
                 side + 1 /* Sides are 1-indexed in atoms.proto */,
                 (getConfiguration().orientation == ORIENTATION_LANDSCAPE),
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/AppForegroundHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/AppForegroundHelperTest.java
new file mode 100644
index 0000000..38ec4ce
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/AppForegroundHelperTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppForegroundHelperTest {
+
+    private static final long TIMEOUT_MS = 5000;
+
+    @Mock private Context mContext;
+    @Mock private ActivityManager mActivityManager;
+
+    private List<ActivityManager.OnUidImportanceListener> mListeners = new ArrayList<>();
+
+    private AppForegroundHelper mHelper;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+
+        doReturn(mActivityManager).when(mContext).getSystemService(ActivityManager.class);
+        doAnswer(invocation -> {
+            mListeners.add(invocation.getArgument(0));
+            return null;
+        }).when(mActivityManager).addOnUidImportanceListener(any(
+                ActivityManager.OnUidImportanceListener.class), eq(IMPORTANCE_FOREGROUND_SERVICE));
+
+        mHelper = new AppForegroundHelper(mContext);
+        mHelper.onSystemReady();
+    }
+
+    private void setImportance(int uid, int importance) {
+        doReturn(importance).when(mActivityManager).getUidImportance(uid);
+        for (ActivityManager.OnUidImportanceListener listener : mListeners) {
+            listener.onUidImportance(uid, importance);
+        }
+    }
+
+    @Test
+    public void testListeners() {
+        AppForegroundHelper.AppForegroundListener listener = mock(
+                AppForegroundHelper.AppForegroundListener.class);
+        mHelper.addListener(listener);
+
+        setImportance(0, IMPORTANCE_FOREGROUND);
+        verify(listener, timeout(TIMEOUT_MS)).onAppForegroundChanged(0, true);
+
+        setImportance(1, IMPORTANCE_FOREGROUND_SERVICE);
+        verify(listener, timeout(TIMEOUT_MS)).onAppForegroundChanged(1, true);
+
+        setImportance(2, IMPORTANCE_VISIBLE);
+        verify(listener, timeout(TIMEOUT_MS)).onAppForegroundChanged(2, false);
+    }
+
+    @Test
+    public void testIsAppForeground() {
+        setImportance(0, IMPORTANCE_FOREGROUND);
+        assertThat(mHelper.isAppForeground(0)).isEqualTo(true);
+
+        setImportance(0, IMPORTANCE_FOREGROUND_SERVICE);
+        assertThat(mHelper.isAppForeground(0)).isEqualTo(true);
+
+        setImportance(0, IMPORTANCE_VISIBLE);
+        assertThat(mHelper.isAppForeground(0)).isEqualTo(false);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
similarity index 73%
rename from services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java
rename to services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
index 06fb102..389fdf9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java
@@ -57,7 +57,7 @@
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class UserInfoStoreTest {
+public class UserInfoHelperTest {
 
     private static final int USER1_ID = 1;
     private static final int USER1_MANAGED_ID = 11;
@@ -72,7 +72,7 @@
     private StaticMockitoSession mMockingSession;
     private List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>();
 
-    private UserInfoStore mStore;
+    private UserInfoHelper mHelper;
 
     @Before
     public void setUp() {
@@ -97,8 +97,8 @@
 
         doReturn(USER1_ID).when(ActivityManager::getCurrentUser);
 
-        mStore = new UserInfoStore(mContext);
-        mStore.onSystemReady();
+        mHelper = new UserInfoHelper(mContext);
+        mHelper.onSystemReady();
     }
 
     @After
@@ -119,8 +119,9 @@
 
     @Test
     public void testListeners() {
-        UserInfoStore.UserChangedListener listener = mock(UserInfoStore.UserChangedListener.class);
-        mStore.addListener(listener);
+        UserInfoHelper.UserChangedListener listener = mock(
+                UserInfoHelper.UserChangedListener.class);
+        mHelper.addListener(listener);
 
         switchUser(USER1_ID);
         verify(listener, never()).onUserChanged(anyInt(), anyInt());
@@ -134,44 +135,44 @@
 
     @Test
     public void testCurrentUser() {
-        assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID);
+        assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID);
 
         switchUser(USER2_ID);
 
-        assertThat(mStore.getCurrentUserId()).isEqualTo(USER2_ID);
+        assertThat(mHelper.getCurrentUserId()).isEqualTo(USER2_ID);
 
         switchUser(USER1_ID);
 
-        assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID);
+        assertThat(mHelper.getCurrentUserId()).isEqualTo(USER1_ID);
     }
 
     @Test
     public void testIsCurrentUserOrProfile() {
-        assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isTrue();
-        assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue();
-        assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isFalse();
-        assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse();
+        assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isTrue();
+        assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue();
+        assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isFalse();
+        assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse();
 
         switchUser(USER2_ID);
 
-        assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isFalse();
-        assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isTrue();
-        assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse();
-        assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue();
+        assertThat(mHelper.isCurrentUserOrProfile(USER1_ID)).isFalse();
+        assertThat(mHelper.isCurrentUserOrProfile(USER2_ID)).isTrue();
+        assertThat(mHelper.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse();
+        assertThat(mHelper.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue();
     }
 
     @Test
     public void testGetParentUserId() {
-        assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
-        assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
-        assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
-        assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
+        assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
+        assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
+        assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
+        assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
 
         switchUser(USER2_ID);
 
-        assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
-        assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
-        assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
-        assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
+        assertThat(mHelper.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
+        assertThat(mHelper.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
+        assertThat(mHelper.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
+        assertThat(mHelper.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
index f262733..f16cf35 100644
--- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -31,7 +32,6 @@
 import static org.testng.Assert.assertThrows;
 
 import android.Manifest;
-import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -46,13 +46,15 @@
 import android.location.IGnssStatusListener;
 import android.location.INetInitiatedListener;
 import android.location.Location;
+import android.location.LocationManagerInternal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Message;
 import android.os.RemoteException;
 
-import com.android.server.LocationManagerService;
+import com.android.server.LocalServices;
+import com.android.server.location.AppForegroundHelper;
 import com.android.server.location.GnssBatchingProvider;
 import com.android.server.location.GnssCapabilitiesProvider;
 import com.android.server.location.GnssLocationProvider;
@@ -63,7 +65,9 @@
 import com.android.server.location.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative;
 import com.android.server.location.GnssStatusListenerHelper;
 import com.android.server.location.LocationUsageLogger;
+import com.android.server.location.SettingsHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.AdditionalMatchers;
@@ -93,18 +97,20 @@
     @Mock
     private GnssMeasurementCorrectionsProvider mMockGnssMeasurementCorrectionsProvider;
     @Mock
-    private INetInitiatedListener mMockNetInitiatedListener;
+    private INetInitiatedListener mNetInitiatedListener;
     private GnssMeasurementsProvider mTestGnssMeasurementsProvider;
     private GnssStatusListenerHelper mTestGnssStatusProvider;
     private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider;
 
     // Managers and services
     @Mock
-    private AppOpsManager mMockAppOpsManager;
+    private AppOpsManager mAppOpsManager;
     @Mock
-    private ActivityManager mMockActivityManager;
+    private SettingsHelper mSettingsHelper;
     @Mock
-    private LocationManagerService mMockLocationManagerService;
+    private AppForegroundHelper mAppForegroundHelper;
+    @Mock
+    private LocationManagerInternal mLocationManagerInternal;
 
     // Context and handler
     @Mock
@@ -113,25 +119,23 @@
     private Context mMockContext;
 
     // Class under test
-    private com.android.server.location.gnss.GnssManagerService mGnssManagerService;
+    private GnssManagerService mGnssManagerService;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         GnssLocationProvider.setIsSupportedForTest(true);
 
-        // Set up mock context
         when(mMockContext.getSystemServiceName(AppOpsManager.class)).thenReturn(
                 Context.APP_OPS_SERVICE);
-        when(mMockContext.getSystemServiceName(ActivityManager.class)).thenReturn(
-                Context.ACTIVITY_SERVICE);
         when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(
-                mMockAppOpsManager);
-        when(mMockContext.getSystemService(
-                eq(Context.ACTIVITY_SERVICE))).thenReturn(
-                mMockActivityManager);
+                mAppOpsManager);
         enableLocationPermissions();
 
+        when(mAppForegroundHelper.isAppForeground(anyInt())).thenReturn(true);
+
+        LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
+
         // Mock Handler will execute posted runnables immediately
         when(mMockHandler.sendMessageAtTime(any(Message.class), anyLong())).thenAnswer(
                 (InvocationOnMock invocation) -> {
@@ -164,15 +168,22 @@
         when(mMockGnssLocationProvider.getGnssNavigationMessageProvider()).thenReturn(
                 mTestGnssNavigationMessageProvider);
         when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn(
-                mMockNetInitiatedListener);
+                mNetInitiatedListener);
 
         // Setup GnssBatching provider
         when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true);
         when(mMockGnssBatchingProvider.stop()).thenReturn(true);
 
         // Create GnssManagerService
-        mGnssManagerService = new GnssManagerService(mMockLocationManagerService, mMockContext,
-                mMockGnssLocationProvider, new LocationUsageLogger());
+        mGnssManagerService = new GnssManagerService(mMockContext, mSettingsHelper,
+                mAppForegroundHelper, new LocationUsageLogger(),
+                mMockGnssLocationProvider);
+        mGnssManagerService.onSystemReady();
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(LocationManagerInternal.class);
     }
 
     private void overrideAsBinder(IInterface mockListener) {
@@ -225,7 +236,7 @@
                 PackageManager.PERMISSION_GRANTED);
 
         // AppOpsManager will return true if OP_FINE_LOCATION is checked
-        when(mMockAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenAnswer(
+        when(mAppOpsManager.checkOp(anyInt(), anyInt(), anyString())).thenAnswer(
                 (InvocationOnMock invocation) -> {
                     int code = (int) (invocation.getArguments()[0]);
                     if (code == AppOpsManager.OP_FINE_LOCATION) {
@@ -237,11 +248,11 @@
 
     private void disableLocationPermissions() {
         Mockito.doThrow(new SecurityException()).when(
-                mMockContext).enforceCallingPermission(anyString(), anyString());
+                mMockContext).enforceCallingPermission(anyString(), nullable(String.class));
         Mockito.doThrow(new SecurityException()).when(
                 mMockContext).checkPermission(anyString(), anyInt(), anyInt());
 
-        when(mMockAppOpsManager.checkOp(anyInt(), anyInt(),
+        when(mAppOpsManager.checkOp(anyInt(), anyInt(),
                 anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
     }
 
@@ -733,14 +744,12 @@
 
     @Test
     public void sendNiResponseWithPermissionsTest() throws RemoteException {
-        when(mMockNetInitiatedListener.sendNiResponse(anyInt(), anyInt())).thenReturn(true);
-
         int notifId = 0;
         int userResponse = 0;
         enableLocationPermissions();
 
-        assertThat(mGnssManagerService.sendNiResponse(notifId, userResponse)).isEqualTo(true);
+        mGnssManagerService.sendNiResponse(notifId, userResponse);
 
-        verify(mMockNetInitiatedListener, times(1)).sendNiResponse(notifId, userResponse);
+        verify(mNetInitiatedListener, times(1)).sendNiResponse(notifId, userResponse);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index f871203..3ceaac2 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
 
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
@@ -34,12 +35,15 @@
 import android.util.IntArray;
 import android.util.SparseLongArray;
 
+import com.android.server.pm.ApexManager;
 import com.android.server.pm.Installer;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.InOrder;
+import org.mockito.Mock;
 import org.mockito.Mockito;
 
 import java.io.File;
@@ -48,10 +52,17 @@
 @RunWith(JUnit4.class)
 public class AppDataRollbackHelperTest {
 
+    @Mock private ApexManager mApexManager;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+    }
+
     @Test
     public void testSnapshotAppData() throws Exception {
         Installer installer = mock(Installer.class);
-        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer));
+        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer, mApexManager));
 
         // All users are unlocked so we should snapshot data for them.
         doReturn(true).when(helper).isUserCredentialLocked(eq(10));
@@ -114,7 +125,7 @@
     @Test
     public void testRestoreAppDataSnapshot_pendingBackupForUser() throws Exception {
         Installer installer = mock(Installer.class);
-        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer));
+        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer, mApexManager));
 
         PackageRollbackInfo info = createPackageRollbackInfo("com.foo");
         IntArray pendingBackups = info.getPendingBackups();
@@ -139,7 +150,7 @@
     @Test
     public void testRestoreAppDataSnapshot_availableBackupForLockedUser() throws Exception {
         Installer installer = mock(Installer.class);
-        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer));
+        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer, mApexManager));
         doReturn(true).when(helper).isUserCredentialLocked(eq(10));
 
         PackageRollbackInfo info = createPackageRollbackInfo("com.foo");
@@ -163,7 +174,7 @@
     @Test
     public void testRestoreAppDataSnapshot_availableBackupForUnlockedUser() throws Exception {
         Installer installer = mock(Installer.class);
-        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer));
+        AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer, mApexManager));
         doReturn(false).when(helper).isUserCredentialLocked(eq(10));
 
         PackageRollbackInfo info = createPackageRollbackInfo("com.foo");
@@ -184,7 +195,7 @@
     @Test
     public void destroyAppData() throws Exception {
         Installer installer = mock(Installer.class);
-        AppDataRollbackHelper helper = new AppDataRollbackHelper(installer);
+        AppDataRollbackHelper helper = new AppDataRollbackHelper(installer, mApexManager);
 
         PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar");
         info.putCeSnapshotInode(11, 239L);
@@ -206,7 +217,7 @@
     @Test
     public void commitPendingBackupAndRestoreForUser() throws Exception {
         Installer installer = mock(Installer.class);
-        AppDataRollbackHelper helper = new AppDataRollbackHelper(installer);
+        AppDataRollbackHelper helper = new AppDataRollbackHelper(installer, mApexManager);
 
         when(installer.snapshotAppData(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(53L);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index d819b1a..7ffdd7c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -57,7 +57,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrameLw().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
-        mProvider.setWindow(statusBar, null);
+        mProvider.setWindow(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
         assertEquals(Insets.of(0, 100, 0, 0),
@@ -74,7 +74,7 @@
         ime.getGivenContentInsetsLw().set(0, 0, 0, 60);
         ime.getGivenVisibleInsetsLw().set(0, 0, 0, 75);
         ime.mHasSurface = true;
-        mProvider.setWindow(ime, null);
+        mProvider.setWindow(ime, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 40), mProvider.getSource().getFrame());
         assertEquals(new Rect(0, 0, 500, 25), mProvider.getSource().getVisibleFrame());
@@ -89,7 +89,7 @@
     public void testPostLayout_invisible() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrameLw().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null);
+        mProvider.setWindow(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
                         false /* ignoreVisibility */));
@@ -102,7 +102,7 @@
         mProvider.setWindow(statusBar,
                 (displayFrames, windowState, rect) -> {
                     rect.set(10, 10, 20, 20);
-                });
+                }, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
     }
@@ -112,7 +112,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrameLw().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null);
+        mProvider.setWindow(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         assertNotNull(mProvider.getControl(target));
         mProvider.updateControlForTarget(null, false /* force */);
@@ -124,7 +124,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrameLw().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null);
+        mProvider.setWindow(statusBar, null, null);
         mProvider.updateControlForFakeTarget(target);
         assertNotNull(mProvider.getControl(target));
         assertNull(mProvider.getControl(target).getLeash());
@@ -137,7 +137,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrameLw().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null);
+        mProvider.setWindow(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         InsetsState state = new InsetsState();
         state.getSource(ITYPE_STATUS_BAR).setVisible(false);
@@ -150,7 +150,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrameLw().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null);
+        mProvider.setWindow(statusBar, null, null);
         InsetsState state = new InsetsState();
         state.getSource(ITYPE_STATUS_BAR).setVisible(false);
         mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR));
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index d13baec..39cdd2c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -21,26 +21,26 @@
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.test.InsetsModeSession;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 @SmallTest
 @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
 @Presubmit
@@ -65,7 +65,7 @@
     public void testStripForDispatch_notOwn() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
         assertNotNull(getController().getInsetsForDispatch(app).getSource(ITYPE_STATUS_BAR));
     }
@@ -74,7 +74,7 @@
     public void testStripForDispatch_own() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
-                .setWindow(statusBar, null);
+                .setWindow(statusBar, null, null);
         statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
         final InsetsState state = getController().getInsetsForDispatch(statusBar);
         for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
@@ -88,19 +88,36 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null);
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
         assertEquals(0, getController().getInsetsForDispatch(navBar).getSourcesCount());
     }
 
     @Test
+    public void testImeForDispatch() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+        InsetsSourceProvider statusBarProvider =
+                getController().getSourceProvider(ITYPE_STATUS_BAR);
+        statusBarProvider.setWindow(statusBar, null, ((displayFrames, windowState, rect) ->
+                        rect.set(0, 1, 2, 3)));
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+        statusBar.setControllableInsetProvider(statusBarProvider);
+
+        statusBarProvider.onPostLayout();
+
+        final InsetsState state = getController().getInsetsForDispatch(ime);
+        assertEquals(new Rect(0, 1, 2, 3), state.getSource(ITYPE_STATUS_BAR).getFrame());
+    }
+
+    @Test
     public void testBarControllingWinChanged() {
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
         getController().onBarControlTargetChanged(app, null, app, null);
         InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
         assertEquals(2, controls.length);
@@ -110,7 +127,7 @@
     public void testControlRevoked() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
         getController().onBarControlTargetChanged(null, null, null, null);
@@ -122,7 +139,7 @@
     public void testControlRevoked_animation() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
         statusBar.cancelAnimation();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 186ff6b..2c68cc7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -425,7 +425,7 @@
         statusBar.mHasSurface = true;
         assertTrue(statusBar.isVisible());
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
-                .setWindow(statusBar, null /* frameProvider */);
+                .setWindow(statusBar, null /* frameProvider */, null /* imeFrameProvider */);
         mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
                 app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
         final InsetsSource source = new InsetsSource(ITYPE_STATUS_BAR);
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
index 33ad5e8..1dd7539 100644
--- a/telephony/java/android/telephony/CallForwardingInfo.java
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -15,7 +15,6 @@
  */
 
 package android.telephony;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -25,6 +24,8 @@
 import android.telephony.Annotation.CallForwardingReason;
 import android.telephony.Annotation.CallForwardingStatus;
 
+import com.android.telephony.Rlog;
+
 import java.util.Objects;
 
 /**
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 3f0aeb5..0c2f1f3 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -69,8 +69,8 @@
     protected String mAlphaShort;
 
     /** @hide */
-    protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
-                           String alphas) {
+    protected CellIdentity(@Nullable String tag, int type, @Nullable String mcc,
+            @Nullable String mnc, @Nullable String alphal, @Nullable String alphas) {
         mTag = tag;
         mType = type;
 
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 1a6bf33..e220b07 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.telephony.cdma.CdmaCellLocation;
 
@@ -90,8 +91,8 @@
      *
      * @hide
      */
-    public CellIdentityCdma(
-            int nid, int sid, int bid, int lon, int lat, String alphal, String alphas) {
+    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat,
+            @Nullable String alphal, @Nullable String alphas) {
         super(TAG, CellInfo.TYPE_CDMA, null, null, alphal, alphas);
         mNetworkId = inRangeOrUnavailable(nid, 0, NETWORK_ID_MAX);
         mSystemId = inRangeOrUnavailable(sid, 0, SYSTEM_ID_MAX);
@@ -108,22 +109,22 @@
     }
 
     /** @hide */
-    public CellIdentityCdma(android.hardware.radio.V1_0.CellIdentityCdma cid) {
+    public CellIdentityCdma(@NonNull android.hardware.radio.V1_0.CellIdentityCdma cid) {
         this(cid.networkId, cid.systemId, cid.baseStationId, cid.longitude, cid.latitude, "", "");
     }
 
     /** @hide */
-    public CellIdentityCdma(android.hardware.radio.V1_2.CellIdentityCdma cid) {
+    public CellIdentityCdma(@NonNull android.hardware.radio.V1_2.CellIdentityCdma cid) {
         this(cid.base.networkId, cid.base.systemId, cid.base.baseStationId, cid.base.longitude,
                 cid.base.latitude, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
     }
 
-    private CellIdentityCdma(CellIdentityCdma cid) {
+    private CellIdentityCdma(@NonNull CellIdentityCdma cid) {
         this(cid.mNetworkId, cid.mSystemId, cid.mBasestationId, cid.mLongitude, cid.mLatitude,
                 cid.mAlphaLong, cid.mAlphaShort);
     }
 
-    CellIdentityCdma copy() {
+    @NonNull CellIdentityCdma copy() {
         return new CellIdentityCdma(this);
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index dc73cbf..9f2537c 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -23,6 +23,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -78,26 +79,31 @@
      *
      * @hide
      */
-    public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
-                            String mncStr, String alphal, String alphas,
-                            List<String> additionalPlmns) {
+    public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, @Nullable String mccStr,
+            @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
+            @NonNull List<String> additionalPlmns) {
         super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
         mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
-        mAdditionalPlmns = additionalPlmns;
+        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
     }
 
     /** @hide */
-    public CellIdentityGsm(android.hardware.radio.V1_0.CellIdentityGsm cid) {
+    public CellIdentityGsm(@NonNull android.hardware.radio.V1_0.CellIdentityGsm cid) {
         this(cid.lac, cid.cid, cid.arfcn,
                 cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic,
                 cid.mcc, cid.mnc, "", "", Collections.emptyList());
     }
 
     /** @hide */
-    public CellIdentityGsm(android.hardware.radio.V1_2.CellIdentityGsm cid) {
+    public CellIdentityGsm(@NonNull android.hardware.radio.V1_2.CellIdentityGsm cid) {
         this(cid.base.lac, cid.base.cid, cid.base.arfcn,
                 cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc,
                 cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
@@ -105,7 +111,7 @@
     }
 
     /** @hide */
-    public CellIdentityGsm(android.hardware.radio.V1_5.CellIdentityGsm cid) {
+    public CellIdentityGsm(@NonNull android.hardware.radio.V1_5.CellIdentityGsm cid) {
         this(cid.base.base.lac, cid.base.base.cid, cid.base.base.arfcn,
                 cid.base.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE
                         : cid.base.base.bsic, cid.base.base.mcc,
@@ -113,12 +119,12 @@
                 cid.base.operatorNames.alphaShort, cid.additionalPlmns);
     }
 
-    private CellIdentityGsm(CellIdentityGsm cid) {
+    private CellIdentityGsm(@NonNull CellIdentityGsm cid) {
         this(cid.mLac, cid.mCid, cid.mArfcn, cid.mBsic, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns);
     }
 
-    CellIdentityGsm copy() {
+    @NonNull CellIdentityGsm copy() {
         return new CellIdentityGsm(this);
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index cf8fe6a..a194ae3 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -24,6 +24,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -104,34 +105,40 @@
      *
      * @hide
      */
-    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
-            String mncStr, String alphal, String alphas, List<String> additionalPlmns,
-            ClosedSubscriberGroupInfo csgInfo) {
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth,
+            @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal,
+            @Nullable String alphas, @NonNull List<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
         mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
         mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
-        mAdditionalPlmns = additionalPlmns;
+        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
         mCsgInfo = csgInfo;
     }
 
     /** @hide */
-    public CellIdentityLte(android.hardware.radio.V1_0.CellIdentityLte cid) {
+    public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) {
         this(cid.ci, cid.pci, cid.tac, cid.earfcn,
                 CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", Collections.emptyList(), null);
     }
 
     /** @hide */
-    public CellIdentityLte(android.hardware.radio.V1_2.CellIdentityLte cid) {
+    public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) {
         this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
                 cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
                 cid.operatorNames.alphaShort, Collections.emptyList(), null);
     }
 
     /** @hide */
-    public CellIdentityLte(android.hardware.radio.V1_5.CellIdentityLte cid) {
+    public CellIdentityLte(@NonNull android.hardware.radio.V1_5.CellIdentityLte cid) {
         this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn,
                 cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc,
                 cid.base.operatorNames.alphaLong, cid.base.operatorNames.alphaShort,
@@ -139,7 +146,7 @@
                         ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
     }
 
-    private CellIdentityLte(CellIdentityLte cid) {
+    private CellIdentityLte(@NonNull CellIdentityLte cid) {
         this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
     }
@@ -152,7 +159,7 @@
                 mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
     }
 
-    CellIdentityLte copy() {
+    @NonNull CellIdentityLte copy() {
         return new CellIdentityLte(this);
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index d4f181f..a0ef5aa 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -64,26 +64,32 @@
      * @hide
      */
     public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand List<Integer> bands,
-                          String mccStr, String mncStr, long nci, String alphal, String alphas,
-                          List<String> additionalPlmns) {
+                          @Nullable String mccStr, @Nullable String mncStr, long nci,
+                          @Nullable String alphal, @Nullable String alphas,
+                          @NonNull List<String> additionalPlmns) {
         super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
         mBands = new ArrayList<>(bands);
         mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns);
+        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
     }
 
     /** @hide */
-    public CellIdentityNr(android.hardware.radio.V1_4.CellIdentityNr cid) {
+    public CellIdentityNr(@NonNull android.hardware.radio.V1_4.CellIdentityNr cid) {
         this(cid.pci, cid.tac, cid.nrarfcn, Collections.emptyList(), cid.mcc, cid.mnc, cid.nci,
                 cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
                 Collections.emptyList());
     }
 
     /** @hide */
-    public CellIdentityNr(android.hardware.radio.V1_5.CellIdentityNr cid) {
+    public CellIdentityNr(@NonNull android.hardware.radio.V1_5.CellIdentityNr cid) {
         this(cid.base.pci, cid.base.tac, cid.base.nrarfcn, cid.bands, cid.base.mcc, cid.base.mnc,
                 cid.base.nci, cid.base.operatorNames.alphaLong,
                 cid.base.operatorNames.alphaShort, cid.additionalPlmns);
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 2ff351c..531487a 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -21,6 +21,7 @@
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -82,39 +83,44 @@
      *
      * @hide
      */
-    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, int uarfcn,
-            String alphal, String alphas, @NonNull List<String> additionalPlmns,
-            ClosedSubscriberGroupInfo csgInfo) {
+    public CellIdentityTdscdma(@Nullable String mcc, @Nullable String mnc, int lac, int cid,
+            int cpid, int uarfcn, @Nullable String alphal, @Nullable String alphas,
+            @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID);
         mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
-        mAdditionalPlmns = additionalPlmns;
+        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
         mCsgInfo = csgInfo;
     }
 
-    private CellIdentityTdscdma(CellIdentityTdscdma cid) {
+    private CellIdentityTdscdma(@NonNull CellIdentityTdscdma cid) {
         this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
                 cid.mCpid, cid.mUarfcn, cid.mAlphaLong,
                 cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
     }
 
     /** @hide */
-    public CellIdentityTdscdma(android.hardware.radio.V1_0.CellIdentityTdscdma cid) {
+    public CellIdentityTdscdma(@NonNull android.hardware.radio.V1_0.CellIdentityTdscdma cid) {
         this(cid.mcc, cid.mnc, cid.lac, cid.cid, cid.cpid, CellInfo.UNAVAILABLE, "", "",
                 Collections.emptyList(), null);
     }
 
     /** @hide */
-    public CellIdentityTdscdma(android.hardware.radio.V1_2.CellIdentityTdscdma cid) {
+    public CellIdentityTdscdma(@NonNull android.hardware.radio.V1_2.CellIdentityTdscdma cid) {
         this(cid.base.mcc, cid.base.mnc, cid.base.lac, cid.base.cid, cid.base.cpid,
                 cid.uarfcn, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
                 Collections.emptyList(), null);
     }
 
     /** @hide */
-    public CellIdentityTdscdma(android.hardware.radio.V1_5.CellIdentityTdscdma cid) {
+    public CellIdentityTdscdma(@NonNull android.hardware.radio.V1_5.CellIdentityTdscdma cid) {
         this(cid.base.base.mcc, cid.base.base.mnc, cid.base.base.lac, cid.base.base.cid,
                 cid.base.base.cpid, cid.base.uarfcn, cid.base.operatorNames.alphaLong,
                 cid.base.operatorNames.alphaShort,
@@ -130,7 +136,7 @@
                 mAdditionalPlmns, null);
     }
 
-    CellIdentityTdscdma copy() {
+    @NonNull CellIdentityTdscdma copy() {
         return new CellIdentityTdscdma(this);
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 9be42a1..15e491b 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -23,6 +23,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -83,34 +84,38 @@
      *
      * @hide
      */
-    public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
-                              String mccStr, String mncStr, String alphal, String alphas,
-                              @NonNull List<String> additionalPlmns,
-                              @Nullable ClosedSubscriberGroupInfo csgInfo) {
+    public CellIdentityWcdma(int lac, int cid, int psc, int uarfcn, @Nullable String mccStr,
+            @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
+            @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC);
         mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
-        mAdditionalPlmns = additionalPlmns;
+        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
         mCsgInfo = csgInfo;
     }
 
     /** @hide */
-    public CellIdentityWcdma(android.hardware.radio.V1_0.CellIdentityWcdma cid) {
+    public CellIdentityWcdma(@NonNull android.hardware.radio.V1_0.CellIdentityWcdma cid) {
         this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "",
                 Collections.emptyList(), null);
     }
 
     /** @hide */
-    public CellIdentityWcdma(android.hardware.radio.V1_2.CellIdentityWcdma cid) {
+    public CellIdentityWcdma(@NonNull android.hardware.radio.V1_2.CellIdentityWcdma cid) {
         this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn,
                 cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
                 cid.operatorNames.alphaShort, Collections.emptyList(), null);
     }
 
     /** @hide */
-    public CellIdentityWcdma(android.hardware.radio.V1_5.CellIdentityWcdma cid) {
+    public CellIdentityWcdma(@NonNull android.hardware.radio.V1_5.CellIdentityWcdma cid) {
         this(cid.base.base.lac, cid.base.base.cid, cid.base.base.psc, cid.base.base.uarfcn,
                 cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong,
                 cid.base.operatorNames.alphaShort, cid.additionalPlmns,
@@ -118,7 +123,7 @@
                         ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
     }
 
-    private CellIdentityWcdma(CellIdentityWcdma cid) {
+    private CellIdentityWcdma(@NonNull CellIdentityWcdma cid) {
         this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
     }
@@ -131,7 +136,7 @@
                 mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
     }
 
-    CellIdentityWcdma copy() {
+    @NonNull CellIdentityWcdma copy() {
         return new CellIdentityWcdma(this);
     }
 
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 5cd7cf8..8e83c4c 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2543,7 +2543,7 @@
         MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
         if (m != null) {
             m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides,
-                    sentIntent);
+                    sentIntent, 0L /* messageId */);
         }
     }
 
@@ -2581,7 +2581,7 @@
         MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
         if (m != null) {
             m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri,
-                    configOverrides, downloadedIntent);
+                    configOverrides, downloadedIntent, 0L /* messageId */);
         }
     }
 
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 80491cd..0fdcbc5 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -584,6 +584,25 @@
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
     }
 
+    @Test
+    public void testRollbackApexDataDirectories_Phase1() throws Exception {
+        int sessionId = Install.single(TEST_APEX_WITH_APK_V2).setStaged().setEnableRollback()
+                .commit();
+        InstallUtils.waitForSessionReady(sessionId);
+    }
+
+    @Test
+    public void testRollbackApexDataDirectories_Phase2() throws Exception {
+        RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME);
+
+        RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2);
+        RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
+
+        // Note: The app is not rolled back until after the rollback is staged
+        // and the device has been rebooted.
+        InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+    }
+
     private static void runShellCommand(String cmd) {
         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand(cmd);
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 672cbb0..ce2f7c5 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -16,6 +16,8 @@
 
 package com.android.tests.rollback.host;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 import static org.testng.Assert.assertThrows;
@@ -53,6 +55,17 @@
 
     private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
 
+    private static final String TEST_SUBDIR = "/subdir/";
+
+    private static final String TEST_FILENAME_1 = "test_file.txt";
+    private static final String TEST_STRING_1 = "hello this is a test";
+    private static final String TEST_FILENAME_2 = "another_file.txt";
+    private static final String TEST_STRING_2 = "this is a different file";
+    private static final String TEST_FILENAME_3 = "also.xyz";
+    private static final String TEST_STRING_3 = "also\n a\n test\n string";
+    private static final String TEST_FILENAME_4 = "one_more.test";
+    private static final String TEST_STRING_4 = "once more unto the test";
+
     @Before
     public void setUp() throws Exception {
         if (!getDevice().isAdbRoot()) {
@@ -77,6 +90,10 @@
         getDevice().executeShellCommand(
                 "rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex "
                         + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
+        getDevice().executeShellCommand(
+                "rm -rf " + apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "*");
+        getDevice().executeShellCommand(
+                "rm -rf " + apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "*");
         getDevice().reboot();
     }
 
@@ -245,15 +262,7 @@
     @Test
     public void testRollbackApexWithApk() throws Exception {
         getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
-        final File apex = buildHelper.getTestFile(fileName);
-        if (!getDevice().isAdbRoot()) {
-            getDevice().enableAdbRoot();
-        }
-        getDevice().remountSystemWritable();
-        assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
-        getDevice().reboot();
+        pushTestApex();
         runPhase("testRollbackApexWithApk_Phase1");
         getDevice().reboot();
         runPhase("testRollbackApexWithApk_Phase2");
@@ -267,15 +276,7 @@
     @Test
     public void testRollbackApexWithApkCrashing() throws Exception {
         getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
-        final File apex = buildHelper.getTestFile(fileName);
-        if (!getDevice().isAdbRoot()) {
-            getDevice().enableAdbRoot();
-        }
-        getDevice().remountSystemWritable();
-        assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
-        getDevice().reboot();
+        pushTestApex();
 
         // Install an apex with apk that crashes
         runPhase("testRollbackApexWithApkCrashing_Phase1");
@@ -289,6 +290,102 @@
         runPhase("testRollbackApexWithApkCrashing_Phase3");
     }
 
+    /**
+     * Tests that data in DE_sys apex data directory is restored when apex is rolled back.
+     */
+    @Test
+    public void testRollbackApexDataDirectories_DeSys() throws Exception {
+        pushTestApex();
+
+        // Push files to apex data directory
+        String oldFilePath1 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_1;
+        String oldFilePath2 =
+                apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_2;
+        assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
+        assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+
+        // Install new version of the APEX with rollback enabled
+        runPhase("testRollbackApexDataDirectories_Phase1");
+        getDevice().reboot();
+
+        // Replace files in data directory
+        getDevice().deleteFile(oldFilePath1);
+        getDevice().deleteFile(oldFilePath2);
+        String newFilePath3 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_3;
+        String newFilePath4 =
+                apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_4;
+        assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
+        assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+
+        // Roll back the APEX
+        runPhase("testRollbackApexDataDirectories_Phase2");
+        getDevice().reboot();
+
+        // Verify that old files have been restored and new files are gone
+        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+        assertNull(getDevice().pullFile(newFilePath3));
+        assertNull(getDevice().pullFile(newFilePath4));
+    }
+
+    /**
+     * Tests that data in CE apex data directory is restored when apex is rolled back.
+     */
+    @Test
+    public void testRollbackApexDataDirectories_Ce() throws Exception {
+        pushTestApex();
+
+        // Push files to apex data directory
+        String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
+        String oldFilePath2 =
+                apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
+        assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
+        assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+
+        // Install new version of the APEX with rollback enabled
+        runPhase("testRollbackApexDataDirectories_Phase1");
+        getDevice().reboot();
+
+        // Replace files in data directory
+        getDevice().deleteFile(oldFilePath1);
+        getDevice().deleteFile(oldFilePath2);
+        String newFilePath3 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
+        String newFilePath4 =
+                apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
+        assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
+        assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+
+        // Roll back the APEX
+        runPhase("testRollbackApexDataDirectories_Phase2");
+        getDevice().reboot();
+
+        // Verify that old files have been restored and new files are gone
+        assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+        assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+        assertNull(getDevice().pullFile(newFilePath3));
+        assertNull(getDevice().pullFile(newFilePath4));
+    }
+
+    private void pushTestApex() throws Exception {
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+        final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
+        final File apex = buildHelper.getTestFile(fileName);
+        if (!getDevice().isAdbRoot()) {
+            getDevice().enableAdbRoot();
+        }
+        getDevice().remountSystemWritable();
+        assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+        getDevice().reboot();
+    }
+
+    private static String apexDataDirDeSys(String apexName) {
+        return String.format("/data/misc/apexdata/%s", apexName);
+    }
+
+    private static String apexDataDirCe(String apexName, int userId) {
+        return String.format("/data/misc_ce/%d/apexdata/%s", userId, apexName);
+    }
+
     private void crashProcess(String processName, int numberOfCrashes) throws Exception {
         String pid = "";
         String lastPid = "invalid";
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 31b3a50..1f1c0c1 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -123,8 +123,6 @@
 
     boolean isWifiStandardSupported(int standard);
 
-    boolean needs5GHzToAnyApBandConversion();
-
     DhcpInfo getDhcpInfo();
 
     boolean isScanAlwaysAvailable();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9a25106..76f9716 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2802,29 +2802,6 @@
     }
 
     /**
-     * Check if the device is dual mode capable i.e. supports concurrent STA + Soft AP.
-     *
-     * If the device is dual mode capable, it may require conversion of the user's Soft AP band
-     * selection {@link SoftApConfiguration#mBand} from {@link SoftApConfiguration#BAND_5GHZ} to
-     * include also {@link SoftApConfiguration#BAND_2GHZ}, since if the device is connected to a
-     * 5GHz DFS channel as a STA, it may be unable to honor a request to start Soft AP on the same
-     * DFS channel.
-     *
-     * @return {@code true} if dual mode STA + AP is supported by this device, {@code false}
-     * otherwise.
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public boolean isDualModeSupported() {
-        try {
-            return mService.needs5GHzToAnyApBandConversion();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Return the DHCP-assigned addresses from the last successful DHCP request,
      * if any.
      * @return the DHCP information
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index b467553..060c85c 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -38,6 +38,7 @@
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkSuggestion;
 import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
@@ -268,7 +269,8 @@
         throw new UnsupportedOperationException();
     }
 
-    @Override
+    /** @deprecated use {@link WifiManager#isStaApConcurrencySupported()} */
+    @Deprecated
     public boolean needs5GHzToAnyApBandConversion() {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 0c2876e..a189d50 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1928,16 +1928,6 @@
     }
 
     /**
-     * Test behavior of {@link WifiManager#isDualModeSupported()} ()}
-     */
-    @Test
-    public void testIsDualModeSupported() throws Exception {
-        when(mWifiService.needs5GHzToAnyApBandConversion()).thenReturn(true);
-        assertTrue(mWifiManager.isDualModeSupported());
-        verify(mWifiService).needs5GHzToAnyApBandConversion();
-    }
-
-    /**
      * Test behavior of {@link WifiManager#is5GHzBandSupported()}
      */
     @Test