Reapply "AudioService: synchronize audio mode and focus for Telecom"

Reland commit 15f9e56e with fixes to possible leaks in audio mode reset counter.
 - Use separate message types to avoid overwritting the sender's signal
request if another message with REPLACE policy is sent.
 - Reset the counter systematcially in case of timeout to avoid sticky
 error conditons.
 - Reset the counter in case of error processing AudioSystem.setPhoneState() in
   onUpdateAudioMode().

Also similarly reset the communication device update count in case of
timeout in AudioDeviceBroker.setCommunicationDevice().

This reverts commit 38818052dc43a4322d2ae0e113e6544175c198b0.

Bug: 375018522
Bug: 350415723
Test: make
Flag: EXEMPT bug fix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:94a9fc5800397efeccdc87177033e7a4a1386ca0)
Merged-In: Iacd348b6da01af08796b26232f94d9177a4a591b
Change-Id: Iacd348b6da01af08796b26232f94d9177a4a591b
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 0fd22c5..cdfb740 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -679,6 +679,8 @@
                 elapsed = System.currentTimeMillis() - start;
                 if (elapsed >= SET_COMMUNICATION_DEVICE_TIMEOUT_MS) {
                     Log.e(TAG, "Timeout waiting for communication device update.");
+                    // reset counter to avoid sticky out of sync condition
+                    mCommunicationDeviceUpdateCount = 0;
                     break;
                 }
             }
@@ -1321,9 +1323,9 @@
         sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info);
     }
 
-    /*package*/ void postSetModeOwner(int mode, int pid, int uid) {
-        sendLMsgNoDelay(MSG_I_SET_MODE_OWNER, SENDMSG_REPLACE,
-                new AudioModeInfo(mode, pid, uid));
+    /*package*/ void postSetModeOwner(int mode, int pid, int uid, boolean signal) {
+        sendLMsgNoDelay(signal ? MSG_L_SET_MODE_OWNER_SIGNAL : MSG_L_SET_MODE_OWNER,
+                SENDMSG_REPLACE, new AudioModeInfo(mode, pid, uid));
     }
 
     /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
@@ -2025,7 +2027,8 @@
                         mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
                     }
                     break;
-                case MSG_I_SET_MODE_OWNER:
+                case MSG_L_SET_MODE_OWNER:
+                case MSG_L_SET_MODE_OWNER_SIGNAL:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
                             int btScoRequesterUid = bluetoothScoRequestOwnerUid();
@@ -2036,6 +2039,9 @@
                             }
                         }
                     }
+                    if (msg.what == MSG_L_SET_MODE_OWNER_SIGNAL) {
+                        mAudioService.decrementAudioModeResetCount();
+                    }
                     break;
 
                 case MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT:
@@ -2224,7 +2230,8 @@
     private static final int MSG_REPORT_NEW_ROUTES = 13;
     private static final int MSG_II_SET_HEARING_AID_VOLUME = 14;
     private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
-    private static final int MSG_I_SET_MODE_OWNER = 16;
+    private static final int MSG_L_SET_MODE_OWNER = 16;
+    private static final int MSG_L_SET_MODE_OWNER_SIGNAL = 17;
 
     private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22;
     private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e1909d9..8abe121 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -455,7 +455,7 @@
     private static final int MSG_UPDATE_AUDIO_MODE = 36;
     private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
     private static final int MSG_BT_DEV_CHANGED = 38;
-
+    private static final int MSG_UPDATE_AUDIO_MODE_SIGNAL = 39;
     private static final int MSG_DISPATCH_AUDIO_MODE = 40;
     private static final int MSG_ROUTING_UPDATED = 41;
     private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
