Merge "Fix NPE for CompanionDeviceActivity.setResultAndFinish()" into sc-v2-dev
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 856dfe5..e8725f0 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -162,8 +162,6 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     boolean mIsCreating = false;
-    private volatile boolean mRtHandlingPositionUpdates = false;
-    private volatile boolean mRtReleaseSurfaces = false;
 
     private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener =
             this::updateSurface;
@@ -909,13 +907,14 @@
                 mBlastBufferQueue = null;
             }
 
-            if (mRtHandlingPositionUpdates) {
-                mRtReleaseSurfaces = true;
-                return;
+            ViewRootImpl viewRoot = getViewRootImpl();
+            Transaction transaction = new Transaction();
+            releaseSurfaces(transaction);
+            if (viewRoot != null) {
+                viewRoot.applyTransactionOnDraw(transaction);
+            } else {
+                transaction.apply();
             }
-
-            releaseSurfaces(mTmpTransaction);
-            mTmpTransaction.apply();
         }
     }
 
@@ -1468,15 +1467,6 @@
                 if (mSurfaceControl == null) {
                     return;
                 }
-                // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
-                // its 2nd frame if RenderThread is running slowly could potentially see
-                // this as false, enter the branch, get pre-empted, then this comes along
-                // and reports a new position, then the UI thread resumes and reports
-                // its position. This could therefore be de-sync'd in that interval, but
-                // the synchronization would violate the rule that RT must never block
-                // on the UI thread which would open up potential deadlocks. The risk of
-                // a single-frame desync is therefore preferable for now.
-                mRtHandlingPositionUpdates = true;
             }
             if (mRTLastReportedPosition.left == left
                     && mRTLastReportedPosition.top == top
@@ -1552,12 +1542,7 @@
                     return;
                 }
                 mRtTransaction.hide(mSurfaceControl);
-                if (mRtReleaseSurfaces) {
-                    mRtReleaseSurfaces = false;
-                    releaseSurfaces(mRtTransaction);
-                }
                 applyOrMergeTransaction(mRtTransaction, frameNumber);
-                mRtHandlingPositionUpdates = false;
             }
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 65d0e49..2d7388f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10507,9 +10507,11 @@
 
     @Override
     public boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t) {
-        registerRtFrameCallback(frame -> {
-            mergeWithNextTransaction(t, frame);
-        });
+        if (mRemoved) {
+            t.apply();
+        } else {
+            registerRtFrameCallback(frame -> mergeWithNextTransaction(t, frame));
+        }
         return true;
     }
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 57b7d61..2357d13 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5746,9 +5746,11 @@
         // persisted across change, and has the RemoteViews re-applied in a different situation
         // (orientation or size), we throw an exception, since the layouts may be completely
         // unrelated.
-        if (!rvToApply.canRecycleView(v)) {
-            throw new RuntimeException("Attempting to re-apply RemoteViews to a view that"
-                    + " that does not share the same root layout id.");
+        if (hasMultipleLayouts()) {
+            if (!rvToApply.canRecycleView(v)) {
+                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
+                        " that does not share the same root layout id.");
+            }
         }
 
         rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
@@ -5792,9 +5794,11 @@
         // In the case that a view has this RemoteViews applied in one orientation, is persisted
         // across orientation change, and has the RemoteViews re-applied in the new orientation,
         // we throw an exception, since the layouts may be completely unrelated.
-        if (!rvToApply.canRecycleView(v)) {
-            throw new RuntimeException("Attempting to re-apply RemoteViews to a view that"
-                    + " that does not share the same root layout id.");
+        if (hasMultipleLayouts()) {
+            if (!rvToApply.canRecycleView(v)) {
+                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
+                        " that does not share the same root layout id.");
+            }
         }
 
         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 669965b..2511520 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -115,7 +115,7 @@
     }
 
     /**
-     * Appends dozing event to the logs
+     * Appends dozing event to the logs. Logs current dozing state when entering/exiting AOD.
      * @param dozing true if dozing, else false
      */
     public void traceDozing(boolean dozing) {
@@ -124,6 +124,14 @@
     }
 
     /**
+     * Appends dozing event to the logs when dozing has changed in AOD.
+     * @param dozing true if we're now dozing, else false
+     */
+    public void traceDozingChanged(boolean dozing) {
+        mLogger.logDozingChanged(dozing);
+    }
+
+    /**
      * Appends dozing event to the logs
      * @param suppressed true if dozing is suppressed
      */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index d79bf22..4ba6b51 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -66,6 +66,14 @@
         })
     }
 
