Add audioserver state callback
Add system APIs for components (e.g rild) to monitor native audioserver
state and be able to reaply states after a native audioserver
crash and restart.
Bug: 67317552
Test: manual test.
Change-Id: I2afba9da5fc32b3768ca5ca0d5f97cc68707c408
diff --git a/Android.bp b/Android.bp
index 05fb3c0..03abf75 100644
--- a/Android.bp
+++ b/Android.bp
@@ -425,6 +425,7 @@
"media/java/android/media/IAudioFocusDispatcher.aidl",
"media/java/android/media/IAudioRoutesObserver.aidl",
"media/java/android/media/IAudioService.aidl",
+ "media/java/android/media/IAudioServerStateDispatcher.aidl",
"media/java/android/media/IMediaHTTPConnection.aidl",
"media/java/android/media/IMediaHTTPService.aidl",
"media/java/android/media/IMediaResourceMonitor.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index 3dbb333..3388fc8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2519,12 +2519,15 @@
public class AudioManager {
method public deprecated int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+ method public void clearAudioServerStateCallback();
method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
+ method public boolean isAudioServerRunning();
method public boolean isHdmiSystemAudioSupported();
method public int registerAudioPolicy(android.media.audiopolicy.AudioPolicy);
method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method public deprecated int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
method public int requestAudioFocus(android.media.AudioFocusRequest, android.media.audiopolicy.AudioPolicy);
+ method public void setAudioServerStateCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioServerStateCallback);
method public void setFocusRequestResult(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
method public void unregisterAudioPolicyAsync(android.media.audiopolicy.AudioPolicy);
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
@@ -2532,6 +2535,12 @@
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
}
+ public static abstract class AudioManager.AudioServerStateCallback {
+ ctor public AudioManager.AudioServerStateCallback();
+ method public void onAudioServerDown();
+ method public void onAudioServerUp();
+ }
+
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
method public int getClientPid();
method public int getClientUid();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 0be54ec..9ff964b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -65,6 +66,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
/**
* AudioManager provides access to volume and ringer mode control.
@@ -4864,6 +4867,114 @@
}
}
+
+ /**
+ * @hide
+ * Abstract class to receive event notification about audioserver process state.
+ */
+ @SystemApi
+ public abstract static class AudioServerStateCallback {
+ public void onAudioServerDown() { }
+ public void onAudioServerUp() { }
+ }
+
+ private Executor mAudioServerStateExec;
+ private AudioServerStateCallback mAudioServerStateCb;
+ private final Object mAudioServerStateCbLock = new Object();
+
+ private final IAudioServerStateDispatcher mAudioServerStateDispatcher =
+ new IAudioServerStateDispatcher.Stub() {
+ @Override
+ public void dispatchAudioServerStateChange(boolean state) {
+ Executor exec;
+ AudioServerStateCallback cb;
+
+ synchronized (mAudioServerStateCbLock) {
+ exec = mAudioServerStateExec;
+ cb = mAudioServerStateCb;
+ }
+
+ if ((exec == null) || (cb == null)) {
+ return;
+ }
+ if (state) {
+ exec.execute(() -> cb.onAudioServerUp());
+ } else {
+ exec.execute(() -> cb.onAudioServerDown());
+ }
+ }
+ };
+
+ /**
+ * @hide
+ * Registers a callback for notification of audio server state changes.
+ * @param executor {@link Executor} to handle the callbacks
+ * @param stateCallback the callback to receive the audio server state changes
+ * To remove the callabck, pass a null reference for both executor and stateCallback.
+ */
+ @SystemApi
+ public void setAudioServerStateCallback(@NonNull Executor executor,
+ @NonNull AudioServerStateCallback stateCallback) {
+ if (stateCallback == null) {
+ throw new IllegalArgumentException("Illegal null AudioServerStateCallback");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException(
+ "Illegal null Executor for the AudioServerStateCallback");
+ }
+
+ synchronized (mAudioServerStateCbLock) {
+ if (mAudioServerStateCb != null) {
+ throw new IllegalStateException(
+ "setAudioServerStateCallback called with already registered callabck");
+ }
+ final IAudioService service = getService();
+ try {
+ service.registerAudioServerStateDispatcher(mAudioServerStateDispatcher);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mAudioServerStateExec = executor;
+ mAudioServerStateCb = stateCallback;
+ }
+ }
+
+ /**
+ * @hide
+ * Unregisters the callback for notification of audio server state changes.
+ */
+ @SystemApi
+ public void clearAudioServerStateCallback() {
+ synchronized (mAudioServerStateCbLock) {
+ if (mAudioServerStateCb != null) {
+ final IAudioService service = getService();
+ try {
+ service.unregisterAudioServerStateDispatcher(
+ mAudioServerStateDispatcher);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ mAudioServerStateExec = null;
+ mAudioServerStateCb = null;
+ }
+ }
+
+ /**
+ * @hide
+ * Checks if native audioservice is running or not.
+ * @return true if native audioservice runs, false otherwise.
+ */
+ @SystemApi
+ public boolean isAudioServerRunning() {
+ final IAudioService service = getService();
+ try {
+ return service.isAudioServerRunning();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
//---------------------------------------------------------
// Inner classes
//--------------------
diff --git a/media/java/android/media/IAudioServerStateDispatcher.aidl b/media/java/android/media/IAudioServerStateDispatcher.aidl
new file mode 100644
index 0000000..2bc90ea
--- /dev/null
+++ b/media/java/android/media/IAudioServerStateDispatcher.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package android.media;
+
+/**
+ * AIDL for the AudioService to signal audio server state changes
+ *
+ * {@hide}
+ */
+oneway interface IAudioServerStateDispatcher {
+
+ void dispatchAudioServerStateChange(boolean state);
+
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 4c37014..05ba4c3 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -24,10 +24,12 @@
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
+import android.media.IAudioServerStateDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
+import android.media.IVolumeController;
import android.media.PlayerBase;
import android.media.VolumePolicy;
import android.media.audiopolicy.AudioPolicyConfig;
@@ -208,6 +210,12 @@
oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
in IAudioPolicyCallback pcb);
+ void registerAudioServerStateDispatcher(IAudioServerStateDispatcher asd);
+
+ oneway void unregisterAudioServerStateDispatcher(IAudioServerStateDispatcher asd);
+
+ boolean isAudioServerRunning();
+
// WARNING: read warning at top of file, new methods that need to be used by native
// code via IAudioManager.h need to be added to the top section.
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e95608f..fffe7dc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -70,6 +70,7 @@
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
+import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
@@ -242,6 +243,7 @@
private static final int MSG_INDICATE_SYSTEM_READY = 26;
private static final int MSG_ACCESSORY_PLUG_MEDIA_UNMUTE = 27;
private static final int MSG_NOTIFY_VOL_EVENT = 28;
+ private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 29;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -390,6 +392,8 @@
case AudioSystem.AUDIO_STATUS_SERVER_DIED:
sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED,
SENDMSG_NOOP, 0, 0, null, 0);
+ sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
+ SENDMSG_QUEUE, 0, 0, null, 0);
break;
default:
break;
@@ -1000,6 +1004,21 @@
onIndicateSystemReady();
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
+
+ sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
+ SENDMSG_QUEUE, 1, 0, null, 0);
+ }
+
+ private void onDispatchAudioServerStateChange(boolean state) {
+ synchronized (mAudioServerStateListeners) {
+ for (AsdProxy asdp : mAudioServerStateListeners.values()) {
+ try {
+ asdp.callback().dispatchAudioServerStateChange(state);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not call dispatchAudioServerStateChange()", e);
+ }
+ }
+ }
}
private void createAudioSystemThread() {
@@ -5089,6 +5108,10 @@
onAudioServerDied();
break;
+ case MSG_DISPATCH_AUDIO_SERVER_STATE:
+ onDispatchAudioServerStateChange(msg.arg1 == 1);
+ break;
+
case MSG_UNLOAD_SOUND_EFFECTS:
onUnloadSoundEffects();
break;
@@ -7341,6 +7364,77 @@
//======================
+ // Audioserver state displatch
+ //======================
+ private class AsdProxy implements IBinder.DeathRecipient {
+ private final IAudioServerStateDispatcher mAsd;
+
+ AsdProxy(IAudioServerStateDispatcher asd) {
+ mAsd = asd;
+ }
+
+ public void binderDied() {
+ synchronized (mAudioServerStateListeners) {
+ mAudioServerStateListeners.remove(mAsd.asBinder());
+ }
+ }
+
+ IAudioServerStateDispatcher callback() {
+ return mAsd;
+ }
+ }
+
+ private HashMap<IBinder, AsdProxy> mAudioServerStateListeners =
+ new HashMap<IBinder, AsdProxy>();
+
+ private void checkMonitorAudioServerStatePermission() {
+ if (!(mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE) ==
+ PackageManager.PERMISSION_GRANTED ||
+ mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING) ==
+ PackageManager.PERMISSION_GRANTED)) {
+ throw new SecurityException("Not allowed to monitor audioserver state");
+ }
+ }
+
+ public void registerAudioServerStateDispatcher(IAudioServerStateDispatcher asd) {
+ checkMonitorAudioServerStatePermission();
+ synchronized (mAudioServerStateListeners) {
+ if (mAudioServerStateListeners.containsKey(asd.asBinder())) {
+ Slog.w(TAG, "Cannot re-register audio server state dispatcher");
+ return;
+ }
+ AsdProxy asdp = new AsdProxy(asd);
+ try {
+ asd.asBinder().linkToDeath(asdp, 0/*flags*/);
+ } catch (RemoteException e) {
+
+ }
+ mAudioServerStateListeners.put(asd.asBinder(), asdp);
+ }
+ }
+
+ public void unregisterAudioServerStateDispatcher(IAudioServerStateDispatcher asd) {
+ checkMonitorAudioServerStatePermission();
+ synchronized (mAudioServerStateListeners) {
+ AsdProxy asdp = mAudioServerStateListeners.remove(asd.asBinder());
+ if (asdp == null) {
+ Slog.w(TAG, "Trying to unregister unknown audioserver state dispatcher for pid "
+ + Binder.getCallingPid() + " / uid " + Binder.getCallingUid());
+ return;
+ } else {
+ asd.asBinder().unlinkToDeath(asdp, 0/*flags*/);
+ }
+ }
+ }
+
+ public boolean isAudioServerRunning() {
+ checkMonitorAudioServerStatePermission();
+ return (AudioSystem.checkAudioFlinger() == AudioSystem.AUDIO_STATUS_OK);
+ }
+
+ //======================
// misc
//======================
private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =