做网站和推广新闻发布系统
摘要:
1.在Android9中为了防止App后台录音,实际上已经存在一个静音的接口用于禁止App录音,不过这个接口存在两个问题:1)仅在C++层供系统调用,没有暴露给App调用;2)这个接口是根据uid静音的,很多时候App开发人员为了图省事都将自己的App设置为系统级别的App,如果根据uid静音,那么系统级别的uid都是1000,就会造成误伤,所以我们增加了根据包名静音的接口并暴露给上层调用
2.并发录音是项目中很常见的需求,其实Android9中已经包含并发录音的代码,只不过被屏蔽了,只要稍加修改就可以实现功能
一、根据包名静音接口的实现
1.Java层实现
我决定将接口设置在CarAudioService中(因为本人是搞车机的),其实CarAudioService里面的很多接口最终都会调用到AudioService里面,这个接口也不例外,闲话少说,上代码:
CarAudioManager.java:
/*** setRecordSilenced according to uid** @hide*/@SystemApipublic int setRecordSilenced(String packageName, boolean silenced) throws CarNotConnectedException {try {return mService.setRecordSilenced(packageName, silenced);} catch (RemoteException e) {Log.e(CarLibLog.TAG_CAR, "setRecordSilenced failed", e);throw new CarNotConnectedException(e);}}
ICarAudio.aidl:
int setRecordSilenced(String packageName, boolean silenced);
CarAudioService.java:
@Overridepublic int setRecordSilenced(String packageName, boolean silenced) {return mAudioManager.setRecordSilenced(packageName, silenced);}
AudioManager.java:
/*** setRecordSilenced according to packageName** @hide*/public int setRecordSilenced(String packageName, boolean silenced) {final IAudioService service = getService();try {return service.setRecordSilenced(packageName, silenced);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
IAudioService.aidl:
int setRecordSilenced(String packageName, boolean silenced);
AudioService.java:
/** @hide */@Overridepublic int setRecordSilenced(String packageName, boolean silenced) {return AudioSystem.setRecordSilenced(packageName, silenced);}
AudioSystem.java:
/*** @hide*/public static native int setRecordSilenced(String packageName, boolean silenced);
2.JNI层实现
android_media_AudioSystem.cpp:
static jint
android_media_AudioSystem_setRecordSilenced(JNIEnv *env, jobject thiz, jstring packageName, jboolean silenced)
{const char *c_packageName = env->GetStringUTFChars(packageName, NULL);int status = check_AudioSystem_Command(AudioSystem::setRecordSilenced(c_packageName, silenced));env->ReleaseStringUTFChars(packageName, c_packageName);return (jint) status;
}
static const JNINativeMethod gMethods[] = {......{"setRecordSilenced", "(Ljava/lang/String;Z)I", (void *)android_media_AudioSystem_setRecordSilenced},......
hiddenapi-light-greylist.txt:
Landroid/media/AudioSystem;->setRecordSilenced(Ljava/lang/String;Z)I
hiddenapi-private-dex.txt:
Landroid/media/AudioSystem;->setRecordSilenced(Ljava/lang/String;Z)I
3.C++层实现
AudioSystem.cpp:
status_t AudioSystem::setRecordSilenced(const char *packageName, bool silenced)
{ALOGV("setRecordSilencedByName : packageName = %s, is silenced = %d", packageName, silenced);const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();if (aps == 0) return PERMISSION_DENIED;aps->setRecordSilencedByName(packageName, silenced);return NO_ERROR;
}
AudioSystem.h:
class AudioSystem
{
public:
......static status_t muteMicrophone(bool state);......static status_t setRecordSilenced(const char *packageName, bool active);
};
这里有两点需要说明:
- 原生的
setRecordSilenced
接口在AudioPolicyService.cpp
中,我们需要调用到AudioPolicyService
中,要利用Binder机制,幸运的是AudioSystem中很轻易就能获取到BpAudioPolicyService
实例,接下来理所当然要修改一些Binder有关的文件 - 因为
AudioPolicyService.cpp
中本来就有一个接口叫setRecordSilenced
,我们并不是直接调用那个接口,而是先进行一些处理,所以我们调用的时候要换一个名字,叫setRecordSilencedByName
IAudioPolicyService.cpp:
enum {SET_DEVICE_CONNECTION_STATE = IBinder::FIRST_CALL_TRANSACTION,GET_DEVICE_CONNECTION_STATE,......//在枚举中加入代表咱们函数的成员SET_RECORD_SILENCED_BY_NAME
};
class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
{
public:explicit BpAudioPolicyService(const sp<IBinder>& impl): BpInterface<IAudioPolicyService>(impl){}virtual status_t setDeviceConnectionState(audio_devices_t device,audio_policy_dev_state_t state,const char *device_address,const char *device_name){Parcel data, reply;data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());data.writeInt32(static_cast <uint32_t>(device));data.writeInt32(static_cast <uint32_t>(state));data.writeCString(device_address);data.writeCString(device_name);remote()->transact(SET_DEVICE_CONNECTION_STATE, data, &reply);return static_cast <status_t> (reply.readInt32());}......//在BpAudioPolicyService中加入咱们的函数virtual void setRecordSilencedByName(const char *packageName, bool silenced){Parcel data, reply;data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());data.writeCString(packageName);data.writeInt32(silenced ? 1 : 0);remote()->transact(SET_RECORD_SILENCED_BY_NAME, data, &reply);}......
};
status_t BnAudioPolicyService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{......switch (code) {case SET_DEVICE_CONNECTION_STATE: {CHECK_INTERFACE(IAudioPolicyService, data, reply);audio_devices_t device =static_cast <audio_devices_t>(data.readInt32());audio_policy_dev_state_t state =static_cast <audio_policy_dev_state_t>(data.readInt32());const char *device_address = data.readCString();const char *device_name = data.readCString();if (device_address == nullptr || device_name == nullptr) {ALOGE("Bad Binder transaction: SET_DEVICE_CONNECTION_STATE for device %u", device);reply->writeInt32(static_cast<int32_t> (BAD_VALUE));} else {reply->writeInt32(static_cast<uint32_t> (setDeviceConnectionState(device,state,device_address,device_name)));}return NO_ERROR;} break;......//在BnAudioPolicyService::onTransact的switch/case中加入咱们的函数case SET_RECORD_SILENCED_BY_NAME: {CHECK_INTERFACE(IAudioPolicyService, data, reply);const char *packageName = data.readCString();setRecordSilencedByName(packageName, data.readInt32() == 1);return NO_ERROR;} break;default:return BBinder::onTransact(code, data, reply, flags);}
}
IAudioPolicyService.h:
class IAudioPolicyService : public IInterface
{
public:DECLARE_META_INTERFACE(AudioPolicyService);//// IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)//virtual status_t setDeviceConnectionState(audio_devices_t device,audio_policy_dev_state_t state,const char *device_address,const char *device_name) = 0;......virtual void setRecordSilencedByName(const char *packageName, bool silenced) = 0;......};
AudioPolicyService.cpp:
void AudioPolicyService::setRecordSilencedByName(const char *packageName, bool silenced)
{Mutex::Autolock _l(mNotificationClientsLock);ALOGD("AudioPolicyService:setRecordSilencedByName(packageName: %s, silenced: %d)", packageName, silenced);int count = mAudioRecordClients.size();for (int i = 0; i<count ; i++) {sp<AudioRecordClient> client = mAudioRecordClients.valueAt(i);//遍历mAudioRecordClients,根据packageName找到sessionID,之后我们会根据这个sessionID来静音if (strcmp(String8(client->opPackageName).string(), packageName) == 0) {ALOGD("AudioPolicyService:setRecordSilencedByName Find same package name in mAudioRecordClients");int32_t sessionID = client->session;setRecordSilenced(sessionID, silenced);} }
}
AudioPolicyService.h:
virtual void setAudioPortCallbacksEnabled(bool enabled);......virtual void setRecordSilencedByName(const char *packageName, bool silenced);
这里需要稍微解释一下:
- 名义上是根据包名静音,实际上在这里必须把包名转换为
sessionID
,因为sessionID
才是AudioSession
等对象共同持有的“通货”,packageName
只保存在AudioPolicyService
中 - 之后我们直接调用了原生代码的
setRecordSilenced
方法,但是传入的却是sessionID
而不是UID
,为什么可以这样用?这是因为sessionID
和UID
都是int
类型,而且巧合的是,它们的取值范围正好错开的,这就避免了增加一大堆新方法,直接修改老方法的实现,在老方法上根据取值判断是sessionID
还是UID
,并采取不同的操作就可以了
AudioPolicyManager.cpp:
#define AID_ROOT 0
#define AID_SYSTEM 1000
......
void AudioPolicyManager::setRecordSilenced(uid_t uid, bool silenced)
{ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced);Vector<sp<AudioInputDescriptor> > activeInputs = mInputs.getActiveInputs();//根据取值范围判断if (AID_ROOT < uid < AID_SYSTEM) {for (size_t i = 0; i < activeInputs.size(); i++) {sp<AudioInputDescriptor> activeDesc = activeInputs[i];AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true);for (size_t j = 0; j < activeSessions.size(); j++) {sp<AudioSession> activeSession = activeSessions.valueAt(j);uid_t session = activeSession->session();if (session == uid) {activeSession->setSilenced(silenced);ALOGD("setRecordSilencedByName in AudioPolicyManager:setRecordSilenced, find it!");}}}} else {for (size_t i = 0; i < activeInputs.size(); i++) {sp<AudioInputDescriptor> activeDesc = activeInputs[i];AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true);for (size_t j = 0; j < activeSessions.size(); j++) {sp<AudioSession> activeSession = activeSessions.valueAt(j);if (activeSession->uid() == uid) {activeSession->setSilenced(silenced);}}}}
}
Threads.cpp:
#define AID_ROOT 0
#define AID_SYSTEM 1000
......
void AudioFlinger::RecordThread::setRecordSilenced(uid_t uid, bool silenced)
{Mutex::Autolock _l(mLock);if (AID_ROOT < uid < AID_SYSTEM) {for (size_t i = 0; i < mTracks.size() ; i++) {sp<RecordTrack> track = mTracks[i];uid_t session = track->sessionId();if (track != 0 && session == uid) {track->setSilenced(silenced);ALOGD("setRecordSilencedByName in AudioFlinger::RecordThread, find it!");}}} else {for (size_t i = 0; i < mTracks.size() ; i++) {sp<RecordTrack> track = mTracks[i];if (track != 0 && track->uid() == uid) {track->setSilenced(silenced);}}}
}
......
void AudioFlinger::MmapCaptureThread::setRecordSilenced(uid_t uid, bool silenced)
{ALOGD("setRecordSilencedByName AudioFlinger::MmapCaptureThread::setRecordSilenced");Mutex::Autolock _l(mLock);if (AID_ROOT < uid < AID_SYSTEM) {for (size_t i = 0; i < mActiveTracks.size() ; i++) {uid_t session = mActiveTracks[i]->sessionId();if (session == uid) {ALOGD("setRecordSilencedByName in AudioFlinger::MmapCaptureThread, find it!");mActiveTracks[i]->setSilenced_l(silenced);broadcast_l();}}} else {for (size_t i = 0; i < mActiveTracks.size() ; i++) {if (mActiveTracks[i]->uid() == uid) {mActiveTracks[i]->setSilenced_l(silenced);broadcast_l();}}}
}
以上
二、并发录音的实现
非常简单,只要在AudioPolicyManager.cpp
中修改三个地方:
sp<AudioSession> audioSession = new AudioSession(session,inputSource,config->format,samplingRate,config->channel_mask,flags,uid,isSoundTrigger,policyMix, mpClientInterface);// FIXME: disable concurrent capture until UI is ready
//这里将#if 0改为#if 1
#if 1// reuse an open input if possiblesp<AudioInputDescriptor> reusedInputDesc;
sp<AudioSession> audioSession = inputDesc->getAudioSession(session);if (audioSession == 0) {ALOGW("startInput() unknown session %d on input %d", session, input);return BAD_VALUE;}// FIXME: disable concurrent capture until UI is ready
//同样将#if 0改为#if 1
#if 1if (!isConcurentCaptureAllowed(inputDesc, audioSession)) {ALOGW("startInput(%d) failed: other input already started", input);return INVALID_OPERATION;}
//static
bool AudioPolicyManager::isConcurrentSource(audio_source_t source)
{return (source == AUDIO_SOURCE_HOTWORD) ||(source == AUDIO_SOURCE_VOICE_RECOGNITION) ||//这里把需要实现并发录音的录音类型加上(source == AUDIO_SOURCE_MIC) ||(source == AUDIO_SOURCE_FM_TUNER);
}
关于最后一个修改的解释:将两个if 0
改为if 1
后,Android实际上已经可以并发录音,但是允许并发录音的类型仅限isConcurrentSource
里面包含的AUDIO_SOURCE_HOTWORD
,AUDIO_SOURCE_VOICE_RECOGNITION
和AUDIO_SOURCE_FM_TUNER
,如果还录其他类型,就需要在这里加上,例如作者在这里增加的AUDIO_SOURCE_MIC
以上
如果文章存在错误和不明白的地方,欢迎留言讨论!