@@ -1918,7 +1918,7 @@
         // Restore call state
         synchronized (mDeviceBroker.mSetModeLock) {
             onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
-                    mContext.getPackageName(), true /*force*/);
+                    mContext.getPackageName(), true /*force*/, false /*signal*/);
         }
         final int forSys;
         synchronized (mSettingsLock) {
@@ -4746,17 +4746,45 @@
                 }
             }
             if (updateAudioMode) {
-                sendMsg(mAudioHandler,
-                        MSG_UPDATE_AUDIO_MODE,
-                        existingMsgPolicy,
-                        AudioSystem.MODE_CURRENT,
-                        android.os.Process.myPid(),
-                        mContext.getPackageName(),
-                        delay);
+                postUpdateAudioMode(existingMsgPolicy, AudioSystem.MODE_CURRENT,
+                        android.os.Process.myPid(), mContext.getPackageName(),
+                        false /*signal*/, delay);
             }
         }
     }
 
+    static class UpdateAudioModeInfo {
+        UpdateAudioModeInfo(int mode, int pid, String packageName) {
+            mMode = mode;
+            mPid = pid;
+            mPackageName = packageName;
+        }
+        private final int mMode;
+        private final int mPid;
+        private final String mPackageName;
+
+        int getMode() {
+            return mMode;
+        }
+        int getPid() {
+            return mPid;
+        }
+        String getPackageName() {
+            return mPackageName;
+        }
+    }
+
+    void postUpdateAudioMode(int msgPolicy, int mode, int pid, String packageName,
+            boolean signal, int delay) {
+        synchronized (mAudioModeResetLock) {
+            if (signal) {
+                mAudioModeResetCount++;
+            }
+            sendMsg(mAudioHandler, signal ? MSG_UPDATE_AUDIO_MODE_SIGNAL : MSG_UPDATE_AUDIO_MODE,
+                    msgPolicy, 0, 0, new UpdateAudioModeInfo(mode, pid, packageName), delay);
+        }
+    }
+
     private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor =
             new IRecordingConfigDispatcher.Stub() {
         @Override
@@ -6155,13 +6183,9 @@
                 } else {
                     SetModeDeathHandler h = mSetModeDeathHandlers.get(index);
                     mSetModeDeathHandlers.remove(index);
-                    sendMsg(mAudioHandler,
-                            MSG_UPDATE_AUDIO_MODE,
-                            SENDMSG_QUEUE,
-                            AudioSystem.MODE_CURRENT,
-                            android.os.Process.myPid(),
-                            mContext.getPackageName(),
-                            0);
+                    postUpdateAudioMode(SENDMSG_QUEUE, AudioSystem.MODE_CURRENT,
+                            android.os.Process.myPid(), mContext.getPackageName(),
+                            false /*signal*/, 0);
                 }
             }
         }
@@ -6407,19 +6431,14 @@
                 }
             }
 
-            sendMsg(mAudioHandler,
-                    MSG_UPDATE_AUDIO_MODE,
-                    SENDMSG_REPLACE,
-                    mode,
-                    pid,
-                    callingPackage,
-                    0);
+            postUpdateAudioMode(SENDMSG_REPLACE, mode, pid, callingPackage,
+                    hasModifyPhoneStatePermission && mode == AudioSystem.MODE_NORMAL, 0);
         }
     }
 
     @GuardedBy("mDeviceBroker.mSetModeLock")
     void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage,
-                           boolean force) {
+                           boolean force, boolean signal) {
         if (requestedMode == AudioSystem.MODE_CURRENT) {
             requestedMode = getMode();
         }
@@ -6434,7 +6453,7 @@
         }
         if (DEBUG_MODE) {
             Log.v(TAG, "onUpdateAudioMode() new mode: " + mode + ", current mode: "
-                    + mMode.get() + " requested mode: " + requestedMode);
+                    + mMode.get() + " requested mode: " + requestedMode + " signal: " + signal);
         }
         if (mode != mMode.get() || force) {
             int status = AudioSystem.SUCCESS;
@@ -6480,8 +6499,11 @@
 
                 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
                 // connections not started by the application changing the mode when pid changes
-                mDeviceBroker.postSetModeOwner(mode, pid, uid);
+                mDeviceBroker.postSetModeOwner(mode, pid, uid, signal);
             } else {
+                // reset here to avoid sticky out of sync condition (would have been reset
+                // by AudioDeviceBroker processing MSG_L_SET_MODE_OWNER_SIGNAL message)
+                resetAudioModeResetCount();
                 Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
             }
         }
