最近在學習FM模塊,FM是一個值得學習的模塊,能夠從上層看到底層。上層就是FM的按扭操做和界面顯示,從而調用到FM底層驅動來實現廣播收聽的功能。java
看看Fm啓動流程:以下圖:git
先進入FMRadio.java類,onCreate初始化一些數據,畫出FM界面,啓動fm在onStart()方法裏啓動FMRadioService.java (調用bindToService(this, osc)方法)。api
註冊下fm設置(在設置後發送一個設置廣播,更新FMRadio類的狀態)。學習
加載初始化數據,獲取頻率地址ui
newPresetStation("",FmSharedPreferences.getTunedFrequency());this
在bindToService(this,osc)方法中,先啓動StartService(同一個Service只onCreate一次),再啓動bindservice(這樣有個好處按返回鍵service不會走onDestroy方法)bindservice經過onBind回傳一個IBinder對象到FMRadio類的內部類ServiceConnection的onServiceConnected方法中,調用enableRadio()方法。.net
在enableRaido方法中調用FMRadio.java的isAntennaAvailable()方法進行耳機判斷,天線判斷是否可用,經過一個插入拔出廣播接收來控制的(FMRadio中的registerHeadsetListener()方法)action(Intent.ACTION_HEADSET_PLUG)3d
mHeadsetPlugged =(intent.getIntExtra("state", 0) == 1); 等於1說明耳機可用,等於0可用。orm
調用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()</strong>) {
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;
}
設置耳機等能夠接受fm聲音
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)
/**
* Returns true if successful, false otherwise
*
* <p>
* This method sets internal antenna type to true/false
*
* @param intAntenna true is Internal antenna is present
*
* <p>
* @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;
}
好,到此爲止,FM的啓動工做基本上就完成了。接下來就須要去搜索頻道了,後續會繼續分析FM搜索。 ---------------------