+    fun logDozingChanged(isDozing: Boolean) {
+        buffer.log(TAG, INFO, {
+            bool1 = isDozing
+        }, {
+            "Dozing changed dozing=$bool1"
+        })
+    }
+
     fun logDozingSuppressed(isDozingSuppressed: Boolean) {
         buffer.log(TAG, INFO, {
             bool1 = isDozingSuppressed
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 292b0e2..fbfb919 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -109,11 +109,9 @@
     }
 
     @MainThread
-    private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
-        val enabled = device != null
-        val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
+    private fun processDevice(key: String, oldKey: String?, device: MediaDeviceData?) {
         listeners.forEach {
-            it.onMediaDeviceChanged(key, oldKey, data)
+            it.onMediaDeviceChanged(key, oldKey, device)
         }
     }
 
@@ -135,7 +133,7 @@
             get() = controller?.sessionToken
         private var started = false
         private var playbackType = PLAYBACK_TYPE_UNKNOWN
-        private var current: MediaDevice? = null
+        private var current: MediaDeviceData? = null
             set(value) {
                 if (!started || value != field) {
                     field = value
@@ -201,15 +199,13 @@
 
         @WorkerThread
         private fun updateCurrent() {
-            val device = localMediaManager.getCurrentConnectedDevice()
-            controller?.let {
-                val route = mr2manager.getRoutingSessionForMediaController(it)
-                // If we get a null route, then don't trust the device. Just set to null to disable the
-                // output switcher chip.
-                current = if (route != null) device else null
-            } ?: run {
-                current = device
-            }
+            val device = localMediaManager.currentConnectedDevice
+            val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it)}
+
+            // If we have a controller but get a null route, then don't trust the device
+            val enabled = device != null && (controller == null || route != null)
+            val name = route?.name?.toString() ?: device?.name
+            current = MediaDeviceData(enabled, device?.iconWithoutBackground, name)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 21c3e5e..7de4668 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -21,7 +21,6 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
@@ -92,10 +91,14 @@
     };
 
     @Inject
-    public DozeScrimController(DozeParameters dozeParameters, DozeLog dozeLog) {
+    public DozeScrimController(
+            DozeParameters dozeParameters,
+            DozeLog dozeLog,
+            StatusBarStateController statusBarStateController
+    ) {
         mDozeParameters = dozeParameters;
-        //Never expected to be destroyed
-        Dependency.get(StatusBarStateController.class).addCallback(this);
+        // Never expected to be destroyed
+        statusBarStateController.addCallback(this);
         mDozeLog = dozeLog;
     }
 
@@ -219,6 +222,10 @@
 
     @Override
     public void onDozingChanged(boolean isDozing) {
+        if (mDozing != isDozing) {
+            mDozeLog.traceDozingChanged(isDozing);
+        }
+
         setDozing(isDozing);
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index bf5a6e4..bbeadf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -122,7 +122,7 @@
 
     private lateinit var session: MediaSession
     private val device = MediaDeviceData(true, null, DEVICE_NAME)
-    private val disabledDevice = MediaDeviceData(false, null, null)
+    private val disabledDevice = MediaDeviceData(false, null, "Disabled Device")
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index ab3b208..7dadbad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -56,6 +56,7 @@
 private const val SESSION_KEY = "SESSION_KEY"
 private const val SESSION_TITLE = "SESSION_TITLE"
 private const val DEVICE_NAME = "DEVICE_NAME"
+private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME"
 private const val USER_ID = 0
 
 private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
@@ -195,8 +196,6 @@
         // THEN the device should be disabled
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
-        assertThat(data.name).isNull()
-        assertThat(data.icon).isNull()
     }
 
     @Test
@@ -263,6 +262,20 @@
     }
 
     @Test
+    fun deviceNameFromMR2RouteInfo() {
+        // GIVEN that MR2Manager returns a valid routing session
+        whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
+        // WHEN a notification is added
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        // THEN it uses the route name (instead of device name)
+        val data = captureDeviceData(KEY)
+        assertThat(data.enabled).isTrue()
+        assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+    }
+
+    @Test
     fun deviceDisabledWhenMR2ReturnsNullRouteInfo() {
         // GIVEN that MR2Manager returns null for routing session
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
@@ -273,8 +286,6 @@
         // THEN the device is disabled
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
-        assertThat(data.name).isNull()
-        assertThat(data.icon).isNull()
     }
 
     @Test
@@ -294,8 +305,6 @@
         // THEN the device is disabled
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
-        assertThat(data.name).isNull()
-        assertThat(data.icon).isNull()
     }
 
     @Test
@@ -315,8 +324,6 @@
         // THEN the device is disabled
         val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
-        assertThat(data.name).isNull()
-        assertThat(data.icon).isNull()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index 1ce336e..34c43ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -28,6 +28,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -44,12 +45,15 @@
     private DozeParameters mDozeParameters;
     @Mock
     private DozeLog mDozeLog;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
     private DozeScrimController mDozeScrimController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog);
+        mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog,
+                mStatusBarStateController);
         mDozeScrimController.setDozing(true);
     }
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 63301ac..ab220b5e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -47,6 +47,7 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SrvccState;
@@ -1999,42 +2000,8 @@
 
         ApnSetting apnSetting = preciseState.getApnSetting();
 
-        int apnTypes = apnSetting.getApnTypeBitmask();
-        int state = preciseState.getState();
-        int networkType = preciseState.getNetworkType();
-
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                // We only call the callback when the change is for default APN type.
-                if ((ApnSetting.TYPE_DEFAULT & apnTypes) != 0
-                        && (mDataConnectionState[phoneId] != state
-                        || mDataConnectionNetworkType[phoneId] != networkType)) {
-                    String str = "onDataConnectionStateChanged("
-                            + TelephonyUtils.dataStateToString(state)
-                            + ", " + getNetworkTypeName(networkType)
-                            + ") subId=" + subId + ", phoneId=" + phoneId;
-                    log(str);
-                    mLocalLog.log(str);
-                    for (Record r : mRecords) {
-                        if (r.matchTelephonyCallbackEvent(
-                                TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
-                                && idMatch(r, subId, phoneId)) {
-                            try {
-                                if (DBG) {
-                                    log("Notify data connection state changed on sub: " + subId);
-                                }
-                                r.callback.onDataConnectionStateChanged(state, networkType);
-                            } catch (RemoteException ex) {
-                                mRemoveList.add(r.binder);
-                            }
-                        }
-                    }
-                    handleRemoveListLocked();
-
-                    mDataConnectionState[phoneId] = state;
-                    mDataConnectionNetworkType[phoneId] = networkType;
-                }
-
                 Pair<Integer, ApnSetting> key = Pair.create(preciseState.getTransportType(),
                         preciseState.getApnSetting());
                 PreciseDataConnectionState oldState = mPreciseDataConnectionStates.get(phoneId)
@@ -2066,6 +2033,73 @@
                 if (preciseState.getState() != TelephonyManager.DATA_DISCONNECTED) {
                     mPreciseDataConnectionStates.get(phoneId).put(key, preciseState);
                 }
+
+                // Note that below is just the workaround for reporting the correct data connection
+                // state. The actual fix should be put in the new data stack in T.
+                // TODO: Remove the code below in T.
+
+                // Collect all possible candidate data connection state for internet. Key is the
+                // data connection state, value is the precise data connection state.
+                Map<Integer, PreciseDataConnectionState> internetConnections = new ArrayMap<>();
+                if (preciseState.getState() == TelephonyManager.DATA_DISCONNECTED
+                        && preciseState.getApnSetting().getApnTypes()
+                        .contains(ApnSetting.TYPE_DEFAULT)) {
+                    internetConnections.put(TelephonyManager.DATA_DISCONNECTED, preciseState);
+                }
+                for (Map.Entry<Pair<Integer, ApnSetting>, PreciseDataConnectionState> entry :
+                        mPreciseDataConnectionStates.get(phoneId).entrySet()) {
+                    if (entry.getKey().first == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+                            && entry.getKey().second.getApnTypes()
+                            .contains(ApnSetting.TYPE_DEFAULT)) {
+                        internetConnections.put(entry.getValue().getState(), entry.getValue());
+                    }
+                }
+
+                // If any internet data is in connected state, then report connected, then check
+                // suspended, connecting, disconnecting, and disconnected. The order is very
+                // important.
+                int[] statesInPriority = new int[]{TelephonyManager.DATA_CONNECTED,
+                        TelephonyManager.DATA_SUSPENDED, TelephonyManager.DATA_CONNECTING,
+                        TelephonyManager.DATA_DISCONNECTING,
+                        TelephonyManager.DATA_DISCONNECTED};
+                int state = TelephonyManager.DATA_DISCONNECTED;
+                int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+                for (int s : statesInPriority) {
+                    if (internetConnections.containsKey(s)) {
+                        state = s;
+                        networkType = internetConnections.get(s).getNetworkType();
+                        break;
+                    }
+                }
+
+                if (mDataConnectionState[phoneId] != state
+                        || mDataConnectionNetworkType[phoneId] != networkType) {
+                    String str = "onDataConnectionStateChanged("
+                            + TelephonyUtils.dataStateToString(state)
+                            + ", " + TelephonyManager.getNetworkTypeName(networkType)
+                            + ") subId=" + subId + ", phoneId=" + phoneId;
+                    log(str);
+                    mLocalLog.log(str);
+                    for (Record r : mRecords) {
+                        if (r.matchTelephonyCallbackEvent(
+                                TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
+                                && idMatch(r, subId, phoneId)) {
+                            try {
+                                if (DBG) {
+                                    log("Notify data connection state changed on sub: " + subId);
+                                }
+                                r.callback.onDataConnectionStateChanged(state, networkType);
+                            } catch (RemoteException ex) {
+                                mRemoveList.add(r.binder);
+                            }
+                        }
+                    }
+
+                    mDataConnectionState[phoneId] = state;
+                    mDataConnectionNetworkType[phoneId] = networkType;
+
+                    handleRemoveListLocked();
+                }
             }
         }
     }