@@ -10162,7 +10184,7 @@
                         h.setRecordingActive(isRecordingActiveForUid(h.getUid()));
                         if (wasActive != h.isActive()) {
                             onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
-                                    mContext.getPackageName(), false /*force*/);
+                                    mContext.getPackageName(), false /*force*/, false /*signal*/);
                         }
                     }
                     break;
@@ -10191,8 +10213,11 @@
                     break;
 
                 case MSG_UPDATE_AUDIO_MODE:
+                case MSG_UPDATE_AUDIO_MODE_SIGNAL:
                     synchronized (mDeviceBroker.mSetModeLock) {
-                        onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj, false /*force*/);
+                        UpdateAudioModeInfo info = (UpdateAudioModeInfo) msg.obj;
+                        onUpdateAudioMode(info.getMode(), info.getPid(), info.getPackageName(),
+                                false /*force*/, msg.what == MSG_UPDATE_AUDIO_MODE_SIGNAL);
                     }
                     break;
 
@@ -10895,9 +10920,68 @@
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
         mmi.record();
+        //delay abandon focus requests from Telecom if an audio mode reset from Telecom
+        // is still being processed
+        final boolean abandonFromTelecom = (mContext.checkCallingOrSelfPermission(
+                    MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)
+                && ((aa != null && aa.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION)
+                        || AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId));
+        if (abandonFromTelecom) {
+            synchronized (mAudioModeResetLock) {
+                final long start = java.lang.System.currentTimeMillis();
+                long elapsed = 0;
+                while (mAudioModeResetCount > 0) {
+                    if (DEBUG_MODE) {
+                        Log.i(TAG, "Abandon focus from Telecom, waiting for mode change");
+                    }
+                    try {
+                        mAudioModeResetLock.wait(
+                                AUDIO_MODE_RESET_TIMEOUT_MS - elapsed);
+                    } catch (InterruptedException e) {
+                        Log.w(TAG, "Interrupted while waiting for audio mode reset");
+                    }
+                    elapsed = java.lang.System.currentTimeMillis() - start;
+                    if (elapsed >= AUDIO_MODE_RESET_TIMEOUT_MS) {
+                        Log.e(TAG, "Timeout waiting for audio mode reset");
+                        // reset count to avoid sticky out of sync state.
+                        resetAudioModeResetCount();
+                        break;
+                    }
+                }
+                if (DEBUG_MODE && elapsed != 0) {
+                    Log.i(TAG, "Abandon focus from Telecom done waiting");
+                }
+            }
+        }
         return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
     }
 
+    /** synchronization between setMode(NORMAL) and abandonAudioFocus() from Telecom */
+    private static final long AUDIO_MODE_RESET_TIMEOUT_MS = 3000;
+
+    private final Object mAudioModeResetLock = new Object();
+
+    @GuardedBy("mAudioModeResetLock")
+    private int mAudioModeResetCount = 0;
+
+    void decrementAudioModeResetCount() {
+        synchronized (mAudioModeResetLock) {
+            if (mAudioModeResetCount > 0) {
+                mAudioModeResetCount--;
+            } else {
+                Log.w(TAG, "mAudioModeResetCount already 0");
+            }
+            mAudioModeResetLock.notify();
+        }
+    }
+
+    private void resetAudioModeResetCount() {
+        synchronized (mAudioModeResetLock) {
+            mAudioModeResetCount = 0;
+            mAudioModeResetLock.notify();
+        }
+    }
+
     /** see {@link AudioManager#abandonAudioFocusForTest(AudioFocusRequest, String)} */
     public int abandonAudioFocusForTest(IAudioFocusDispatcher fd, String clientId,
             AudioAttributes aa, String callingPackageName) {