Androidレコーディングのいくつかのプロセス制御
1.他のappの録音行為をどのように監視しますか?
調べてみると、このAPIが見つかりました.
使用方法は、大体次のようになります.
AudioRecordingConfigurationには、次の情報が含まれています.
実測の結果、appレコーディングがあるときは2回コールバックします.コールバックのパラメータでは、相手が録音を停止したのか録音を開始したのかは、しばらく判断できません.また、判断を補助するAPIもあります.
私はframworkをしています.これらのメカニズムをはっきりさせないと、どうして言えますか.だからコードの追跡を開始します.
まず、AudioManagerが提供するpublicインタフェースを見てみましょう.
mRecordCallbackList定義:
AudioServiceに本当に登録されているmRecCbは何ですか?
registerAudioRecordingCallbackインタフェースが呼び出されているすべてのインタフェースを巡ります.登録したクライアントは、コールバックされます.
AudioManagerは、AudioServiceがmRecCbを呼び出すとクライアントが受信することを基本的に理解しています.
onRecordingConfigChangedのコールバック.
では、AudioServiceの方に行ってみましょう.
別のクラスRecordingActivity Monitorを引き出す
rmc.Init()はrcdb(観察者)のために死亡エージェントを設定し、rcdbを切るとrmcが知り、コールバックします.
AudioServiceに戻ると、このRecordingActivity Monitorクラスは次のように動作します.
鍵はここにある
ここでのthisはもちろんRecordingActivity Monitorのいずれかのオブジェクトを指します.AudioServiceにとって、このmRecordMonitorです.
RecordingActivity Monitorの定義をよく見てください.
ははは、どうやら、真相はAudioSystemの中にあるようだ.
ついでにRecordingActivity Monitorも振り返ってみよう
特権と非特権の違いは何ですか?
updateSnapshotの結果を返し、anonymizeForPublicConsumption(configsSystem)の結果を返します.
まずupdateSnapshotを見てみましょう
簡単にupdateSnapshotをまとめると、変化があれば(新しい録音が起動したり、終了したり、録音のセッションが変わったりします).mRecordConfigsという記録された配列を返します.
configsSystemです.
configsPublic:
mHasPublicClientsでない場合、空のArrayListは1つしか返されません.mHasPublicClientsの場合、anonymizeForPublicConsumption(uidとpkgNameを隠す?)が得られます.処理したconfigsSystem、拭いて、めまいがしました.どうせ私はシステムappです.きっと手に入れたのはconfigsSystemです.
最終的な結果は、以下の実測で利用可能な判断方法である.
忘れそうになった
前述のように、この関数がコールバックされたため、最終appがonRecordingConfigChangedのコールバックを受信する.注意してください、この2つの方法、1つはConfigurationで、1つはConfigです(appはフルスペルではなく、略語だけを使用します).
AudioSystem.JAvaにおけるsetRecordingCallbackの登録後、実際にAudioSystem(native)クラスに登録する.
つまり、onRecordingConfigurationUpdateをコールバックすると、onRecordingConfigChangedコールバックが受信されます.
グローバル検索後、この2つの場所がこのonRecordingConfigurationUpdateメソッドにコールバックすることがわかりました.
changeActiveCount
簡単に説明を書きましょう.もう明らかですが.APMがstartInputを呼び出すと、appはコールバックを受け取ります.パラメータは1です.stopInputの場合のコールバックパラメータは-1.パラメータの意味については、次を参照してください.
AudioSession::ActiveCount関数を変更します.
onSessionInfoUpdate
調べましたが、直接showでスタックを下げます.
私の現在の理解では、主にcreateAudioPatchという状況に使用されています.これは私もよく知らないので,売らない.
2.hfp電話に接続した後、録音はなぜstandbyされるのですか?
hfp通話中に常に開いているRecordThread(updateCallRoutingのcreateAudioPatchによって作成された)が2つ存在するのはなぜですか?
git logで見たのはusb voice callのためとか
この段落は実はよく分からなかった.自分ででたらめに直したようだ.私のやり方はupdateCallRoutingを注釈することです.どうせusb voice callは使いません.
2番目の問題を防止するには、次の3番目の問題に従って操作します.
3.電話が通じたとき、録音を一時停止し、電話が切れたとき、録音を再開するにはどうすればいいですか.
この問題は比較的に簡単で、直接私の書いたごみのコードに行きます
すみません、虎頭蛇尾です.
調べてみると、このAPIが見つかりました.
android.media.AudioManager.AudioRecordingCallback
使用方法は、大体次のようになります.
mAudioManger = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
mRecordingCallback = new SystemRecordingCallback();
//
mAudioManger.registerAudioRecordingCallback(mRecordingCallback,null);
//
public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
super.onRecordingConfigChanged(configs);
for (int i = 0; i < configs.size(); i++) {
AudioRecordingConfiguration config = configs.get(i);
Log.d(TAG, "onRecordingConfigChanged :" +
AudioRecordingConfiguration.toLogFriendlyString(config));
int source = config.getClientAudioSource();
switch (source) {
case MediaRecorder.AudioSource.MIC: {
// app
}
break;
case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
Log.d(TAG, "It is a Call");
break;
}
}
}
AudioRecordingConfigurationには、次の情報が含まれています.
// hide
/**
* @hide
*/
public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
return new String("session:" + arc.mSessionId
+ " -- source:" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
+ " -- uid:" + arc.mClientUid
+ " -- patch:" + arc.mPatchHandle
+ " -- pack:" + arc.mClientPackageName
+ " -- format client=" + arc.mClientFormat.toLogFriendlyString()
+ ", dev=" + arc.mDeviceFormat.toLogFriendlyString());
}
実測の結果、appレコーディングがあるときは2回コールバックします.コールバックのパラメータでは、相手が録音を停止したのか録音を開始したのかは、しばらく判断できません.また、判断を補助するAPIもあります.
mAudioManger.getActiveRecordingConfigurations();
私はframworkをしています.これらのメカニズムをはっきりさせないと、どうして言えますか.だからコードの追跡を開始します.
まず、AudioManagerが提供するpublicインタフェースを見てみましょう.
public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb, Handler handler)
{
...
mRecordCallbackList.add(new AudioRecordingCallbackInfo(cb,/* , */);
...
final IAudioService service = getService();
service.registerRecordingCallback(mRecCb);
...
}
mRecordCallbackList定義:
//AudioRecordingCallbackInfo AudioRecordingCallback handle
//
private List<AudioRecordingCallbackInfo> mRecordCallbackList;
AudioServiceに本当に登録されているmRecCbは何ですか?
private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
@Override
public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
if (mRecordCallbackList != null) {
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);
}
}
}
}
}
//
case MSSG_RECORDING_CONFIG_CHANGE: {
final RecordConfigChangeCallbackData cbData =(RecordConfigChangeCallbackData) msg.obj;
if (cbData.mCb != null) {
//
cbData.mCb.onRecordingConfigChanged(cbData.mConfigs);
}
} break;
registerAudioRecordingCallbackインタフェースが呼び出されているすべてのインタフェースを巡ります.登録したクライアントは、コールバックされます.
AudioManagerは、AudioServiceがmRecCbを呼び出すとクライアントが受信することを基本的に理解しています.
onRecordingConfigChangedのコールバック.
では、AudioServiceの方に行ってみましょう.
//AudioService.java
public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
// MODIFY_AUDIO_ROUTING
final boolean isPrivileged =
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING));
mRecordMonitor.registerRecordingCallback(rcdb, isPrivileged);
}
別のクラスRecordingActivity Monitorを引き出す
void registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged) {
...
final RecMonitorClient rmc = new RecMonitorClient(rcdb, isPrivileged);
if (rmc.init()) {
if (!isPrivileged) {
// MODIFY_AUDIO_ROUTING
mHasPublicClients = true;
}
mClients.add(rmc);
}
}
rmc.Init()はrcdb(観察者)のために死亡エージェントを設定し、rcdbを切るとrmcが知り、コールバックします.
public void binderDied() {
Log.w(TAG, "client died");
//
sMonitor.unregisterRecordingCallback(mDispatcherCb);
}
AudioServiceに戻ると、このRecordingActivity Monitorクラスは次のように動作します.
//AudioService.java
//======================
// Audio policy callbacks from AudioSystem for recording configuration updates
//======================
private final RecordingActivityMonitor mRecordMonitor;
public AudioService(Context context) {
...
mRecordMonitor = new RecordingActivityMonitor(mContext);
...
mRecordMonitor.initMonitor();
...
}
鍵はここにある
//RecordingActivityMonitor.java
void initMonitor() {
AudioSystem.setRecordingCallback(this);
}
ここでのthisはもちろんRecordingActivity Monitorのいずれかのオブジェクトを指します.AudioServiceにとって、このmRecordMonitorです.
RecordingActivity Monitorの定義をよく見てください.
public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback {
...
ははは、どうやら、真相はAudioSystemの中にあるようだ.
private static AudioRecordingCallback sRecordingCallback;
public static void setRecordingCallback(AudioRecordingCallback cb) {
synchronized (AudioSystem.class) {
sRecordingCallback = cb;
native_register_recording_callback();
}
}
// , ,
private static void recordingCallbackFromNative(int event, int uid, int session, int source,int[] recordingFormat) {
//cb AudioService mRecordMonitor
cb = sRecordingCallback;
...
cb.onRecordingConfigurationChanged(event, uid, session, source, recordingFormat, "");
...
}
ついでにRecordingActivity Monitorも振り返ってみよう
//services/core/java/com/android/server/audio/RecordingActivityMonitor.java
// , AudioSystem
public void onRecordingConfigurationChanged(int event, int uid, int session, int source,int[] recordingInfo, String packName) {
// Source FM_TUNER return. app
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
// configsSystem, configsPublic
final List<AudioRecordingConfiguration> configsSystem =
updateSnapshot(event, uid, session, source, recordingInfo);
...
//mHasPublicClients , MODIFY_AUDIO_ROUTING
final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients ?anonymizeForPublicConsumption(configsSystem) :new ArrayList<AudioRecordingConfiguration>();
//
final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
while (clientIterator.hasNext()) {
final RecMonitorClient rmc = clientIterator.next();
...
// , configsSystem, , configsPublic
if (rmc.mIsPrivileged) { rmc.mDispatcherCb.dispatchRecordingConfigChange(configsSystem);
} else { rmc.mDispatcherCb.dispatchRecordingConfigChange(configsPublic);
}
}
}
特権と非特権の違いは何ですか?
updateSnapshotの結果を返し、anonymizeForPublicConsumption(configsSystem)の結果を返します.
まずupdateSnapshotを見てみましょう
private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session,int source, int[] recordingInfo) {
final boolean configChanged;
final ArrayList<AudioRecordingConfiguration> configs;
...
switch (event) {
case AudioManager.RECORD_CONFIG_EVENT_STOP:
// , mRecordConfigs
// , configChanged
configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
break;
case AudioManager.RECORD_CONFIG_EVENT_START:
...
//sessionKey session Integer
//updatedConfig AudioSystem
//
mRecordConfigs.put(sessionKey, updatedConfig);
configChanged = true;
...
}
if (configChanged) {
configs = new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
} else {
configs = null;
}
...
return configs;
}
簡単にupdateSnapshotをまとめると、変化があれば(新しい録音が起動したり、終了したり、録音のセッションが変わったりします).mRecordConfigsという記録された配列を返します.
configsSystemです.
configsPublic:
//“ ” 。
final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients ?anonymizeForPublicConsumption(configsSystem) :
new ArrayList<AudioRecordingConfiguration>();
mHasPublicClientsでない場合、空のArrayListは1つしか返されません.mHasPublicClientsの場合、anonymizeForPublicConsumption(uidとpkgNameを隠す?)が得られます.処理したconfigsSystem、拭いて、めまいがしました.どうせ私はシステムappです.きっと手に入れたのはconfigsSystemです.
最終的な結果は、以下の実測で利用可能な判断方法である.
mRecordingCallback = new SystemRecordingCallback();
mAudioManger.registerAudioRecordingCallback(mRecordingCallback,null);
private class SystemRecordingCallback extends AudioManager.AudioRecordingCallback {
private final String TAG = "CaptureService.SystemRecordingCallback";
@Override
public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
super.onRecordingConfigChanged(configs);
int activeSize = configs.size();
if (activeSize == 0) {
// , app.
} else {
for (int i = 0; i < activeSize; i++) {
AudioRecordingConfiguration config = configs.get(i);
int source = config.getClientAudioSource();
switch (source) {
case MediaRecorder.AudioSource.MIC:
...
// ,
break;
}
}
}
}
}
忘れそうになった
前述のように、この関数がコールバックされたため、最終appがonRecordingConfigChangedのコールバックを受信する.注意してください、この2つの方法、1つはConfigurationで、1つはConfigです(appはフルスペルではなく、略語だけを使用します).
AudioSystem.JAvaにおけるsetRecordingCallbackの登録後、実際にAudioSystem(native)クラスに登録する.
//AudioSystem.cpp
/*static*/ void AudioSystem::setRecordConfigCallback(record_config_callback cb)
{
Mutex::Autolock _l(gLock);
gRecordConfigCallback = cb;
}
void AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate(
...
record_config_callback cb = NULL;
{
Mutex::Autolock _l(AudioSystem::gLock);
cb = gRecordConfigCallback;
}
...
cb(event, clientInfo, clientConfig, deviceConfig, patchHandle);
...
}
つまり、onRecordingConfigurationUpdateをコールバックすると、onRecordingConfigChangedコールバックが受信されます.
グローバル検索後、この2つの場所がこのonRecordingConfigurationUpdateメソッドにコールバックすることがわかりました.
//frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
uint32_t AudioSession::changeActiveCount(int delta)
{
...
mClientInterface->onRecordingConfigurationUpdate(event, &mRecordClientInfo,
&mConfig, &deviceConfig, patchHandle);
...
}
void AudioSession::onSessionInfoUpdate() const
{
...
mClientInterface->onRecordingConfigurationUpdate(RECORD_CONFIG_EVENT_START,
&mRecordClientInfo, &mConfig, &deviceConfig, patchHandle);
...
}
changeActiveCount
//frameworks/av/services/audiopolicy/managerdefault/
AudioPolicyManager.cpp
2036 audioSession->changeActiveCount(1); in startInput()
2107 audioSession->changeActiveCount(-1); in stopInput()
簡単に説明を書きましょう.もう明らかですが.APMがstartInputを呼び出すと、appはコールバックを受け取ります.パラメータは1です.stopInputの場合のコールバックパラメータは-1.パラメータの意味については、次を参照してください.
AudioSession::ActiveCount関数を変更します.
onSessionInfoUpdate
調べましたが、直接showでスタックを下げます.
status_t AudioPolicyManager::setInputDevice
inputDesc->setPatchHandle(patchDesc->mHandle);
mSessions.onSessionInfoUpdate();
私の現在の理解では、主にcreateAudioPatchという状況に使用されています.これは私もよく知らないので,売らない.
2.hfp電話に接続した後、録音はなぜstandbyされるのですか?
hfp通話中に常に開いているRecordThread(updateCallRoutingのcreateAudioPatchによって作成された)が2つ存在するのはなぜですか?
git logで見たのはusb voice callのためとか
この段落は実はよく分からなかった.自分ででたらめに直したようだ.私のやり方はupdateCallRoutingを注釈することです.どうせusb voice callは使いません.
2番目の問題を防止するには、次の3番目の問題に従って操作します.
3.電話が通じたとき、録音を一時停止し、電話が切れたとき、録音を再開するにはどうすればいいですか.
この問題は比較的に簡単で、直接私の書いたごみのコードに行きます
private class MyPhoneStateListener extends PhoneStateListener {
String TAG = "CaptureService.MyPhoneStateListener";
@Override
public void onCallStateChanged(int state, String phoneNumber) {
super.onCallStateChanged(state, phoneNumber);
Log.d(TAG, "MyPhoneStateListener state: "+ String.valueOf(state));
switch (state) {
case CALL_STATE_IDLE:
//
mCaptureThread.startCapturing();
break;
case TelephonyManager.CALL_STATE_RINGING:
Log.d(TAG, "CustomPhoneStateListener onCallStateChanged endCall");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//
mCaptureThread.stopCapturing();
break;
}
}
}
すみません、虎頭蛇尾です.