Merge "Restore the behavior of adding a RESUMED activity to stopping list"
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index f08d0ef8..6b8cf63 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -36,7 +36,7 @@
  * (new) system for storing the brightness. It has methods to convert between the two and also
  * observes for when one of the settings is changed and syncs this with the other.
  */
-public class BrightnessSynchronizer{
+public class BrightnessSynchronizer {
 
     private static final int MSG_UPDATE_FLOAT = 1;
     private static final int MSG_UPDATE_INT = 2;
@@ -78,6 +78,26 @@
         mContext = context;
         mBrightnessSyncObserver = new BrightnessSyncObserver(mHandler);
         mBrightnessSyncObserver.startObserving();
+
+        // It is possible for the system to start up with the int and float values not
+        // synchronized. So we force an update to the int value, since float is the source
+        // of truth. Fallback to int value, if float is invalid. If both are invalid, use default
+        // float value from config.
+        final float currentFloatBrightness = getScreenBrightnessFloat(context);
+        final int currentIntBrightness = getScreenBrightnessInt(context);
+
+        if (!Float.isNaN(currentFloatBrightness)) {
+            updateBrightnessIntFromFloat(currentFloatBrightness);
+        } else if (currentIntBrightness != -1) {
+            updateBrightnessFloatFromInt(currentIntBrightness);
+        } else {
+            final float defaultBrightness = mContext.getResources().getFloat(
+                    com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat);
+            Settings.System.putFloatForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS_FLOAT, defaultBrightness,
+                    UserHandle.USER_CURRENT);
+
+        }
     }
 
     /**
@@ -132,7 +152,8 @@
 
     private static int getScreenBrightnessInt(Context context) {
         return Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS, 0, UserHandle.USER_CURRENT);
+                Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID,
+                UserHandle.USER_CURRENT);
     }
 
     private float mPreferredSettingValue;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7516a87..4a0e26a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -156,7 +156,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0);
+    static final int VERSION = 188 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -14576,6 +14576,7 @@
         mDailyStartTimeMs = in.readLong();
         mNextMinDailyDeadlineMs = in.readLong();
         mNextMaxDailyDeadlineMs = in.readLong();
+        mBatteryTimeToFullSeconds = in.readLong();
 
         mStartCount++;
 
@@ -15068,6 +15069,7 @@
         out.writeLong(mDailyStartTimeMs);
         out.writeLong(mNextMinDailyDeadlineMs);
         out.writeLong(mNextMaxDailyDeadlineMs);
+        out.writeLong(mBatteryTimeToFullSeconds);
 
         mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -15651,6 +15653,7 @@
         mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mLastWriteTimeMs = in.readLong();
+        mBatteryTimeToFullSeconds = in.readLong();
 
         mRpmStats.clear();
         int NRPMS = in.readInt();
@@ -15850,6 +15853,7 @@
         mDischargeLightDozeCounter.writeToParcel(out);
         mDischargeDeepDozeCounter.writeToParcel(out);
         out.writeLong(mLastWriteTimeMs);
+        out.writeLong(mBatteryTimeToFullSeconds);
 
         out.writeInt(mRpmStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index f506b7c..0bde5c0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -1,7 +1,9 @@
 # Default reviewers for this and subdirectories.
-qal@google.com
+andychou@google.com
 arcwang@google.com
-govenliu@google.com
 asapperstein@google.com
+goldmanj@google.com
+qal@google.com
+wengsu@google.com
 
-# Emergency approvers in case the above are not available
\ No newline at end of file
+# Emergency approvers in case the above are not available
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml b/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
deleted file mode 100644
index 9b48a70..0000000
--- a/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
+++ /dev/null
@@ -1,26 +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.
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-
-  <corners
-      android:bottomLeftRadius="8dp"
-      android:topLeftRadius="8dp" />
-  <solid android:color="@color/tv_audio_recording_indicator_background" />
-
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml b/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
deleted file mode 100644
index 0334875..0000000
--- a/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
+++ /dev/null
@@ -1,26 +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.
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-
-  <corners
-      android:bottomRightRadius="8dp"
-      android:topRightRadius="8dp" />
-  <solid android:color="@color/tv_audio_recording_indicator_background" />
-
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
index f9336a54..b62018d 100644
--- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
+++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
@@ -22,80 +22,17 @@
               android:padding="12dp">
 
     <FrameLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content">
+        android:layout_width="34dp"
+        android:layout_height="24dp"
+        android:layout_gravity="center"
+        android:background="@drawable/tv_rect_shadow_rounded">
 
-        <LinearLayout
-            android:id="@+id/icon_texts_container"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <FrameLayout
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent">
-
-                <View
-                    android:id="@+id/icon_container_bg"
-                    android:layout_width="50dp"
-                    android:layout_height="match_parent"
-                    android:background="@drawable/tv_rect_dark_left_rounded"/>
-
-                <FrameLayout
-                    android:id="@+id/icon_mic"
-                    android:layout_width="34dp"
-                    android:layout_height="24dp"
-                    android:layout_gravity="center"
-                    android:background="@drawable/tv_rect_shadow_rounded">
-
-                    <ImageView
-                        android:layout_width="13dp"
-                        android:layout_height="13dp"
-                        android:layout_gravity="center"
-                        android:background="@drawable/tv_ic_mic_white"/>
-                </FrameLayout>
-
-            </FrameLayout>
-
-            <LinearLayout
-                android:id="@+id/texts_container"
-                android:layout_width="wrap_content"
-                android:layout_height="47dp"
-                android:background="@color/tv_audio_recording_indicator_background"
-                android:orientation="vertical"
-                android:visibility="visible">
-
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="14dp"
-                    android:layout_marginTop="10dp"
-                    android:layout_marginBottom="1dp"
-                    android:text="@string/mic_active"
-                    android:textColor="@android:color/white"
-                    android:fontFamily="sans-serif"
-                    android:textSize="10dp"/>
-
-                <TextView
-                    android:id="@+id/text"
-                    android:layout_width="wrap_content"
-                    android:layout_height="14dp"
-                    android:singleLine="true"
-                    android:text="SomeApplication accessed your microphone"
-                    android:textColor="@android:color/white"
-                    android:fontFamily="sans-serif"
-                    android:textSize="8dp"/>
-
-            </LinearLayout>
-
-        </LinearLayout>
+        <ImageView
+            android:layout_width="13dp"
+            android:layout_height="13dp"
+            android:layout_gravity="center"
+            android:src="@drawable/tv_ic_mic_white"/>
 
     </FrameLayout>
 
-    <View
-        android:id="@+id/bg_end"
-        android:layout_width="12dp"
-        android:layout_height="47dp"
-        android:background="@drawable/tv_rect_dark_right_rounded"
-        android:visibility="visible"/>
-
 </LinearLayout>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index cb49918..1177bb5 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -22,8 +22,6 @@
     <color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
     <color name="recents_tv_text_shadow_color">#7F000000</color>
 
-    <!-- Background color for audio recording indicator (G800) -->
-    <color name="tv_audio_recording_indicator_background">#FF3C4043</color>
     <color name="tv_audio_recording_indicator_icon_background">#CC000000</color>
     <color name="tv_audio_recording_indicator_stroke">#33FFFFFF</color>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index a29db4d..7aeca64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -21,7 +21,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.IntDef;
 import android.annotation.UiThread;
@@ -36,7 +35,6 @@
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
-import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.tv.TvStatusBar;
@@ -83,19 +81,14 @@
     private static final int STATE_SHOWN = 2;
     private static final int STATE_DISAPPEARING = 3;
 
-    private static final int ANIMATION_DURATION = 600;
+    private static final int ANIMATION_DURATION_MS = 200;
 
     private final Context mContext;
     private boolean mIsEnabled;
 
     private View mIndicatorView;
-    private View mIconTextsContainer;
-    private View mIconContainerBg;
-    private View mIcon;
-    private View mBgEnd;
-    private View mTextsContainers;
-    private TextView mTextView;
-    private boolean mIsLtr;
+    private boolean mViewAndWindowAdded;
+    private ObjectAnimator mAnimator;
 
     @State private int mState = STATE_STOPPED;
 
@@ -190,7 +183,7 @@
         }
 
         if (active) {
-            showIfNotShown();
+            showIfNeeded();
         } else {
             hideIndicatorIfNeeded();
         }
@@ -198,153 +191,132 @@
 
     @UiThread
     private void hideIndicatorIfNeeded() {
-        // If not STATE_APPEARING, will check whether the indicator should be hidden when the
-        // indicator comes to the STATE_SHOWN.
-        // If STATE_DISAPPEARING or STATE_SHOWN - nothing else for us to do here.
-        if (mState != STATE_SHOWN) return;
+        // If STOPPED, NOT_SHOWN or DISAPPEARING - nothing else for us to do here.
+        if (mState != STATE_SHOWN && mState != STATE_APPEARING) return;
 
-        // If is in the STATE_SHOWN and there are no active recorders - hide.
-        if (!hasActiveRecorders()) {
-            hide();
+        if (hasActiveRecorders()) {
+            return;
+        }
+
+        if (mViewAndWindowAdded) {
+            mState = STATE_DISAPPEARING;
+            animateDisappearance();
+        } else {
+            // Appearing animation has not started yet, as we were still waiting for the View to be
+            // laid out.
+            mState = STATE_NOT_SHOWN;
+            removeIndicatorView();
         }
     }
 
     @UiThread
-    private void showIfNotShown() {
-        if (mState != STATE_NOT_SHOWN) return;
+    private void showIfNeeded() {
+        // If STOPPED, SHOWN or APPEARING - nothing else for us to do here.
+        if (mState != STATE_NOT_SHOWN && mState != STATE_DISAPPEARING) return;
+
         if (DEBUG) Log.d(TAG, "Showing indicator");
 
-        mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
-                == View.LAYOUT_DIRECTION_LTR;
+        final int prevState = mState;
+        mState = STATE_APPEARING;
+
+        if (prevState == STATE_DISAPPEARING) {
+            animateAppearance();
+            return;
+        }
 
         // Inflate the indicator view
         mIndicatorView = LayoutInflater.from(mContext).inflate(
-                R.layout.tv_audio_recording_indicator,
-                null);
-        mIconTextsContainer = mIndicatorView.findViewById(R.id.icon_texts_container);
-        mIconContainerBg = mIconTextsContainer.findViewById(R.id.icon_container_bg);
-        mIcon = mIconTextsContainer.findViewById(R.id.icon_mic);
-        mTextsContainers = mIconTextsContainer.findViewById(R.id.texts_container);
-        mTextView = mTextsContainers.findViewById(R.id.text);
-        mBgEnd = mIndicatorView.findViewById(R.id.bg_end);
+                R.layout.tv_audio_recording_indicator, null);
 
-        mTextsContainers.setVisibility(View.GONE);
-        mIconContainerBg.setVisibility(View.GONE);
-        mTextView.setVisibility(View.GONE);
-        mBgEnd.setVisibility(View.GONE);
-        mTextsContainers = null;
-        mIconContainerBg = null;
-        mTextView = null;
-        mBgEnd = null;
-
-        // Initially change the visibility to INVISIBLE, wait until and receives the size and
-        // then animate it moving from "off" the screen correctly
-        mIndicatorView.setVisibility(View.INVISIBLE);
+        // 1. Set alpha to 0.
+        // 2. Wait until the window is shown and the view is laid out.
+        // 3. Start a "fade in" (alpha) animation.
+        mIndicatorView.setAlpha(0f);
         mIndicatorView
                 .getViewTreeObserver()
                 .addOnGlobalLayoutListener(
                         new ViewTreeObserver.OnGlobalLayoutListener() {
                             @Override
                             public void onGlobalLayout() {
-                                if (mState == STATE_STOPPED) {
-                                    return;
-                                }
+                                // State could have changed to NOT_SHOWN (if all the recorders are
+                                // already gone) to STOPPED (if the indicator was disabled)
+                                if (mState != STATE_APPEARING) return;
 
+                                mViewAndWindowAdded = true;
                                 // Remove the observer
                                 mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
                                         this);
 
-                                // Now that the width of the indicator has been assigned, we can
-                                // move it in from off the screen.
-                                final int initialOffset =
-                                        (mIsLtr ? 1 : -1) * mIndicatorView.getWidth();
-                                final AnimatorSet set = new AnimatorSet();
-                                set.setDuration(ANIMATION_DURATION);
-                                set.playTogether(
-                                        ObjectAnimator.ofFloat(mIndicatorView,
-                                                View.TRANSLATION_X, initialOffset, 0),
-                                        ObjectAnimator.ofFloat(mIndicatorView, View.ALPHA, 0f,
-                                                1f));
-                                set.addListener(
-                                        new AnimatorListenerAdapter() {
-                                            @Override
-                                            public void onAnimationStart(Animator animation,
-                                                    boolean isReverse) {
-                                                if (mState == STATE_STOPPED) return;
-
-                                                // Indicator is INVISIBLE at the moment, change it.
-                                                mIndicatorView.setVisibility(View.VISIBLE);
-                                            }
-
-                                            @Override
-                                            public void onAnimationEnd(Animator animation) {
-                                                onAppeared();
-                                            }
-                                        });
-                                set.start();
+                                animateAppearance();
                             }
                         });
 
+        final boolean isLtr = mContext.getResources().getConfiguration().getLayoutDirection()
+                == View.LAYOUT_DIRECTION_LTR;
         final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                 WRAP_CONTENT,
                 WRAP_CONTENT,
                 WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSLUCENT);
-        layoutParams.gravity = Gravity.TOP | (mIsLtr ? Gravity.RIGHT : Gravity.LEFT);
+        layoutParams.gravity = Gravity.TOP | (isLtr ? Gravity.RIGHT : Gravity.LEFT);
         layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
         layoutParams.packageName = mContext.getPackageName();
         final WindowManager windowManager = (WindowManager) mContext.getSystemService(
                 Context.WINDOW_SERVICE);
         windowManager.addView(mIndicatorView, layoutParams);
-
-        mState = STATE_APPEARING;
     }
 
-    @UiThread
-    private void hide() {
-        if (DEBUG) Log.d(TAG, "Hide indicator");
 
-        final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth()
-                - (int) mIconTextsContainer.getTranslationX());
-        final AnimatorSet set = new AnimatorSet();
-        set.playTogether(
-                ObjectAnimator.ofFloat(mIndicatorView, View.TRANSLATION_X, targetOffset),
-                ObjectAnimator.ofFloat(mIcon, View.ALPHA, 0f));
-        set.setDuration(ANIMATION_DURATION);
-        set.addListener(
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        onHidden();
+    private void animateAppearance() {
+        animateAlphaTo(1f);
+    }
+
+    private void animateDisappearance() {
+        animateAlphaTo(0f);
+    }
+
+    private void animateAlphaTo(final float endValue) {
+        if (mAnimator == null) {
+            if (DEBUG) Log.d(TAG, "set up animator");
+
+            mAnimator = new ObjectAnimator();
+            mAnimator.setTarget(mIndicatorView);
+            mAnimator.setProperty(View.ALPHA);
+            mAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation, boolean isReverse) {
+                    if (DEBUG) Log.d(TAG, "onAnimationStart");
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    if (DEBUG) Log.d(TAG, "onAnimationCancel");
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (DEBUG) Log.d(TAG, "onAnimationEnd");
+
+                    if (mState == STATE_APPEARING) {
+                        mState = STATE_SHOWN;
+                    } else if (mState == STATE_DISAPPEARING) {
+                        removeIndicatorView();
+                        mState = STATE_NOT_SHOWN;
                     }
-                });
-        set.start();
-
-        mState = STATE_DISAPPEARING;
-    }
-
-
-    @UiThread
-    private void onAppeared() {
-        if (mState == STATE_STOPPED) return;
-
-        mState = STATE_SHOWN;
-
-        hideIndicatorIfNeeded();
-    }
-
-    @UiThread
-    private void onHidden() {
-        if (mState == STATE_STOPPED) return;
-
-        removeIndicatorView();
-        mState = STATE_NOT_SHOWN;
-
-        if (hasActiveRecorders()) {
-            // Got new recorders, show again.
-            showIfNotShown();
+                }
+            });
+        } else if (mAnimator.isRunning()) {
+            if (DEBUG) Log.d(TAG, "cancel running animation");
+            mAnimator.cancel();
         }
+
+        final float currentValue = mIndicatorView.getAlpha();
+        if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
+
+        mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
+        mAnimator.setFloatValues(endValue);
+        mAnimator.start();
     }
 
     private boolean hasActiveRecorders() {
@@ -363,12 +335,9 @@
         windowManager.removeView(mIndicatorView);
 
         mIndicatorView = null;
-        mIconTextsContainer = null;
-        mIconContainerBg = null;
-        mIcon = null;
-        mTextsContainers = null;
-        mTextView = null;
-        mBgEnd = null;
+        mAnimator = null;
+
+        mViewAndWindowAdded = false;
     }
 
     private static List<String> splitByComma(String string) {
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 1f0066a..01fa9e7 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -367,6 +367,13 @@
                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
                 }
             }
+            // Ignore the case when the network disconnects immediately after stop() has been
+            // called and the keepalive code is waiting for the response from the modem. This
+            // might happen when the caller listens for a lower-layer network disconnect
+            // callback and stop the keepalive at that time. But the stop() races with the
+            // stop() generated in ConnectivityService network disconnection code path.
+            if (mStartedState == STOPPING && reason == ERROR_INVALID_NETWORK) return;
+
             // Store the reason of stopping, and report it after the keepalive is fully stopped.
             if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
                 throw new IllegalStateException("Unexpected stop reason: " + mStopReason);
@@ -380,9 +387,6 @@
                     // e.g. invalid parameter.
                     cleanupStoppedKeepalive(mNai, mSlot);
                     break;
-                case STOPPING:
-                    // Keepalive is already in stopping state, ignore.
-                    return;
                 default:
                     mStartedState = STOPPING;
                     switch (mType) {
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index b9997e0..0a4d17f 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -259,7 +259,7 @@
             return "";
         }
         return String.join(COMPONENT_NAME_USER_ID_DELIM,
-                mComponentName.toString(),
+                mComponentName.flattenToString(),
                 String.valueOf(mUserId),
                 String.valueOf(mComponentType));
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a9f0681..fa4373f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1497,9 +1497,10 @@
             // anyone interested in this piece of information.
             final Task homeStack = targetTask.getDisplayArea().getRootHomeTask();
             final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null);
+            final ActivityRecord top = targetTask.getTopNonFinishingActivity();
+            final boolean visible = top != null && top.isVisible();
             mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
-                    targetTask.getTaskInfo(), homeTaskVisible, clearedTask,
-                    targetTask.getTopNonFinishingActivity().isVisible());
+                    targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2a0488d..4a1151b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6039,8 +6039,9 @@
         // If the most recent activity was noHistory but was only stopped rather
         // than stopped+finished because the device went to sleep, we need to make
         // sure to finish it as we're making a new activity topmost.
-        if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
-                !mLastNoHistoryActivity.finishing) {
+        if (shouldSleepActivities() && mLastNoHistoryActivity != null
+                && !mLastNoHistoryActivity.finishing
+                && mLastNoHistoryActivity != next) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "no-history finish of " + mLastNoHistoryActivity + " on new resume");
             mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 9f0b41f..c895420 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -67,6 +67,9 @@
     private NetworkAgent mNetworkAgent;
     private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
     private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
+    // Controls how test network agent is going to wait before responding to keepalive
+    // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem.
+    private long mKeepaliveResponseDelay = 0L;
     private Integer mExpectedKeepaliveSlot = null;
 
     public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
@@ -134,12 +137,17 @@
             if (mWrapper.mExpectedKeepaliveSlot != null) {
                 assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
             }
-            onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError);
+            mWrapper.mHandlerThread.getThreadHandler().postDelayed(
+                    () -> onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError),
+                    mWrapper.mKeepaliveResponseDelay);
         }
 
         @Override
         public void stopSocketKeepalive(Message msg) {
-            onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError);
+            final int slot = msg.arg1;
+            mWrapper.mHandlerThread.getThreadHandler().postDelayed(
+                    () -> onSocketKeepaliveEvent(slot, mWrapper.mStopKeepaliveError),
+                    mWrapper.mKeepaliveResponseDelay);
         }
 
         @Override
@@ -248,6 +256,10 @@
         mStopKeepaliveError = reason;
     }
 
+    public void setKeepaliveResponseDelay(long delay) {
+        mKeepaliveResponseDelay = delay;
+    }
+
     public void setExpectedKeepaliveSlot(Integer slot) {
         mExpectedKeepaliveSlot = slot;
     }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1f23bf3..7dfac9c 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4292,6 +4292,32 @@
         myNet = connectKeepaliveNetwork(lp);
         mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
 
+        // Check that a stop followed by network disconnects does not result in crash.
+        try (SocketKeepalive ka = mCm.createSocketKeepalive(
+                myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
+            ka.start(validKaInterval);
+            callback.expectStarted();
+            // Delay the response of keepalive events in networkAgent long enough to make sure
+            // the follow-up network disconnection will be processed first.
+            mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS);
+            ka.stop();
+
+            // Make sure the stop has been processed. Wait for executor idle is needed to prevent
+            // flaky since the actual stop call to the service is delegated to executor thread.
+            waitForIdleSerialExecutor(executor, TIMEOUT_MS);
+            waitForIdle();
+
+            mWiFiNetworkAgent.disconnect();
+            mWiFiNetworkAgent.expectDisconnected();
+            callback.expectStopped();
+            callback.assertNoCallback();
+        }
+
+        // Reconnect.
+        waitForIdle();
+        myNet = connectKeepaliveNetwork(lp);
+        mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+
         // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
         mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
         int srcPort2 = 0;