最近在学习FM模块,FM是一个值得学习的模块,可以从上层看到底层。上层就是FM的按扭操作和界面显示,从而调用到FM底层驱动来实现广播收听的功能。
看看Fm启动流程:如下图:
先进入FMRadio.java类,onCreate初始化一些数据,画出FM界面,启动fm在onStart()方法里启动FMRadioService.java (调用bindToService(this, osc)方法)。
注册下fm设置(在设置后发送一个设置广播,更新FMRadio类的状态)。
加载初始化数据,获取频率地址
newPresetStation("",FmSharedPreferences.getTunedFrequency());
在bindToService(this,osc)方法中,先启动StartService(同一个Service只onCreate一次),再启动bindservice(这样有个好处按返回键service不会走onDestroy方法)bindservice通过onBind回传一个IBinder对象到FMRadio类的内部类ServiceConnection的onServiceConnected方法中,调用enableRadio()方法。
在enableRaido方法中调用FMRadio.java的isAntennaAvailable()方法进行耳机判断,天线判断是否可用,通过一个插入拔出广播接收来控制的(FMRadio中的registerHeadsetListener()方法)action(Intent.ACTION_HEADSET_PLUG)
mHeadsetPlugged =(intent.getIntExtra("state", 0) == 1); 等于1说明耳机可用,等于0可用。
调用FmRadio方法FmOn (mService.fmOn())
界面可用enableRadioOnOffUI()
private void enableRadio() {
mIsScaning = false;
mIsSeeking = false;
mIsSearching = false;
boolean bStatus = false;
if (isHdmiOn()) {
showDialog(DIALOG_CMD_FAILED_HDMI_ON);
}else {
if (mService != null) {
try {
if((false == mService.isFmOn()) && isAntennaAvailable()) {
bStatus = mService.fmOn();
if(bStatus) {
tuneRadio(FmSharedPreferences.getTunedFrequency());
enableRadioOnOffUI();
}else {Log.e(LOGTAG, "mService.fmOn failed");
mCommandFailed = CMD_FMON;
if(isCallActive()) {
enableRadioOnOffUI();
showDialog(DIALOG_CMD_FAILED_CALL_ON);
}else {
showDialog(DIALOG_CMD_FAILED);
}
}
}else {enableRadioOnOffUI();
}
}catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
在FMRadioService.java的fmOn()方法中初始化FmReceiver的引用mReceiver = newFmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks);
取出设置保存的地区频率的属性 FmConfig config =FmSharedPreferences.getFMConfiguration();
真正接受fm声音在 bStatus =mReceiver.enable(FmSharedPreferences.getFMConfiguration());
isSpeakerEnabled()扬声器可用,用户设置扬声器
/*
* Turn ON FM: Powers up FM hardware, and initializes the FM module
* .
* @return true if fm Enable api was invoked successfully, false if the api failed.
*/
private boolean fmOn() {
boolean bStatus=false;
mWakeLock.acquire(10*1000);
if ( TelephonyManager.CALL_STATE_IDLE != getCallState() ) {
return bStatus;
}
if(mReceiver == null)
{
try {
mReceiver = new FmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks);
}
catch (InstantiationException e)
{
throw new RuntimeException("FmReceiver service not available!");
}
}
if (mReceiver != null)
{
if (isFmOn())
{
/* FM Is already on,*/
bStatus = true;
Log.d(LOGTAG, "mReceiver.already enabled");
}
else
{ // This sets up the FM radio device
FmConfig config = FmSharedPreferences.getFMConfiguration();
Log.d(LOGTAG, "fmOn: RadioBand :"+ config.getRadioBand());
Log.d(LOGTAG, "fmOn: Emphasis :"+ config.getEmphasis());
Log.d(LOGTAG, "fmOn: ChSpacing :"+ config.getChSpacing());
Log.d(LOGTAG, "fmOn: RdsStd :"+ config.getRdsStd());
Log.d(LOGTAG, "fmOn: LowerLimit :"+ config.getLowerLimit());
Log.d(LOGTAG, "fmOn: UpperLimit :"+ config.getUpperLimit());
bStatus = mReceiver.enable(FmSharedPreferences.getFMConfiguration());
if (isSpeakerEnabled()) {
setAudioPath(false);
} else {setAudioPath(true);
}
Log.d(LOGTAG, "mReceiver.enable done, Status :" + bStatus);
}
if (bStatus == true)
{
/* Put the hardware into normal mode */
bStatus = setLowPowerMode(false);
Log.d(LOGTAG, "setLowPowerMode done, Status :" + bStatus);
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if( (audioManager != null) &&(false == mPlaybackInProgress) )
{
Log.d(LOGTAG, "mAudioManager.setFmRadioOn = true \n" );
//audioManager.setParameters("FMRadioOn="+mAudioDevice);
int state = getCallState();
if ( TelephonyManager.CALL_STATE_IDLE != getCallState() )
{
fmActionOnCallState(state);
} else {
startFM(); // enable FM Audio only when Call is IDLE
}
Log.d(LOGTAG, "mAudioManager.setFmRadioOn done \n" );
}if (mReceiver != null) {//注册远程组的处理
bStatus = mReceiver.registerRdsGroupProcessing(FmReceiver.FM_RX_RDS_GRP_RT_EBL|
FmReceiver.FM_RX_RDS_GRP_PS_EBL|
FmReceiver.FM_RX_RDS_GRP_AF_EBL|
FmReceiver.FM_RX_RDS_GRP_PS_SIMPLE_EBL);
Log.d(LOGTAG, "registerRdsGroupProcessing done, Status :" + bStatus);
}
bStatus = enableAutoAF(FmSharedPreferences.getAutoAFSwitch());//可用自动跳转到选着的频率
Log.d(LOGTAG, "enableAutoAF done, Status :" + bStatus);
/* There is no internal Antenna*/
bStatus = mReceiver.setInternalAntenna(false);//将内置天线设为0
Log.d(LOGTAG, "setInternalAntenna done, Status :" + bStatus);
/* Read back to verify the internal Antenna mode*/
readInternalAntennaAvailable();
startNotification();
bStatus = true;
}
else
{mReceiver = null; // as enable failed no need to disable
// failure of enable can be because handle
// already open which gets effected if
// we disable
stop();
}
}
return(bStatus);
}
设置铃声路径 boolean state =mReceiver.setAnalogMode(analogMode);
private boolean setAudioPath(boolean analogMode) {
if (mReceiver == null) {
return false;
}
if (isAnalogModeEnabled() == analogMode) {
Log.d(LOGTAG,"Analog Path already is set to "+analogMode);
return false;
}
if (!isAnalogModeSupported()) {
Log.d(LOGTAG,"Analog Path is not supported ");
return false;
}
if (SystemProperties.getBoolean("hw.fm.digitalpath",false)) {
return false;
}
boolean state = mReceiver.setAnalogMode(analogMode);
if (false == state) {
Log.d(LOGTAG, "Error in toggling analog/digital path " + analogMode);
return false;
}
misAnalogPathEnabled = analogMode;
return true;
}
analogMode模拟设置低功率 bStatus = setLowPowerMode(false);
电话不在闲置状太下 int state = getCallState();
fmActionOnCallState(state);
启动FM startFM();
private void startFM(){
Log.d(LOGTAG, "In startFM");
if(true == mAppShutdown) { // not to send intent to AudioManager in Shutdown
return;
}
if (isCallActive()) { // when Call is active never let audio playback
mResumeAfterCall = true;
return;
}
mResumeAfterCall = false;
if ( true == mPlaybackInProgress ) // no need to resend event
return;
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int granted = audioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
if(granted != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(LOGTAG, "audio focuss couldnot be granted");
return;
}
Log.d(LOGTAG,"FM registering for registerMediaButtonEventReceiver");
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
ComponentName fmRadio = new ComponentName(this.getPackageName(),
FMMediaButtonIntentReceiver.class.getName());
mAudioManager.registerMediaButtonEventReceiver(fmRadio);
mStoppedOnFocusLoss = false;
if (!isSpeakerEnabled() && !mA2dpDeviceSupportInHal && (true == mA2dpDeviceState.isDeviceAvailable()) &&
!isAnalogModeEnabled()
&& (true == startA2dpPlayback())) {
mOverA2DP=true;
Log.d(LOGTAG, "Audio source set it as A2DP");
AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_BT_A2DP);
} else {
Log.d(LOGTAG, "FMRadio: Requesting to start FM");
//reason for resending the Speaker option is we are sending
//ACTION_FM=1 to AudioManager, the previous state of Speaker we set
//need not be retained by the Audio Manager.
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,
AudioSystem.DEVICE_STATE_AVAILABLE, "");//Fm设备
if (isSpeakerEnabled()) {
mSpeakerPhoneOn = true;
Log.d(LOGTAG, "Audio source set it as speaker");
AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_SPEAKER);
} else {
Log.d(LOGTAG, "Audio source set it as headset");
AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE);
}
}
sendRecordServiceIntent(RECORD_START);
mPlaybackInProgress = true;
}
AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,AudioSystem.FORCE_NONE);
Fm设备可用 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM,
AudioSystem.DEVICE_STATE_AVAILABLE, "");
注册远程组的处理
bStatus = mReceiver.registerRdsGroupProcessing(FmReceiver.FM_RX_RDS_GRP_RT_EBL|
FmReceiver.FM_RX_RDS_GRP_PS_EBL|
FmReceiver.FM_RX_RDS_GRP_AF_EBL|
FmReceiver.FM_RX_RDS_GRP_PS_SIMPLE_EBL);
可用自动跳转到选着的频率 bStatus =enableAutoAF(FmSharedPreferences.getAutoAFSwitch());
将内置天线设为0 FmTransceiver.java
mReceiver.setInternalAntenna(false)
FmReceiverJNI.setControlNative (sFd, V4L2_CID_PRIVATE_TAVARUA_ANTENNA,iAntenna)
/*==============================================================
FUNCTION: setInternalAntenna
==============================================================*/
/**
* Returns true if successful, false otherwise
*
*
* This method sets internal antenna type to true/false
*
* @param intAntenna true is Internal antenna is present
*
*
* @return true/false
*/
public boolean setInternalAntenna(boolean intAnt)
{
int iAntenna ;
if (intAnt)
iAntenna = 1;
else
iAntenna = 0;
int re = FmReceiverJNI.setControlNative (sFd, V4L2_CID_PRIVATE_TAVARUA_ANTENNA, iAntenna);
if (re == 0)
return true;
return false;
}