Snap for 12763142 from 2b4afe184102aa658d23547704d738a2e11aec4e to 25Q1-release

Change-Id: Ib83bf7667c8fe0fb682e576435f57813414c9fa6
diff --git a/flags/telecom_anomaly_report_flags.aconfig b/flags/telecom_anomaly_report_flags.aconfig
index b060ed0..5d42b86 100644
--- a/flags/telecom_anomaly_report_flags.aconfig
+++ b/flags/telecom_anomaly_report_flags.aconfig
@@ -16,3 +16,14 @@
   description: "If a self-managed call is stuck in certain states, disconnect it"
   bug: "360298368"
 }
+
+# OWNER=tgunn TARGET=25Q2
+flag {
+  name: "dont_timeout_destroyed_calls"
+  namespace: "telecom"
+  description: "When create connection timeout is hit, if call is already destroyed, skip anomaly"
+  bug: "381684580"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/flags/telecom_bluetoothdevicemanager_flags.aconfig b/flags/telecom_bluetoothdevicemanager_flags.aconfig
index 4c91491..9b1b53e 100644
--- a/flags/telecom_bluetoothdevicemanager_flags.aconfig
+++ b/flags/telecom_bluetoothdevicemanager_flags.aconfig
@@ -8,3 +8,13 @@
   description: "Fix for Log.wtf in the BinderProxy"
   bug: "333417369"
 }
