Register for capture state notifications via JNI
This change replaces the direct binder connection of the sound trigger
middleware service to the audio policy service with going through
AudioSystem.h through JNI.
Bug: 146157104
Test: Manual verification of proper behavior when starting/stopping
recording, killing of audio server, by examination of the logs.
Merged-In: I9d9a61653e968b63e3de36c0cc7f5925e06204c6
Change-Id: Idde6179580dc5b8eb638a74e7f9b44707d6ca179
This commit is contained in:
@@ -36,40 +36,23 @@ import java.util.function.Consumer;
|
||||
* For simplicity, there is currently no way to stop the tracker. This is possible to add if the
|
||||
* need ever arises.
|
||||
*/
|
||||
public class ExternalCaptureStateTracker {
|
||||
class ExternalCaptureStateTracker {
|
||||
private static final String TAG = "CaptureStateTracker";
|
||||
/** Our client's listener. */
|
||||
private final Consumer<Boolean> mListener;
|
||||
/** A lock used to ensure synchronized access to mListener. */
|
||||
private final Object mListenerLock = new Object();
|
||||
/**
|
||||
* The binder listener that we're providing to the audio policy service. Ensures synchronized
|
||||
* access to mListener.
|
||||
*/
|
||||
private final Listener mSyncListener = new Listener();
|
||||
/** The name of the audio policy service. */
|
||||
private final String mAudioPolicyServiceName;
|
||||
/** This semaphore will get a permit every time we need to reconnect. */
|
||||
private final Semaphore mNeedToConnect = new Semaphore(1);
|
||||
/**
|
||||
* We must hold a reference to the APM service, even though we're not actually using it after
|
||||
* installing the callback. Otherwise, binder silently un-links our death listener.
|
||||
*/
|
||||
private IBinder mService;
|
||||
|
||||
/**
|
||||
* Constructor. Will start a background thread to do the work.
|
||||
*
|
||||
* @param audioPolicyServiceName The name of the audio policy service to connect to.
|
||||
* @param listener A client provided listener that will be called on state
|
||||
* changes. May be
|
||||
* called multiple consecutive times with the same value. Never
|
||||
* called
|
||||
* concurrently.
|
||||
* @param listener A client provided listener that will be called on state
|
||||
* changes. May be
|
||||
* called multiple consecutive times with the same value. Never
|
||||
* called
|
||||
* concurrently.
|
||||
*/
|
||||
public ExternalCaptureStateTracker(String audioPolicyServiceName,
|
||||
Consumer<Boolean> listener) {
|
||||
mAudioPolicyServiceName = audioPolicyServiceName;
|
||||
ExternalCaptureStateTracker(Consumer<Boolean> listener) {
|
||||
mListener = listener;
|
||||
new Thread(this::run).start();
|
||||
}
|
||||
@@ -80,78 +63,29 @@ public class ExternalCaptureStateTracker {
|
||||
private void run() {
|
||||
while (true) {
|
||||
mNeedToConnect.acquireUninterruptibly();
|
||||
connectWithRetry();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect. Retry in case of RemoteException.
|
||||
*/
|
||||
private void connectWithRetry() {
|
||||
while (true) {
|
||||
try {
|
||||
connect();
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Exception caught trying to connect", e);
|
||||
} catch (ServiceManager.ServiceNotFoundException e) {
|
||||
Log.w(TAG, "Service not yet available, waiting", e);
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unexpected exception caught trying to connect", e);
|
||||
throw e;
|
||||
}
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the service, install listener and death notifier.
|
||||
*
|
||||
* @throws RemoteException In case of a binder issue.
|
||||
*/
|
||||
private void connect() throws RemoteException, ServiceManager.ServiceNotFoundException {
|
||||
Log.d(TAG, "Connecting to audio policy service: " + mAudioPolicyServiceName);
|
||||
mService = ServiceManager.getServiceOrThrow(mAudioPolicyServiceName);
|
||||
private native void connect();
|
||||
|
||||
synchronized (mListenerLock) {
|
||||
boolean active = registerSoundTriggerCaptureStateListener(mService, mSyncListener);
|
||||
mListener.accept(active);
|
||||
}
|
||||
|
||||
mService.linkToDeath(() -> {
|
||||
Log.w(TAG, "Audio policy service died");
|
||||
mNeedToConnect.release();
|
||||
}, 0);
|
||||
/**
|
||||
* Called by native code to invoke the client listener.
|
||||
*
|
||||
* @param active true when external capture is active.
|
||||
*/
|
||||
private void setCaptureState(boolean active) {
|
||||
mListener.accept(active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the audio policy service does not have an AIDL interface, this method does the
|
||||
* necessary manual marshalling.
|
||||
*
|
||||
* @param service The service binder object.
|
||||
* @param listener The listener binder object to register.
|
||||
* @return The active state at the time of registration.
|
||||
* Called by native code when the remote service died.
|
||||
*/
|
||||
private boolean registerSoundTriggerCaptureStateListener(IBinder service,
|
||||
ICaptureStateListener listener) throws RemoteException {
|
||||
Parcel request = Parcel.obtain();
|
||||
Parcel response = Parcel.obtain();
|
||||
request.writeInterfaceToken("android.media.IAudioPolicyService");
|
||||
request.writeStrongBinder(listener.asBinder());
|
||||
service.transact(82 /* REGISTER_SOUNDTRIGGER_CAPTURE_STATE_LISTENER */, request, response,
|
||||
0);
|
||||
return response.readBoolean();
|
||||
}
|
||||
|
||||
private class Listener extends ICaptureStateListener.Stub {
|
||||
@Override
|
||||
public void setCaptureState(boolean active) {
|
||||
synchronized (mListenerLock) {
|
||||
mListener.accept(active);
|
||||
}
|
||||
}
|
||||
private void binderDied() {
|
||||
Log.w(TAG, "Audio policy service died");
|
||||
mNeedToConnect.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
|
||||
*/
|
||||
private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareInternal delegate) {
|
||||
mDelegate = Objects.requireNonNull(delegate);
|
||||
new ExternalCaptureStateTracker("media.audio_policy", active -> {
|
||||
new ExternalCaptureStateTracker(active -> {
|
||||
try {
|
||||
mDelegate.setCaptureState(active);
|
||||
} catch (RemoteException e) {
|
||||
|
||||
@@ -40,6 +40,7 @@ cc_library_static {
|
||||
"com_android_server_security_VerityUtils.cpp",
|
||||
"com_android_server_SerialService.cpp",
|
||||
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
|
||||
"com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp",
|
||||
"com_android_server_stats_pull_StatsPullAtomService.cpp",
|
||||
"com_android_server_storage_AppFuseBridge.cpp",
|
||||
"com_android_server_SystemServer.cpp",
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define LOG_TAG "ExternalCaptureStateTracker"
|
||||
|
||||
#include "core_jni_helpers.h"
|
||||
#include <log/log.h>
|
||||
#include <media/AudioSystem.h>
|
||||
|
||||
namespace android {
|
||||
namespace {
|
||||
|
||||
#define PACKAGE "com/android/server/soundtrigger_middleware"
|
||||
#define CLASSNAME PACKAGE "/ExternalCaptureStateTracker"
|
||||
|
||||
jclass gExternalCaptureStateTrackerClassId;
|
||||
jmethodID gSetCaptureStateMethodId;
|
||||
jmethodID gBinderDiedMethodId;
|
||||
|
||||
void PopulateIds(JNIEnv* env) {
|
||||
gExternalCaptureStateTrackerClassId =
|
||||
(jclass) env->NewGlobalRef(FindClassOrDie(env, CLASSNAME));
|
||||
gSetCaptureStateMethodId = GetMethodIDOrDie(env,
|
||||
gExternalCaptureStateTrackerClassId,
|
||||
"setCaptureState",
|
||||
"(Z)V");
|
||||
gBinderDiedMethodId = GetMethodIDOrDie(env,
|
||||
gExternalCaptureStateTrackerClassId,
|
||||
"binderDied",
|
||||
"()V");
|
||||
}
|
||||
|
||||
class Listener : public AudioSystem::CaptureStateListener {
|
||||
public:
|
||||
Listener(JNIEnv* env, jobject obj) : mObj(env->NewGlobalRef(obj)) {}
|
||||
|
||||
~Listener() {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->DeleteGlobalRef(mObj);
|
||||
}
|
||||
|
||||
void onStateChanged(bool active) override {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->CallVoidMethod(mObj, gSetCaptureStateMethodId, active);
|
||||
}
|
||||
|
||||
void onServiceDied() override {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->CallVoidMethod(mObj, gBinderDiedMethodId);
|
||||
}
|
||||
|
||||
private:
|
||||
jobject mObj;
|
||||
};
|
||||
|
||||
void connect(JNIEnv* env, jobject obj) {
|
||||
sp<AudioSystem::CaptureStateListener> listener(new Listener(env, obj));
|
||||
status_t status =
|
||||
AudioSystem::registerSoundTriggerCaptureStateListener(listener);
|
||||
LOG_ALWAYS_FATAL_IF(status != NO_ERROR);
|
||||
}
|
||||
|
||||
const JNINativeMethod gMethods[] = {
|
||||
{"connect", "()V", reinterpret_cast<void*>(connect)},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(
|
||||
JNIEnv* env) {
|
||||
PopulateIds(env);
|
||||
return RegisterMethodsOrDie(env,
|
||||
CLASSNAME,
|
||||
gMethods,
|
||||
NELEM(gMethods));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
@@ -58,6 +58,8 @@ int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
|
||||
int register_android_server_am_LowMemDetector(JNIEnv* env);
|
||||
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
|
||||
JNIEnv* env);
|
||||
int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(
|
||||
JNIEnv* env);
|
||||
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
|
||||
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
|
||||
int register_android_server_AdbDebuggingManager(JNIEnv* env);
|
||||
@@ -112,6 +114,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
|
||||
register_android_server_am_LowMemDetector(env);
|
||||
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
|
||||
env);
|
||||
register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(
|
||||
env);
|
||||
register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
|
||||
register_android_server_stats_pull_StatsPullAtomService(env);
|
||||
register_android_server_AdbDebuggingManager(env);
|
||||
|
||||
Reference in New Issue
Block a user