如何监控录制行为?思路:从底层录制行为触发开始一步步往上抛。
关键点:1:触发点定在哪?2:如何上抛?3上抛之后实现监控?
首先最先想到的是 3 可以选用观察者模式。
然后先处理问题一,触发点。
之前一篇文章研究过record录制会怎么实现。一直会走到 record Audiopolicymanager startinput()函数。所以在startinput no—error之前把记录一下这件事。
触发点找到了。下来就是上抛。上抛由于需要结合谷歌源码中很多部件,所以可以看看源码学习很多处理,解耦。
观察者:
-
观察者需要一个观察者的类,这个类里维护所有观察者对象。当触发事件上来之后,依次给各个观察者对象下发事件(调用观察者继承接口的方法);(AudioPolicyService.cpp)
-
接口,给被观察者用于继承,当事件触发,接口里的方法被调用;(AudioPolicyService::doOnRecordingConfigurationUpdate)
-
观察者对象实现接口;(RecordingActivityMonitor.java 实现AudioRecordingCallback接口)
方法調用逻辑:
service.registerRecordingCallback(mRecCb);
mRecordMonitor.initMonitor();
mRecordMonitor.registerRecordingCallback(rcdb,isPrivileged);
AudioSystem.setRecordingCallback(this);
native_register_recording_callback();
AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback);
gRecordConfigCallback = cb;
/录音状态发生改变,新开了录音/
audioSession->changeActiveCount(1);
mClientInterface->onRecordingConfigurationUpdate(RECORD_CONFIG_EVENT_START,
mSession, mInputSource);
mAudioPolicyService->onRecordingConfigurationUpdate(event, session, source,
clientConfig, deviceConfig);
mOutputCommandThread->recordingConfigurationUpdateCommand(event, clientInfo,
clientConfig, deviceConfig, patchHandle);
sendCommand(command);
insertCommand_l(command, delayMs);
svc->doOnRecordingConfigurationUpdate(data->mEvent, &data->mClientInfo,&data->mClientConfig, &data >mDeviceConfig,data->mPatchHandle);
mNotificationClients.valueAt(i)->onRecordingConfigurationUpdate(event, clientInfo, clientConfig, deviceConfig, patchHandle);
mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo, clientConfig, deviceConfig, patchHandle);
cb(event, clientInfo, clientConfig, deviceConfig, patchHandle);
env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative, event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
"recordingCallbackFromNative", "(IIII[I)V");
cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
rmc.mDispatcherCb.dispatchRecordingConfigChange(configsSystem); or rmc.mDispatcherCb.dispatchRecordingConfigChange(configsPublic);
AudioManager.java
//注册一个AudioRecordingCallback
public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb, Handler handler)
{
if (cb == null) {
throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
}
synchronized(mRecordCallbackLock) {
// lazy initialization of the list of recording callbacks
if (mRecordCallbackList == null) {
mRecordCallbackList = new ArrayList<AudioRecordingCallbackInfo>();
}
final int oldCbCount = mRecordCallbackList.size();
if (!hasRecordCallback_sync(cb)) {
mRecordCallbackList.add(new AudioRecordingCallbackInfo(cb,
new ServiceEventHandlerDelegate(handler).getHandler()));
final int newCbCount = mRecordCallbackList.size();
//如果之前没注册过-注册,若注册过则返回
if ((oldCbCount == 0) && (newCbCount > 0)) {
// register binder for callbacks
final IAudioService service = getService();
try {
service.registerRecordingCallback(mRecCb);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
} else {
Log.w(TAG, "attempt to call registerAudioRecordingCallback() on a previously"
+ "registered callback");
}
}
}
//rmi需要存根(stub)和骨架(skeleton),rmi用于分布式应用,stub用于客户端,skeleton用于[服务器端](https://www.baidu.com/s?wd=%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao),在jdk1.4版本以及以前需要用rmic命令生成stub,在jdk1.5及以后都不再需要手动生成stub了
private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
@Override
public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
synchronized(mRecordCallbackLock) {
if (mRecordCallbackList != null) {
//这个mRecordCallbackList已经加入register里的AudioRecordingCallback cb,包装后的AudioRecordingCallbackInfo
for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
final AudioRecordingCallbackInfo arci = mRecordCallbackList.get(i);
if (arci.mHandler != null) {
final Message m = arci.mHandler.obtainMessage(
MSSG_RECORDING_CONFIG_CHANGE/*what*/,
new RecordConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
arci.mHandler.sendMessage(m);
}
}
}
}
}
};
AudioService.java
private final RecordingActivityMonitor mRecordMonitor = new RecordingActivityMonitor();
//AudioService在构造函数初始化时:
mRecordMonitor = new RecordingActivityMonitor(mContext);
mRecordMonitor.initMonitor();
//给RecodingActiveMonitor注册:传入一个IRecordingConfigDispatcher。
//判断这个
public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
final boolean isPrivileged =
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
android.Manifest.permission.MODIFY_AUDIO_ROUTING));//用于检测是否授权了,授权之后可获得系统级别notification信息,未获得授权能获得部分信息//7.0没有
mRecordMonitor.registerRecordingCallback(rcdb,isPrivileged);
}
RecordingActivityMonitor.java继承AudioSystem.AudioRecordingCallback接口
//Class to receive and dispatch updates from AudioSystem about recording configurations
//这个RecordingActivityMonitor维护了一个mClient列表,用来存放RecMonitorClient,当AudioSystem这边有更新则会通知mClient里的client
void initMonitor(){
AudioSystem.setRecordingCallback(this);
}
void registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged) {//register就会将回调的client加入mClient之中进行管理
if (rcdb == null) {
return;
}
synchronized (mClients) {
final RecMonitorClient rmc = new RecMonitorClient(rcdb, isPrivileged);//Inner class to track clients that want to be notified of recording updates
if (rmc.init()) {
if (!isPrivileged) {
mHasPublicClients = true;
}
mClients.add(rmc);
}
}
}
AudioSystem.java
public static void setRecordingCallback(AudioRecordingCallback cb) {
synchronized (AudioSystem.class) {
sRecordingCallback = cb;//将一路传来的cb赋值给sRecordingCallback
native_register_recording_callback();//
}
}
public interface AudioRecordingCallback//定义该接口 当底层发生recording状态改变,audiosystem调用这个接口里的方法,使之前传入的sRecordingCallback作出响应
{
/**
* Callback for recording activity notifications events
* @param event
* @param uid uid of the client app performing the recording
* @param session
* @param source
* @param recordingFormat an array of ints containing respectively the client and device
* recording configurations (2*3 ints), followed by the patch handle:
* index 0: client format
* 1: client channel mask
* 2: client sample rate
* 3: device format
* 4: device channel mask
* 5: device sample rate
* 6: patch handle
* @param packName package name of the client app performing the recording. NOT SUPPORTED
*/
void onRecordingConfigurationChanged(int event, int uid, int session, int source,
int[] recordingFormat, String packName);
}
android_media_AudioSystem.cpp ##JNI
{"native_register_recording_callback", "()V",(void *)android_media_AudioSystem_registerRecordingCallback},
static void
android_media_AudioSystem_registerRecordingCallback(JNIEnv *env, jobject thiz)
{
AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback);
}
AudioSystem.cpp
/*static*/ void AudioSystem::setRecordConfigCallback(record_config_callback cb)
{
Mutex::Autolock _l(gLock);
gRecordConfigCallback = cb;//就是之上的sRecordingCallback
}
#######################录音状态发生改变,新开了录音##########################
AudioPolicyManager.cpp
##触发监听
status_t AudioPolicyManager::startInput(audio_io_handle_t input,
audio_session_t session,
concurrency_type__mask_t *concurrency)
{
....
// increment activity count before calling getNewInputDevice() below as only active sessions
// are considered for device selection
audioSession->changeActiveCount(1);
....
return NO_ERROR;
}
AudioSession.cpp
##监听事件管理
uint32_t AudioSession::changeActiveCount(int delta)//delta 要么为1(启动一个startInput) 要么为-1(调用一次stopInput)
{
const uint32_t oldActiveCount = mActiveCount;
if ((delta + (int)mActiveCount) < 0) {//边界条件:不可以在Active==0时还调用stop
ALOGW("%s invalid delta %d, active count %d",
__FUNCTION__, delta, mActiveCount);
mActiveCount = (uint32_t)(-delta);
}
mActiveCount += delta;
ALOGV("%s active count %d", __FUNCTION__, mActiveCount);
if ((oldActiveCount == 0) && (mActiveCount > 0)) {//start
// if input maps to a dynamic policy with an activity listener, notify of state change
if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
{
mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId,
MIX_STATE_MIXING);
}
mClientInterface->onRecordingConfigurationUpdate(RECORD_CONFIG_EVENT_START,
mSession, mInputSource);
} else if ((oldActiveCount > 0) && (mActiveCount == 0)) {//stop
// if input maps to a dynamic policy with an activity listener, notify of state change
if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
{
mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mRegistrationId,
MIX_STATE_IDLE);
}
mClientInterface->onRecordingConfigurationUpdate(RECORD_CONFIG_EVENT_STOP,
mSession, mInputSource);
}
return mActiveCount;
}
AudioPolicyClientImpl.cpp
##加入client与device信息,thread来执行命令,update信息分发给各个client
void AudioPolicyService::AudioPolicyClient::onRecordingConfigurationUpdate(
int event, audio_session_t session, audio_source_t source,
const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig)
{
mAudioPolicyService->onRecordingConfigurationUpdate(event, session, source,
clientConfig, deviceConfig);
}
AudioPolicyService.cpp
将信息打包放入命令列表
//有内部类 AudioCommandThread与线程有关的事件处理:所有的命令(音量控制,输入、输出的切换等)最终都会在该线程中排队执行;
在AudioPolicyService对象构造过程中,分别创建了ApmTone、ApmAudio、ApmOutput三个AudioCommandThread线程:
1、 ApmTone用于播放tone音;
2、 ApmAudio用于执行audio命令;
3、ApmOutput用于执行输出命令;
在第一次强引用AudioCommandThread线程对象时,AudioCommandThread的onFirstRef函数被回调,在此启动线程
void AudioPolicyService::AudioCommandThread::onFirstRef()
{
run(mName.string(), ANDROID_PRIORITY_AUDIO);
}
这里采用异步方式来执行audio command,当需要执行上表中的命令时,首先将命令投递到AudioCommandThread的mAudioCommands命令向量表中,然后通过mWaitWorkCV.signal()唤醒AudioCommandThread线程,被唤醒的AudioCommandThread线程执行完command后,又通过mWaitWorkCV.waitRelative(mLock, waitTime)睡眠等待命令到来。
void AudioPolicyService::onRecordingConfigurationUpdate(int event,
const record_client_info_t *clientInfo, const audio_config_base_t *clientConfig,
const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle)
{
mOutputCommandThread->recordingConfigurationUpdateCommand(event, clientInfo,
clientConfig, deviceConfig, patchHandle);
}
void AudioPolicyService::AudioCommandThread::recordingConfigurationUpdateCommand(
int event, const record_client_info_t *clientInfo,
const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
audio_patch_handle_t patchHandle)
{
sp<AudioCommand>command = new AudioCommand();//创建新的command
command->mCommand = RECORDING_CONFIGURATION_UPDATE;//command的mCommand赋值给RECORDING_CONFIGURATION_UPDATE
RecordingConfigurationUpdateData *data = new RecordingConfigurationUpdateData();
data->mEvent = event;
data->mClientInfo = *clientInfo;
data->mClientConfig = *clientConfig;
data->mDeviceConfig = *deviceConfig;
data->mPatchHandle = patchHandle;
command->mParam = data;
ALOGV("AudioCommandThread() adding recording configuration update event %d, source %d uid %u",
event, clientInfo->source, clientInfo->uid);
sendCommand(command);
}
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
{
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);//将命令投递到AudioCommandThread的mAudioCommands命令向量表中
mWaitWorkCV.signal();//唤醒AudioCommandThread线程
}
Mutex::Autolock _l(command->mLock);
while (command->mWaitStatus) {
nsecs_t timeOutNs = kAudioCommandTimeoutNs + milliseconds(delayMs);
if (command->mCond.waitRelative(command->mLock, timeOutNs) != NO_ERROR) {
command->mStatus = TIMED_OUT;
command->mWaitStatus = false;
}
}
return command->mStatus;
}
bool AudioPolicyService::AudioCommandThread::threadLoop()
{
nsecs_t waitTime = -1;
mLock.lock();
while (!exitPending())
{
sp<AudioPolicyService> svc;
while (!mAudioCommands.isEmpty() && !exitPending()) {//insertCommand_l给mAudioCommands添加了指令
nsecs_t curTime = systemTime();
// commands are sorted by increasing time stamp: execute them from index 0 and up
if (mAudioCommands[0]->mTime <= curTime) {//当 命令超过了最晚执行时间,将命令放入指令表最后一位。resort
sp<AudioCommand> command = mAudioCommands[0];
mAudioCommands.removeAt(0);
mLastCommand = command;
...
switch (command->mCommand) {
case RECORDING_CONFIGURATION_UPDATE: {
RecordingConfigurationUpdateData *data =
(RecordingConfigurationUpdateData *)command->mParam.get();
svc = mService.promote();
if (svc == 0) {
break;
}
mLock.unlock();
svc->doOnRecordingConfigurationUpdate(data->mEvent, &data->mClientInfo,&data->mClientConfig, &data->mDeviceConfig,data->mPatchHandle);
mLock.lock();
} break;
...
}
//分发update信息
void AudioPolicyService::doOnRecordingConfigurationUpdate(int event,
const record_client_info_t *clientInfo, const audio_config_base_t *clientConfig,
const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle)
{
Mutex::Autolock _l(mNotificationClientsLock);
for (size_t i = 0; i < mNotificationClients.size(); i++) {
mNotificationClients.valueAt(i)->onRecordingConfigurationUpdate(event, clientInfo,
clientConfig, deviceConfig, patchHandle);
}
}
void AudioPolicyService::NotificationClient::onRecordingConfigurationUpdate(
int event, const record_client_info_t *clientInfo,
const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
audio_patch_handle_t patchHandle)
{
if (mAudioPolicyServiceClient != 0) {
mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo,
clientConfig, deviceConfig, patchHandle);
}
}
AudioSystem.cpp
void AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate(
int event, const record_client_info_t *clientInfo,
const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
audio_patch_handle_t patchHandle) {
record_config_callback cb = NULL;
{
Mutex::Autolock _l(AudioSystem::gLock);
cb = gRecordConfigCallback;//回调方式。
}
if (cb != NULL) {
cb(event, clientInfo, clientConfig, deviceConfig, patchHandle);
}
}// cd 赋值为 gRecordConfigCallback,而gRecordConfigCallback 在之前的 setRecordConfigCallback里赋值为了android_media_AudioSystem_recording_callback
android_media_AudioSystem.cpp ##JNI
static void android_media_AudioSystem_recording_callback(int event, const record_client_info_t *clientInfo, const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig, audio_patch_handle_t patchHandle)
{
...
// callback into java
jclass clazz = env->FindClass(kClassPathName);
env->CallStaticVoidMethod(clazz,
gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
event, (jint) clientInfo->uid, clientInfo->session, clientInfo->source, recParamArray);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(recParamArray);
}
int register_android_media_AudioSystem(JNIEnv *env)
{
...
gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
"recordingCallbackFromNative", "(IIII[I)V");
...
}
AudioSystem.java
/**
330 * Callback from native for recording configuration updates.
331 * @param event
332 * @param session
333 * @param source
334 * @param recordingFormat see
335 * {@link AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int, int[])}
336 * for the description of the record format.
337 */
338 private static void recordingCallbackFromNative(int event, int uid, int session, int source,
339 int[] recordingFormat) {
340 AudioRecordingCallback cb = null;
341 synchronized (AudioSystem.class) {
342 cb = sRecordingCallback;
343 }
344 if (cb != null) {
345 // TODO receive package name from native
346 cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
347 }
348 }
RecordingActivityMonitor.java 实现AudioRecordingCallback接口
public void onRecordingConfigurationChanged(int event, int uid, int session, int source,
int[] recordingInfo, String packName) {
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
final List<AudioRecordingConfiguration> configsSystem =
updateSnapshot(event, uid, session, source, recordingInfo);
if (configsSystem != null){
synchronized (mClients) {
// list of recording configurations for "public consumption". It is only computed if
// there are non-system recording activity listeners.
final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients ?
anonymizeForPublicConsumption(configsSystem) :
new ArrayList<AudioRecordingConfiguration>();
final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
while (clientIterator.hasNext()) {
final RecMonitorClient rmc = clientIterator.next();
try {
if (rmc.mIsPrivileged) {
rmc.mDispatcherCb.dispatchRecordingConfigChange(configsSystem);
} else {
rmc.mDispatcherCb.dispatchRecordingConfigChange(configsPublic);//实现在AudioManager里面
}
} catch (RemoteException e) {
Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
}
}
}
}
}
/*
private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
@Override
public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
synchronized(mRecordCallbackLock) {
if (mRecordCallbackList != null) {
//这个mRecordCallbackList已经加入register里的AudioRecordingCallback cb,包装后的AudioRecordingCallbackInfo
for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
final AudioRecordingCallbackInfo arci = mRecordCallbackList.get(i);
if (arci.mHandler != null) {
final Message m = arci.mHandler.obtainMessage(
MSSG_RECORDING_CONFIG_CHANGE/*what*/,
new RecordConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
arci.mHandler.sendMessage(m);
}
}
}
}
}
};
*/
public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
66 AudioFormat devFormat, int patchHandle, String packageName) {
67 mClientUid = uid;
68 mSessionId = session;
69 mClientSource = source;
70 mClientFormat = clientFormat;
71 mDeviceFormat = devFormat;
72 mPatchHandle = patchHandle;
73 mClientPackageName = packageName;
74 }
private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,
int source, int[] recordingInfo) {
final boolean configChanged;
final ArrayList<AudioRecordingConfiguration> configs;
synchronized(mRecordConfigs) {
switch (event) {
case AudioManager.RECORD_CONFIG_EVENT_STOP:
// return failure if an unknown recording session stopped
configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
break;
case AudioManager.RECORD_CONFIG_EVENT_START:
final AudioFormat clientFormat = new AudioFormat.Builder()
.setEncoding(recordingInfo[0])
// FIXME this doesn't support index-based masks
.setChannelMask(recordingInfo[1])
.setSampleRate(recordingInfo[2])
.build();
final AudioFormat deviceFormat = new AudioFormat.Builder()
.setEncoding(recordingInfo[3])
// FIXME this doesn't support index-based masks
.setChannelMask(recordingInfo[4])
.setSampleRate(recordingInfo[5])
.build();
final int patchHandle = recordingInfo[6];
final Integer sessionKey = new Integer(session);
final String[] packages = mPackMan.getPackagesForUid(uid);
final String packageName;
if (packages != null && packages.length > 0) {
packageName = packages[0];
} else {
packageName = "";
}
final AudioRecordingConfiguration updatedConfig =
new AudioRecordingConfiguration(uid, session, source,
clientFormat, deviceFormat, patchHandle, packageName);//打包传过来的状态改变信息成AudioRecordingConfiguration
if (mRecordConfigs.containsKey(sessionKey)) {
if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {//将新生成的AudioRecordingConfiguration与之前的通过session进行判断
configChanged = false;
} else {
// config exists but has been modified
mRecordConfigs.remove(sessionKey);
mRecordConfigs.put(sessionKey, updatedConfig);
configChanged = true;
}
} else {
mRecordConfigs.put(sessionKey, updatedConfig);
configChanged = true;
}
break;
default:
Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
event, session, source));
configChanged = false;
}
if (configChanged) {
configs = new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());//若改变,将最新所有音频状态赋值给config
} else {
configs = null;
}
}
return configs;
}
网友评论