+# OWNER=huiwang TARGET=25Q1
+flag {
+  name: "keep_bt_devices_cache_updated"
+  namespace: "telecom"
+  description: "Fix the devices cache issue of BluetoothDeviceManager"
+  bug: "380320985"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/flags/telecom_ringer_flag_declarations.aconfig b/flags/telecom_ringer_flag_declarations.aconfig
index 6517e0f..f954b09 100644
--- a/flags/telecom_ringer_flag_declarations.aconfig
+++ b/flags/telecom_ringer_flag_declarations.aconfig
@@ -15,4 +15,16 @@
   namespace: "telecom"
   description: "Gates whether to ensure that when a user is in their car, they are able to hear ringing for an incoming call."
   bug: "348708398"
+}
+
+
+# OWNER=tjstuart TARGET=25Q1
+flag {
+  name: "get_ringer_mode_anom_report"
+  namespace: "telecom"
+  description: "getRingerMode & getRingerModeInternal should return the same val when dnd is off"
+  bug: "307389562"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 9670d6a..22b28b5 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -703,7 +703,7 @@
                 ringtoneFactory, systemVibrator,
                 new Ringer.VibrationEffectProxy(), mInCallController,
                 mContext.getSystemService(NotificationManager.class),
-                accessibilityManagerAdapter, featureFlags);
+                accessibilityManagerAdapter, featureFlags, mAnomalyReporter);
         if (featureFlags.telecomResolveHiddenDependencies()) {
             // This is now deprecated
             mCallRecordingTonePlayer = null;
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 14c8f62..260c238 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -130,11 +130,7 @@
                 synchronized (mLock) {
                     logIncoming("handleCreateConnectionComplete %s", callId);
                     Call call = mCallIdMapper.getCall(callId);
-                    if (call != null && mScheduledFutureMap.containsKey(call)) {
-                        ScheduledFuture<?> existingTimeout = mScheduledFutureMap.get(call);
-                        existingTimeout.cancel(false /* cancelIfRunning */);
-                        mScheduledFutureMap.remove(call);
-                    }
+                    maybeRemoveCleanupFuture(call);
                     // Check status hints image for cross user access
                     if (connection.getStatusHints() != null) {
                         Icon icon = connection.getStatusHints().getIcon();
@@ -174,11 +170,7 @@
                 synchronized (mLock) {
                     logIncoming("handleCreateConferenceComplete %s", callId);
                     Call call = mCallIdMapper.getCall(callId);
-                    if (call != null && mScheduledFutureMap.containsKey(call)) {
-                        ScheduledFuture<?> existingTimeout = mScheduledFutureMap.get(call);
-                        existingTimeout.cancel(false /* cancelIfRunning */);
-                        mScheduledFutureMap.remove(call);
-                    }
+                    maybeRemoveCleanupFuture(call);
                     // Check status hints image for cross user access
                     if (conference.getStatusHints() != null) {
                         Icon icon = conference.getStatusHints().getIcon();
@@ -1678,6 +1670,9 @@
                             Log.getExternalSession(TELECOM_ABBREVIATION));
                 } catch (RemoteException e) {
                     Log.e(this, e, "Failure to createConference -- %s", getComponentName());
+                    if (mFlags.dontTimeoutDestroyedCalls()) {
+                        maybeRemoveCleanupFuture(call);
+                    }
                     mPendingResponses.remove(callId).handleCreateConferenceFailure(
                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
                 }
@@ -1708,6 +1703,9 @@
                     Log.i(ConnectionServiceWrapper.this, "Call not present"
                             + " in call id mapper, maybe it was aborted before the bind"
                             + " completed successfully?");
+                    if (mFlags.dontTimeoutDestroyedCalls()) {
+                        maybeRemoveCleanupFuture(call);
+                    }
                     response.handleCreateConnectionFailure(
                             new DisconnectCause(DisconnectCause.CANCELED));
                     return;
@@ -1793,6 +1791,9 @@
                 mScheduledFutureMap.put(call, future);
                 try {
                     if (mFlags.cswServiceInterfaceIsNull() && mServiceInterface == null) {
+                        if (mFlags.dontTimeoutDestroyedCalls()) {
+                            maybeRemoveCleanupFuture(call);
+                        }
                         mPendingResponses.remove(callId).handleCreateConnectionFailure(
                                 new DisconnectCause(DisconnectCause.ERROR,
                                         "CSW#oCC ServiceInterface is null"));
@@ -1807,6 +1808,9 @@
                     }
                 } catch (RemoteException e) {
                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
+                    if (mFlags.dontTimeoutDestroyedCalls()) {
+                        maybeRemoveCleanupFuture(call);
+                    }
                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
                 }
@@ -2286,6 +2290,9 @@
         if (response != null) {
             response.handleCreateConnectionFailure(disconnectCause);
         }
+        if (mFlags.dontTimeoutDestroyedCalls()) {
+            maybeRemoveCleanupFuture(mCallIdMapper.getCall(callId));
+        }
 
         mCallIdMapper.removeCall(callId);
     }
@@ -2295,6 +2302,9 @@
         if (response != null) {
             response.handleCreateConnectionFailure(disconnectCause);
         }
+        if (mFlags.dontTimeoutDestroyedCalls()) {
+            maybeRemoveCleanupFuture(call);
+        }
 
         mCallIdMapper.removeCall(call);
     }
@@ -2754,4 +2764,20 @@
     public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
         mAnomalyReporter = mAnomalyReporterAdapter;
     }
+
+    /**
+     * Given a call, unschedule and cancel the cleanup future.
+     * @param call the call.
+     */
+    private void maybeRemoveCleanupFuture(Call call) {
+        if (call == null) {
+            return;
+        }
+        ScheduledFuture<?> future = mScheduledFutureMap.remove(call);
+        if (future == null) {
+            return;
+        }
+        future.cancel(false /* interrupt */);
+
+    }
 }
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 12778b0..bfaadf0 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -59,6 +59,7 @@
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -176,6 +177,11 @@
 
     private static VolumeShaper.Configuration mVolumeShaperConfig;
 
