Merge "Update clearData flow for notification settings" into qt-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 13add09..9cd42a5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5719,14 +5719,18 @@
                 if (packages == null) {
                     break;
                 }
+
+                List<String> packagesHandled = new ArrayList<>();
+
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
-                        WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
+                        String packageName = packages[i];
+                        WeakReference<LoadedApk> ref = mPackages.get(packageName);
                         LoadedApk pkgInfo = ref != null ? ref.get() : null;
                         if (pkgInfo != null) {
                             hasPkgInfo = true;
                         } else {
-                            ref = mResourcePackages.get(packages[i]);
+                            ref = mResourcePackages.get(packageName);
                             pkgInfo = ref != null ? ref.get() : null;
                             if (pkgInfo != null) {
                                 hasPkgInfo = true;
@@ -5737,8 +5741,8 @@
                         // Adjust it's internal references to the application info and
                         // resources.
                         if (pkgInfo != null) {
+                            packagesHandled.add(packageName);
                             try {
-                                final String packageName = packages[i];
                                 final ApplicationInfo aInfo =
                                         sPackageManager.getApplicationInfo(
                                                 packageName,
@@ -5770,6 +5774,13 @@
                         }
                     }
                 }
+
+                try {
+                    getPackageManager().notifyPackagesReplacedReceived(
+                            packagesHandled.toArray(new String[0]));
+                } catch (RemoteException ignored) {
+                }
+
                 break;
             }
         }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index cf704d5..6ab4657 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -770,4 +770,6 @@
     int getRuntimePermissionsVersion(int userId);
 
     void setRuntimePermissionsVersion(int version, int userId);
+
+    void notifyPackagesReplacedReceived(in String[] packages);
 }
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index a51a871..232869d 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -453,14 +453,22 @@
         final boolean appIsProfileable = isProfileable(context);
         final boolean deviceIsDebuggable = getCanLoadSystemLibraries() == 1;
         if (appIsDebuggable || appIsProfileable || deviceIsDebuggable) {
+            String debugPackage;
 
-            String debugPackage =
-                    coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+            if (coreSettings != null) {
+                debugPackage =
+                        coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+            } else {
+                ContentResolver contentResolver = context.getContentResolver();
+                debugPackage = Settings.Global.getString(contentResolver,
+                        Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE);
+            }
 
             if ((debugPackage != null) && (!debugPackage.isEmpty())) {
                 return debugPackage;
             }
         }
+
         return "";
     }
 
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 3410d8d..44fc45e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -88,6 +88,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -3567,6 +3568,10 @@
                 return;
             }
             pw.println("Current Location Manager state:");
+            pw.print("  Current System Time: "
+                    + TimeUtils.logTimeOfDay(System.currentTimeMillis()));
+            pw.println(", Current Elapsed Time: "
+                    + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
             pw.println("  Current user: " + mCurrentUserId + " " + Arrays.toString(
                     mCurrentUserProfiles));
             pw.println("  Location mode: " + isLocationEnabled());
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1bcc4c8..a93f218 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3172,15 +3172,28 @@
 
     @GuardedBy("mService")
     void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+        boolean foundProcess = false;
         for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord r = mLruProcesses.get(i);
             if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
                 try {
+                    for (int index = packages.length - 1; index >= 0 && !foundProcess; index--) {
+                        if (packages[index].equals(r.info.packageName)) {
+                            foundProcess = true;
+                        }
+                    }
                     r.thread.dispatchPackageBroadcast(cmd, packages);
                 } catch (RemoteException ex) {
                 }
             }
         }
+
+        if (!foundProcess) {
+            try {
+                AppGlobals.getPackageManager().notifyPackagesReplacedReceived(packages);
+            } catch (RemoteException ignored) {
+            }
+        }
     }
 
     /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index c5733322..4ce6d91 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -43,7 +43,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.util.ArrayList;
 
 /** @hide */
 /*package*/ final class AudioDeviceBroker {
@@ -376,24 +375,29 @@
         mAudioService.postAccessoryPlugMediaUnmute(device);
     }
 
-    /*package*/ AudioService.VolumeStreamState getStreamState(int streamType) {
-        return mAudioService.getStreamState(streamType);
+    /*package*/ int getVssVolumeForDevice(int streamType, int device) {
+        return mAudioService.getVssVolumeForDevice(streamType, device);
     }
 
-    /*package*/ ArrayList<AudioService.SetModeDeathHandler> getSetModeDeathHandlers() {
-        return mAudioService.mSetModeDeathHandlers;
+    /*package*/ int getModeOwnerPid() {
+        return mAudioService.getModeOwnerPid();
     }
 
     /*package*/ int getDeviceForStream(int streamType) {
         return mAudioService.getDeviceForStream(streamType);
     }
 
-    /*package*/ void setDeviceVolume(AudioService.VolumeStreamState streamState, int device) {
-        mAudioService.setDeviceVolume(streamState, device);
+    /*package*/ void postApplyVolumeOnDevice(int streamType, int device, String caller) {
+        mAudioService.postApplyVolumeOnDevice(streamType, device, caller);
     }
 
-    /*packages*/ void observeDevicesForAllStreams() {
-        mAudioService.observeDevicesForAllStreams();
+    /*package*/ void postSetVolumeIndexOnDevice(int streamType, int vssVolIndex, int device,
+                                                String caller) {
+        mAudioService.postSetVolumeIndexOnDevice(streamType, vssVolIndex, device, caller);
+    }
+
+    /*packages*/ void postObserveDevicesForAllStreams() {
+        mAudioService.postObserveDevicesForAllStreams();
     }
 
     /*package*/ boolean isInCommunication() {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 91b51b4..f9dbdd5 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -198,14 +198,10 @@
                     }
                 }
                 if (a2dpVolume != -1) {
-                    AudioService.VolumeStreamState streamState =
-                            mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC);
-                    // Convert index to internal representation in VolumeStreamState
-                    a2dpVolume = a2dpVolume * 10;
-                    streamState.setIndex(a2dpVolume, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                            "onSetA2dpSinkConnectionState");
-                    mDeviceBroker.setDeviceVolume(
-                            streamState, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+                    mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+                            // convert index to internal representation in VolumeStreamState
+                            a2dpVolume * 10,
+                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
                 }
                 makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
                         "onSetA2dpSinkConnectionState", a2dpCodec);
@@ -302,14 +298,11 @@
             if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) {
                 // Device is connected
                 if (a2dpVolume != -1) {
-                    final AudioService.VolumeStreamState streamState =
-                            mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC);
-                    // Convert index to internal representation in VolumeStreamState
-                    a2dpVolume = a2dpVolume * 10;
-                    streamState.setIndex(a2dpVolume, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                    mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+                            // convert index to internal representation in VolumeStreamState
+                            a2dpVolume * 10,
+                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                             "onBluetoothA2dpActiveDeviceChange");
-                    mDeviceBroker.setDeviceVolume(
-                            streamState, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
                 }
             } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
                 if (di.mDeviceCodecFormat != a2dpCodec) {
@@ -352,7 +345,7 @@
             }
         }
         mRoutesObservers.finishBroadcast();
-        mDeviceBroker.observeDevicesForAllStreams();
+        mDeviceBroker.postObserveDevicesForAllStreams();
     }
 
     private static final int DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG =
@@ -655,8 +648,6 @@
             int a2dpCodec) {
         // enable A2DP before notifying A2DP connection to avoid unnecessary processing in
         // audio policy manager
-        AudioService.VolumeStreamState streamState =
-                mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC);
         mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource);
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec);
@@ -727,8 +718,8 @@
 
     @GuardedBy("mConnectedDevices")
     private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
-        final int hearingAidVolIndex = mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC)
-                .getIndex(AudioSystem.DEVICE_OUT_HEARING_AID);
+        final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC,
+                AudioSystem.DEVICE_OUT_HEARING_AID);
         mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, AudioSystem.STREAM_MUSIC);
 
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
@@ -739,9 +730,8 @@
                 new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name,
                         address, AudioSystem.AUDIO_FORMAT_DEFAULT));
         mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