+    public static final UUID GET_RINGER_MODE_ANOMALY_UUID =
+            UUID.fromString("eb10505b-4d7b-4fab-b4a1-a18186799065");
+    public static final String GET_RINGER_MODE_ANOMALY_MSG = "AM#GetRingerMode() and"
+            + " AM#GetRingerModeInternal() are returning diff values when DoNotDisturb is OFF!";
+
     /**
      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
@@ -191,6 +197,8 @@
     private final boolean mIsHapticPlaybackSupportedByDevice;
     private final FeatureFlags mFlags;
     private final boolean mRingtoneVibrationSupported;
+    private final AnomalyReporterAdapter mAnomalyReporter;
+
     /**
      * For unit testing purposes only; when set, {@link #startRinging(Call, boolean)} will complete
      * the future provided by the test using {@link #setBlockOnRingingFuture(CompletableFuture)}.
@@ -237,7 +245,8 @@
             InCallController inCallController,
             NotificationManager notificationManager,
             AccessibilityManagerAdapter accessibilityManagerAdapter,
-            FeatureFlags featureFlags) {
+            FeatureFlags featureFlags,
+            AnomalyReporterAdapter anomalyReporter) {
 
         mLock = new Object();
         mSystemSettingsUtil = systemSettingsUtil;
@@ -252,6 +261,7 @@
         mVibrationEffectProxy = vibrationEffectProxy;
         mNotificationManager = notificationManager;
         mAccessibilityManagerAdapter = accessibilityManagerAdapter;
+        mAnomalyReporter = anomalyReporter;
 
         mDefaultVibrationEffect =
                 loadDefaultRingVibrationEffect(
@@ -405,10 +415,9 @@
                     // If ringer is not audible for this call, then the phone is in "Vibrate" mode.
                     // Use haptic-only ringtone or do not play anything.
                     isHapticOnly = true;
-                    if (DEBUG_RINGER) {
-                        Log.i(this, "Set ringtone as haptic only: " + isHapticOnly);
-                    }
+                    Log.i(this, "Set ringtone as haptic only: " + isHapticOnly);
                 } else {
+                    Log.i(this, "ringer & haptics are off, user missed alerts for call");
                     foregroundCall.setUserMissed(USER_MISSED_NO_VIBRATE);
                     Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION,
                             vibratorAttrs);
@@ -437,7 +446,7 @@
                 ringtoneInfoSupplier = () -> mRingtoneFactory.getRingtone(
                         foregroundCall, null, false);
             }
-
+            Log.i(this, "isRingtoneInfoSupplierNull=[%b]", ringtoneInfoSupplier == null);
             // If vibration will be done, reserve the vibrator.
             boolean vibratorReserved = isVibratorEnabled && attributes.shouldRingForContact()
                 && tryReserveVibration(foregroundCall);
@@ -706,12 +715,43 @@
         // AudioManager#getRingerModeInternal which only useful for volume controllers
         boolean zenModeOn = mNotificationManager != null
                 && mNotificationManager.getZenMode() != ZEN_MODE_OFF;
+        maybeGenAnomReportForGetRingerMode(zenModeOn, audioManager);
         return mVibrator.hasVibrator()
                 && mSystemSettingsUtil.isRingVibrationEnabled(context)
                 && (audioManager.getRingerMode() != AudioManager.RINGER_MODE_SILENT
                 || (zenModeOn && shouldRingForContact));
     }
 
+    /**
+     * There are 3 settings for haptics:
+     * - AudioManager.RINGER_MODE_SILENT
+     * - AudioManager.RINGER_MODE_VIBRATE
+     * - AudioManager.RINGER_MODE_NORMAL
+     * If the user does not have {@link AudioManager#RINGER_MODE_SILENT} set, the user should
+     * have haptic feeback
+     *
+     * Note: If DND/ZEN_MODE is on, {@link AudioManager#getRingerMode()} will return
+     * {@link AudioManager#RINGER_MODE_SILENT}, regardless of the user setting. Therefore,
+     * getRingerModeInternal is the source of truth instead of {@link AudioManager#getRingerMode()}.
+     * However, if DND/ZEN_MOD is off, the APIs should return the same value.  Generate an anomaly
+     * report if they diverge.
+     */
+    private void maybeGenAnomReportForGetRingerMode(boolean isZenModeOn, AudioManager am) {
+        if (!mFlags.getRingerModeAnomReport()) {
+            return;
+        }
+        if (!isZenModeOn) {
+            int ringerMode = am.getRingerMode();
+            int ringerModeInternal = am.getRingerModeInternal();
+            if (ringerMode != ringerModeInternal) {
+                Log.i(this, "getRingerMode=[%d], getRingerModeInternal=[%d]",
+                        ringerMode, ringerModeInternal);
+                mAnomalyReporter.reportAnomaly(GET_RINGER_MODE_ANOMALY_UUID,
+                        GET_RINGER_MODE_ANOMALY_MSG);
+            }
+        }
+    }
+
     private RingerAttributes getRingerAttributes(Call call, boolean isHfpDeviceAttached) {
         mAudioManager = mContext.getSystemService(AudioManager.class);
         RingerAttributes.Builder builder = new RingerAttributes.Builder();
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 4f0aa89..dbc858b 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -519,7 +519,10 @@
                 Log.i(this, "onDeviceConnected: Adding device with address: %s and devicetype=%s",
                         device, getDeviceTypeString(deviceType));
                 targetDeviceMap.put(device.getAddress(), device);
-                mBluetoothRouteManager.onDeviceAdded(device.getAddress());
+                if (!mFeatureFlags.keepBtDevicesCacheUpdated()
+                        || !mFeatureFlags.useRefactoredAudioRouteSwitching()) {
+                    mBluetoothRouteManager.onDeviceAdded(device.getAddress());
+                }
             }
         }
     }
@@ -551,7 +554,10 @@
                 Log.i(this, "onDeviceDisconnected: Removing device with address: %s, devicetype=%s",
                         device, getDeviceTypeString(deviceType));
                 targetDeviceMap.remove(device.getAddress());
-                mBluetoothRouteManager.onDeviceLost(device.getAddress());
+                if (!mFeatureFlags.keepBtDevicesCacheUpdated()
+                        || !mFeatureFlags.useRefactoredAudioRouteSwitching()) {
+                    mBluetoothRouteManager.onDeviceLost(device.getAddress());
+                }
             }
         }
     }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 7fe8246..679db67 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -211,6 +211,9 @@
             if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_DEVICE_ADDED,
                         audioRouteType, device);
+                if (mFeatureFlags.keepBtDevicesCacheUpdated()) {
+                    mBluetoothDeviceManager.onDeviceConnected(device, deviceType);
+                }
             } else {
                 mBluetoothDeviceManager.onDeviceConnected(device, deviceType);
             }
@@ -219,6 +222,9 @@
             if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_DEVICE_REMOVED,
                         audioRouteType, device);
+                if (mFeatureFlags.keepBtDevicesCacheUpdated()) {
+                    mBluetoothDeviceManager.onDeviceDisconnected(device, deviceType);
+                }
             } else {
                 mBluetoothDeviceManager.onDeviceDisconnected(device, deviceType);
             }
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index c4d9678..46916fd 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -66,6 +66,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.telecom.AnomalyReporterAdapter;
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallState;
@@ -123,6 +124,7 @@
     @Mock NotificationManager mockNotificationManager;
     @Mock Ringer.AccessibilityManagerAdapter mockAccessibilityManagerAdapter;
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
 
     @Spy Ringer.VibrationEffectProxy spyVibrationEffectProxy;
 
@@ -178,7 +180,7 @@
         mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
                 asyncRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
                 mockInCallController, mockNotificationManager, mockAccessibilityManagerAdapter,
-                mFeatureFlags);
+                mFeatureFlags, mAnomalyReporterAdapter);
         // This future is used to wait for AsyncRingtonePlayer to finish its part.
         mRingerUnderTest.setBlockOnRingingFuture(mRingCompletionFuture);
     }