-        mDeviceBroker.setDeviceVolume(
-                mDeviceBroker.getStreamState(AudioSystem.STREAM_MUSIC),
-                AudioSystem.DEVICE_OUT_HEARING_AID);
+        mDeviceBroker.postApplyVolumeOnDevice(AudioSystem.STREAM_MUSIC,
+                AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
         setCurrentAudioRouteNameIfPossible(name);
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2d00f29..b6879a3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -258,6 +258,8 @@
     private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23;
     private static final int MSG_ENABLE_SURROUND_FORMATS = 24;
     private static final int MSG_UPDATE_RINGER_MODE = 25;
+    private static final int MSG_SET_DEVICE_STREAM_VOLUME = 26;
+    private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -274,8 +276,8 @@
     /** @see VolumeStreamState */
     private VolumeStreamState[] mStreamStates;
 
-    /*package*/ VolumeStreamState getStreamState(int stream) {
-        return mStreamStates[stream];
+    /*package*/ int getVssVolumeForDevice(int stream, int device) {
+        return mStreamStates[stream].getIndex(device);
     }
 
     private SettingsObserver mSettingsObserver;
@@ -2936,7 +2938,21 @@
 
     }
 
-    /*package*/ class SetModeDeathHandler implements IBinder.DeathRecipient {
+    /**
+     * Return the pid of the current audio mode owner
+     * @return 0 if nobody owns the mode
+     */
+    /*package*/ int getModeOwnerPid() {
+        int modeOwnerPid = 0;
+        try {
+            modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
+        } catch (Exception e) {
+            // nothing to do, modeOwnerPid is not modified
+        }
+        return modeOwnerPid;
+    }
+
+    private class SetModeDeathHandler implements IBinder.DeathRecipient {
         private IBinder mCb; // To be notified of client's death
         private int mPid;
         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
@@ -4096,8 +4112,14 @@
         }
     }
 
+    /*package*/ void postObserveDevicesForAllStreams() {
+        sendMsg(mAudioHandler,
+                MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS,
+                SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/, null /*obj*/,
+                0 /*delay*/);
+    }
 
-    /*package*/ void observeDevicesForAllStreams() {
+    private void onObserveDevicesForAllStreams() {
         observeDevicesForStreams(-1);
     }
 
@@ -4270,7 +4292,7 @@
     //  2   mSetModeLock
     //  3     mSettingsLock
     //  4       VolumeStreamState.class
-    public class VolumeStreamState {
+    private class VolumeStreamState {
         private final int mStreamType;
         private int mIndexMin;
         private int mIndexMax;
@@ -4740,6 +4762,74 @@
         }
     }
 
+    private static final class DeviceVolumeUpdate {
+        final int mStreamType;
+        final int mDevice;
+        final @NonNull String mCaller;
+        private static final int NO_NEW_INDEX = -2049;
+        private final int mVssVolIndex;
+
+        // Constructor with volume index, meant to cause this volume to be set and applied for the
+        // given stream type on the given device
+        DeviceVolumeUpdate(int streamType, int vssVolIndex, int device, @NonNull String caller) {
+            mStreamType = streamType;
+            mVssVolIndex = vssVolIndex;
+            mDevice = device;
+            mCaller = caller;
+        }
+
+        // Constructor with no volume index, meant to cause re-apply of volume for the given
+        // stream type on the given device
+        DeviceVolumeUpdate(int streamType, int device, @NonNull String caller) {
+            mStreamType = streamType;
+            mVssVolIndex = NO_NEW_INDEX;
+            mDevice = device;
+            mCaller = caller;
+        }
+
+        boolean hasVolumeIndex() {
+            return mVssVolIndex != NO_NEW_INDEX;
+        }
+
+        int getVolumeIndex() throws IllegalStateException {
+            Preconditions.checkState(mVssVolIndex != NO_NEW_INDEX);
+            return mVssVolIndex;
+        }
+    }
+
+    /*package*/ void postSetVolumeIndexOnDevice(int streamType, int vssVolIndex, int device,
+                                                String caller) {
+        sendMsg(mAudioHandler,
+                MSG_SET_DEVICE_STREAM_VOLUME,
+                SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/,
+                new DeviceVolumeUpdate(streamType, vssVolIndex, device, caller),
+                0 /*delay*/);
+    }
+
+    /*package*/ void postApplyVolumeOnDevice(int streamType, int device, @NonNull String caller) {
+        sendMsg(mAudioHandler,
+                MSG_SET_DEVICE_STREAM_VOLUME,
+                SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/,
+                new DeviceVolumeUpdate(streamType, device, caller),
+                0 /*delay*/);
+    }
+
+    private void onSetVolumeIndexOnDevice(@NonNull DeviceVolumeUpdate update) {
+        synchronized (VolumeStreamState.class) {
+            final VolumeStreamState streamState = mStreamStates[update.mStreamType];
+            if (update.hasVolumeIndex()) {
+                final int index = update.getVolumeIndex();
+                streamState.setIndex(index, update.mDevice, update.mCaller);
+                sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller + " dev:0x"
+                        + Integer.toHexString(update.mDevice) + " volIdx:" + index));
+            } else {
+                sVolumeLogger.log(new AudioEventLogger.StringEvent(update.mCaller
+                        + " update vol on dev:0x" + Integer.toHexString(update.mDevice)));
+            }
+            setDeviceVolume(streamState, update.mDevice);
+        }
+    }
+
     /*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
 
         final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
@@ -5180,6 +5270,14 @@
                 case MSG_UPDATE_RINGER_MODE:
                     onUpdateRingerModeServiceInt();
                     break;
+
+                case MSG_SET_DEVICE_STREAM_VOLUME:
+                    onSetVolumeIndexOnDevice((DeviceVolumeUpdate) msg.obj);
+                    break;
+
+                case MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS:
+                    onObserveDevicesForAllStreams();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 332ff36..068c3d8 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -765,8 +765,7 @@
                 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
                 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
                 // currently controlled by the same client process.
-                int modeOwnerPid =  mDeviceBroker.getSetModeDeathHandlers().isEmpty()
-                        ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid();
+                final int modeOwnerPid =  mDeviceBroker.getModeOwnerPid();
                 if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
                     Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
                             + modeOwnerPid + " != creatorPid " + mCreatorPid);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 96fc6ec..ff0af13 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -66,6 +66,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.StatsLog;
+import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IBatteryStats;
@@ -332,9 +333,16 @@
     // true if low power mode for the GNSS chipset is part of the latest request.
     private boolean mLowPowerMode = false;
 
-    // true if we started navigation
+    // true if we started navigation in the HAL, only change value of this in setStarted
     private boolean mStarted;
 
+    // for logging of latest change, and warning of ongoing location after a stop
+    private long mStartedChangedElapsedRealtime;
+
+    // threshold for delay in GNSS engine turning off before warning & error
+    private static final long LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS = 2 * 1000;
+    private static final long LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS = 15 * 1000;
+
     // capabilities reported through the top level IGnssCallback.hal
     private volatile int mTopHalCapabilities;
 
@@ -1192,7 +1200,7 @@
             if (DEBUG) Log.d(TAG, "startNavigating");
             mTimeToFirstFix = 0;
             mLastFixTime = 0;
-            mStarted = true;
+            setStarted(true);
             mPositionMode = GPS_POSITION_MODE_STANDALONE;
             // Notify about suppressed output, if speed limit was previously exceeded.
             // Elsewhere, we check again with every speed output reported.
@@ -1230,12 +1238,12 @@
             mLowPowerMode = mProviderRequest.lowPowerMode;
             if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
                     interval, 0, 0, mLowPowerMode)) {
-                mStarted = false;
+                setStarted(false);
                 Log.e(TAG, "set_position_mode failed in startNavigating()");
                 return;
             }
             if (!native_start()) {
-                mStarted = false;
+                setStarted(false);
                 Log.e(TAG, "native_start failed in startNavigating()");
                 return;
             }
@@ -1258,7 +1266,7 @@
     private void stopNavigating() {
         if (DEBUG) Log.d(TAG, "stopNavigating");
         if (mStarted) {
-            mStarted = false;
+            setStarted(false);
             native_stop();
             mLastFixTime = 0;
             // native_stop() may reset the position mode in hardware.
@@ -1270,6 +1278,13 @@
         }
     }
 
+    private void setStarted(boolean started) {
+        if (mStarted != started) {
+            mStarted = started;
+            mStartedChangedElapsedRealtime = SystemClock.elapsedRealtime();
+        }
+    }
+
     private void hibernate() {
         // stop GPS until our next fix interval arrives
         stopNavigating();
@@ -1319,6 +1334,21 @@
                     mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes);
                 }
             }
+        } else {
+            // Warn or error about long delayed GNSS engine shutdown as this generally wastes
+            // power and sends location when not expected.
+            long locationAfterStartedFalseMillis =
+                    SystemClock.elapsedRealtime() - mStartedChangedElapsedRealtime;
+            if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS) {
+                String logMessage = "Unexpected GNSS Location report "
+                        + TimeUtils.formatDuration(locationAfterStartedFalseMillis)
+                        + " after location turned off";
+                if (locationAfterStartedFalseMillis > LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS) {
+                    Log.e(TAG, logMessage);
+                } else {
+                    Log.w(TAG, logMessage);
+                }
+            }
         }
 
         mLastFixTime = SystemClock.elapsedRealtime();
@@ -1538,7 +1568,7 @@
 
     private void restartLocationRequest() {
         if (DEBUG) Log.d(TAG, "restartLocationRequest");
-        mStarted = false;
+        setStarted(false);
         updateRequirements();
     }
 
@@ -2152,7 +2182,10 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         StringBuilder s = new StringBuilder();
-        s.append("  mStarted=").append(mStarted).append('\n');
+        s.append("  mStarted=").append(mStarted).append("   (changed ");
+        TimeUtils.formatDuration(SystemClock.elapsedRealtime()
+                - mStartedChangedElapsedRealtime, s);
+        s.append(" ago)").append('\n');
         s.append("  mFixInterval=").append(mFixInterval).append('\n');
         s.append("  mLowPowerMode=").append(mLowPowerMode).append('\n');
         s.append("  mGnssMeasurementsProvider.isRegistered()=")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c4d4106..3c336ea 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -952,6 +952,9 @@
     ActivityInfo mInstantAppInstallerActivity;
     final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
 
+    private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
+            mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
+
     final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
             = new SparseArray<>();
 
@@ -1319,6 +1322,11 @@
     static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
     static final int ENABLE_ROLLBACK_STATUS = 21;
     static final int ENABLE_ROLLBACK_TIMEOUT = 22;
+    static final int DEFERRED_NO_KILL_POST_DELETE = 23;
+    static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
+
+    static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
+    static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
 
     static final int WRITE_SETTINGS_DELAY = 10*1000;  // 10 seconds
 
@@ -1525,6 +1533,20 @@
 
                     Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
                 } break;
+                case DEFERRED_NO_KILL_POST_DELETE: {
+                    synchronized (mInstallLock) {
+                        InstallArgs args = (InstallArgs) msg.obj;
+                        if (args != null) {
+                            args.doPostDeleteLI(true);
+                        }
+                    }
+                } break;
+                case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
+                    String packageName = (String) msg.obj;
+                    if (packageName != null) {
+                        notifyInstallObserver(packageName);
+                    }
+                } break;
                 case WRITE_SETTINGS: {
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                     synchronized (mPackages) {
@@ -1791,7 +1813,10 @@
             String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
             boolean launchedForRestore, String installerPackage,
             IPackageInstallObserver2 installObserver) {
-        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+        final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
+        final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
+
+        if (succeeded) {
             // Send the removed broadcasts
             if (res.removedInfo != null) {
                 res.removedInfo.sendPackageRemovedBroadcasts(killApp);
@@ -1819,8 +1844,6 @@
                         mPermissionCallback);
             }
 
-            final boolean update = res.removedInfo != null
-                    && res.removedInfo.removedPackage != null;
             final String installerPackageName =
                     res.installerPackageName != null
                             ? res.installerPackageName
@@ -2029,11 +2052,18 @@
                     getUnknownSourcesSettings());
 
             // Remove the replaced package's older resources safely now
-            // We delete after a gc for applications  on sdcard.
-            if (res.removedInfo != null && res.removedInfo.args != null) {
-                Runtime.getRuntime().gc();
-                synchronized (mInstallLock) {
-                    res.removedInfo.args.doPostDeleteLI(true);
+            InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
+            if (args != null) {
+                if (!killApp) {
+                    // If we didn't kill the app, defer the deletion of code/resource files, since
+                    // they may still be in use by the running application. This mitigates problems
+                    // in cases where resources or code is loaded by a new Activity before
+                    // ApplicationInfo changes have propagated to all application threads.
+                    scheduleDeferredNoKillPostDelete(args);
+                } else {
+                    synchronized (mInstallLock) {
+                        args.doPostDeleteLI(true);
+                    }
                 }
             } else {
                 // Force a gc to clear up things. Ask for a background one, it's fine to go on
@@ -2056,18 +2086,62 @@
             }
         }
 
-        // If someone is watching installs - notify them
+        final boolean deferInstallObserver = succeeded && update && !killApp;
+        if (deferInstallObserver) {
+            scheduleDeferredNoKillInstallObserver(res, installObserver);
+        } else {
+            notifyInstallObserver(res, installObserver);
+        }
+    }
+
+    @Override
+    public void notifyPackagesReplacedReceived(String[] packages) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+
+        for (String packageName : packages) {
+            PackageSetting setting = mSettings.mPackages.get(packageName);
+            if (setting != null && filterAppAccessLPr(setting, callingUid, callingUserId)) {
+                notifyInstallObserver(packageName);
+            }
+        }
+    }
+
+    private void notifyInstallObserver(String packageName) {
+        Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
+                mNoKillInstallObservers.remove(packageName);
+
+        if (pair != null) {
+            notifyInstallObserver(pair.first, pair.second);
+        }
+    }
+
+    private void notifyInstallObserver(PackageInstalledInfo info,
+            IPackageInstallObserver2 installObserver) {
         if (installObserver != null) {
             try {
-                Bundle extras = extrasForInstallResult(res);
-                installObserver.onPackageInstalled(res.name, res.returnCode,
-                        res.returnMsg, extras);
+                Bundle extras = extrasForInstallResult(info);
+                installObserver.onPackageInstalled(info.name, info.returnCode,
+                        info.returnMsg, extras);
             } catch (RemoteException e) {
                 Slog.i(TAG, "Observer no longer exists.");
             }
         }
     }
 
+    private void scheduleDeferredNoKillPostDelete(InstallArgs args) {
+        Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
+        mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
+    }
+
+    private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
+            IPackageInstallObserver2 observer) {
+        String packageName = info.pkg.packageName;
+        mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
+        Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
+        mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
+    }
+
     /**
      * Gets the type of the external storage a package is installed on.
      * @param packageVolume The storage volume of the package.
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index db2fba9..2a9cb89 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2687,7 +2687,7 @@
 
     private void writePackageListLPrInternal(int creatingUserId) {
         // Only derive GIDs for active users (not dying)
-        final List<UserInfo> users = UserManagerService.getInstance().getUsers(true);
+        final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true);
         int[] userIds = new int[users.size()];
         for (int i = 0; i < userIds.length; i++) {
             userIds[i] = users.get(i).id;
@@ -4357,10 +4357,26 @@
         return pkgSetting.getHarmfulAppWarning(userId);
     }
 
+    /**
+     * Return all users on the device, including partial or dying users.
+     * @param userManager UserManagerService instance
+     * @return the list of users
+     */
     private static List<UserInfo> getAllUsers(UserManagerService userManager) {
+        return getUsers(userManager, false);
+    }
+
+    /**
+     * Return the list of users on the device. Clear the calling identity before calling into
+     * UserManagerService.
+     * @param userManager UserManagerService instance
+     * @param excludeDying Indicates whether to exclude any users marked for deletion.
+     * @return the list of users
+     */
+    private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) {
         long id = Binder.clearCallingIdentity();
         try {
-            return userManager.getUsers(false);
+            return userManager.getUsers(excludeDying);
         } catch (NullPointerException npe) {
             // packagemanager not yet initialized
         } finally {