当前位置:   article > 正文

Android FM流程分析_安卓系统收音机 代码流程

安卓系统收音机 代码流程

一,FM主要类介绍

FmMainActivity.java 主界面

FmService.java  核心

FmNative.java  调用JNI和底层通信

二,FM主要功能介绍

1,收音播放:

    1 ) 搜台

    2)收藏

    3)耳机/外放播放

    4 ) 切台

    5)定时关闭

    6)飞行模式

2,录音播放

三,流程分析

1,不插入耳机

不插入耳机启动FM,是不能播放FM,会提示 “请插入耳机”

代码流程分析:

在FmMainActivity.java-->onCreate()中会加载布局

  1. @Override
  2. public void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. ......
  5. initUiComponent();
  6. ......
  7. }

FmMainActivity.java-->initUiComponent()

  1. private void initUiComponent() {
  2. ......
  3. mMainLayout = (LinearLayout) findViewById(R.id.main_view);
  4. mNoHeadsetLayout = (RelativeLayout) findViewById(R.id.no_headset);
  5. ......
  6. }

这里加载了两个布局no_headset和main_view,main_view是插入耳机正常收听时的界面,no_headset是不插入耳机时的提示界面,我们现在先分析no_headset

在FmMainActivity.java-->changeToNoHeadsetLayout()中会将mMainLayout隐藏掉,将mNoHeadsetLayout设置成visible

  1. /**
  2. * change to no headset layout
  3. */
  4. private void changeToNoHeadsetLayout() {
  5. ......
  6. mMainLayout.setVisibility(View.GONE);
  7. ......
  8. mNoHeadsetLayout.setVisibility(View.VISIBLE);
  9. ......
  10. }

接下来看下changeToNoHeadsetLayout()是在哪儿调用的

  1. @Override
  2. public void onStart() {
  3. super.onStart();
  4. ......
  5. } else if (isAntennaAvailable()) {
  6. changeToMainLayout();
  7. } else {
  8. changeToNoHeadsetLayout();
  9. }
  10. ......
  11. }

可以看到是通过isAntennaAvailable()来判断是否有耳机

FmMainActivity.java-->isAntennaAvailable()

  1. private boolean isAntennaAvailable() {
  2. if (FmUtils.supportShortAntenna) {
  3. return true;
  4. } else {
  5. return isWiredHeadsetOn();
  6. //mAudioManager.isWiredHeadsetOn();
  7. }
  8. }

FmMainActivity.java-->isWiredHeadsetOn()

  1. private boolean isWiredHeadsetOn() {
  2. //bug939562 return true ,Only wired headset is plugged in
  3. AudioDeviceInfo[] audioDeviceInfo = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
  4. for (AudioDeviceInfo info : audioDeviceInfo) {
  5. //wired headset(with mic or not) is pluged in
  6. if ((info.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET)||(info.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES)){
  7. Log.d(TAG,"Wired headset is exist");
  8. return true;
  9. }
  10. }
  11. return false;
  12. //return true ,type-c or wired headset is plugged in
  13. //mAudioManager.isWiredHeadsetOn();
  14. }

可以看到启动app的时候,是通过AudioDeviceInfo的tpye来判断是否插入了耳机

2.插入耳机

到插入耳机的时候,会切换到main_view这个界面,下面看下这个流程

再看耳机插入监听流程前,需要先讲下FmService的启动流程

  1. @Override
  2. public void onStart() {
  3. super.onStart();
  4. ......
  5. if (null == startForegroundService(new Intent(FmMainActivity.this, FmService.class))) {
  6. Log.e(TAG, "onStart, cannot start FM service");
  7. return;
  8. }
  9. mIsServiceStarted = true;
  10. mIsServiceBinded = bindService(new Intent(FmMainActivity.this, FmService.class),
  11. mServiceConnection, Context.BIND_AUTO_CREATE);
  12. ......
  13. }

在FmService onBind()成功后,后就会调用ServiceConnection里的onServiceConnected()方法

  1. private final ServiceConnection mServiceConnection = new ServiceConnection() {
  2. /**
  3. * called by system when bind service
  4. *
  5. * @param className component name
  6. * @param service service binder
  7. */
  8. @Override
  9. public void onServiceConnected(ComponentName className, IBinder service) {
  10. mService = ((FmService.ServiceBinder) service).getService();
  11. if (null == mService) {
  12. Log.e(TAG, "onServiceConnected, mService is null");
  13. finish();
  14. return;
  15. }
  16. Log.d(TAG, "onServiceConnected, mService is not null");
  17. refreshRDSVisiable();
  18. mService.registerFmRadioListener(mFmRadioListener);
  19. mService.setFmActivityForground(mIsActivityForeground);
  20. // bug568587, new feature FM new UI
  21. mService.setTimerListenr(new MyTimeCountListener());
  22. if (!mService.isServiceInited()) {
  23. mService.initService();
  24. powerUpFm();
  25. } else {
  26. if (mService.isDeviceOpen()) {
  27. updateMenuStatus();
  28. } else {
  29. // Normal case will not come here
  30. // Need to exit FM for this case
  31. exitService();
  32. finish();
  33. }
  34. }
  35. }
  36. /**
  37. * When unbind service will call this method
  38. *
  39. * @param className The component name
  40. */
  41. @Override
  42. public void onServiceDisconnected(ComponentName className) {
  43. }
  44. };

在这里会调用FmService.java-->registerFmRadioListener()监听mFmRadioListener

  1. private FmListener mFmRadioListener = new FmListener() {
  2. @Override
  3. public void onCallBack(Bundle bundle) {
  4. int flag = bundle.getInt(FmListener.CALLBACK_FLAG);
  5. if (flag == FmListener.MSGID_FM_EXIT) {
  6. mHandler.removeCallbacksAndMessages(null);
  7. }
  8. // remove tag message first, avoid too many same messages in queue.
  9. Message msg = mHandler.obtainMessage(flag);
  10. msg.setData(bundle);
  11. mHandler.removeMessages(flag);
  12. mHandler.sendMessage(msg);
  13. }
  14. };

FmListener是个接口,FmMainActivity.java实现了这个接口

再反过来看FmService.java-->registerFmRadioListener()

  1. public void registerFmRadioListener(FmListener callback) {
  2. synchronized (mRecords) {
  3. // register callback in AudioProfileService, if the callback is
  4. // exist, just replace the event.
  5. Record record = null;
  6. int hashCode = callback.hashCode();
  7. final int n = mRecords.size();
  8. for (int i = 0; i < n; i++) {
  9. record = mRecords.get(i);
  10. if (hashCode == record.mHashCode) {
  11. return;
  12. }
  13. }
  14. record = new Record();
  15. record.mHashCode = hashCode;
  16. record.mCallback = callback;
  17. mRecords.add(record);
  18. }
  19. }

这里会把FmListener add到一个list里,因为在FmFavoriteActivity FmMainActivity FmRecordActivity 中都会注册监听这个接口

这套监听机制介绍后,就要具体讲FmService.java监听到耳机插拔事件怎么通知给FmMainActivity,让它更新界面了

在FmService.java中有个内部广播接收器FmServiceBroadcastReceiver extends BroadcastReceiver,在FmServiceBroadcastReceiver里会监听Intent.ACTION_HEADSET_PLUG事件

监听到后,调用switchAntennaAsync()

  1. public void switchAntennaAsync(int antenna) {
  2. final int bundleSize = 1;
  3. mFmServiceHandler.removeMessages(FmListener.MSGID_SWITCH_ANTENNA);
  4. Bundle bundle = new Bundle(bundleSize);
  5. bundle.putInt(FmListener.SWITCH_ANTENNA_VALUE, antenna);
  6. Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SWITCH_ANTENNA);
  7. msg.setData(bundle);
  8. mFmServiceHandler.sendMessage(msg);
  9. }
  1. case FmListener.MSGID_SWITCH_ANTENNA:
  2. bundle = msg.getData();
  3. int value = bundle.getInt(FmListener.SWITCH_ANTENNA_VALUE);
  4. // if ear phone insert, need dismiss plugin earphone
  5. // dialog
  6. // if earphone plug out and it is not play recorder
  7. // state, show plug dialog.
  8. bundle.putInt(FmListener.CALLBACK_FLAG,
  9. FmListener.MSGID_SWITCH_ANTENNA);
  10. bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, (0 == value));
  11. notifyActivityStateChanged(bundle);
  12. break;
  1. private void notifyActivityStateChanged(Bundle bundle) {
  2. if (!mRecords.isEmpty()) {
  3. synchronized (mRecords) {
  4. Iterator<Record> iterator = mRecords.iterator();
  5. while (iterator.hasNext()) {
  6. Record record = (Record) iterator.next();
  7. FmListener listener = record.mCallback;
  8. if (listener == null) {
  9. iterator.remove();
  10. return;
  11. }
  12. listener.onCallBack(bundle);
  13. }
  14. }
  15. }
  16. }

到这里就回到了FmMainActivity.java-->FmListener .java-->onCallBack()

  1. private FmListener mFmRadioListener = new FmListener() {
  2. @Override
  3. public void onCallBack(Bundle bundle) {
  4. int flag = bundle.getInt(FmListener.CALLBACK_FLAG);
  5. if (flag == FmListener.MSGID_FM_EXIT) {
  6. mHandler.removeCallbacksAndMessages(null);
  7. }
  8. // remove tag message first, avoid too many same messages in queue.
  9. Message msg = mHandler.obtainMessage(flag);
  10. msg.setData(bundle);
  11. mHandler.removeMessages(flag);
  12. mHandler.sendMessage(msg);
  13. }
  14. };

从bundle里传过来是MSGID_SWITCH_ANTENNA

  1. case FmListener.MSGID_SWITCH_ANTENNA:
  2. ......
  3. if (hasAntenna) {
  4. cancelNoHeadsetAnimation();
  5. if (mIsActivityForeground) {
  6. playMainAnimation();
  7. } else {
  8. changeToMainLayout();
  9. }
  10. ......

2.搜台功能

点击FmFavoriteActivity.java界面的menu菜单,点击刷新

  1. @Override
  2. public boolean onOptionsItemSelected(MenuItem item) {
  3. switch (item.getItemId()) {
  4. case R.id.fm_station_list_refresh:
  5. if ((null != mService) && (!mService.isSeeking())) {
  6. onOptionsItemSelectedScan(false);
  7. }
  8. break;
  9. }
  10. return super.onOptionsItemSelected(item);
  11. }

FmFavoriteActivity.java-->onOptionsItemSelectedScan()

  1. private void onOptionsItemSelectedScan(boolean fromCurrent) {
  2. if (null != mService) {
  3. showTipView(true);
  4. if (fromCurrent) {
  5. mService.startScanAsyncFromCurrent(mCurrentStationItem.stationFreq);
  6. } else {
  7. mService.startScanAsync();
  8. }
  9. }
  10. }

FmService.java-->startScanAsync()

  1. /**
  2. * Scan stations
  3. */
  4. public void startScanAsync() {
  5. mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED);
  6. mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_SCAN_FINISHED);
  7. }

 

  1. // start scan
  2. case FmListener.MSGID_SCAN_FINISHED:
  3. ......
  4. stations = startScan(start_freq);
  5. /**
  6. * @}
  7. */
  8. }

 

  1. private int[] startScan(int start_freq) {
  2. ......
  3. stations = mFmManager.autoScan(start_freq);
  4. ......
  5. return stations;
  6. }

FmManagerSelect.java -->autoScan()

  1. public int[] autoScan(int start_freq) {
  2. if (mIsUseBrcmFmChip) {
  3. return mFmManagerForBrcm.autoScanStation(start_freq);
  4. } else {
  5. short[] stationsInShort = null;
  6. int[] stations = null;
  7. stationsInShort = FmNative.autoScan(start_freq);
  8. if (null != stationsInShort) {
  9. int size = stationsInShort.length;
  10. stations = new int[size];
  11. for (int i = 0; i < size; i++) {
  12. stations[i] = stationsInShort[i];
  13. }
  14. }
  15. return stations;
  16. }
  17. }

这里就调用到FmNative.java-->autoScan()

static native short[] autoScan(int start_freq);

扫描成功后,会将扫描到的数据插入到数据库中

  1. private int[] updateStations(int[] stations) {
  2. Log.d(TAG, "updateStations.firstValidstation:" + Arrays.toString(stations));
  3. int firstValidstation = mCurrentStationItem.stationFreq;
  4. FmStation.cleanSearchedStations(mContext);
  5. int stationNum = batchInsertStationToDb(stations, null);
  6. int searchedNum = (stations == null ? 0 : stations.length);
  7. Log.d(TAG, "updateStations.firstValidstation:" + firstValidstation +
  8. ",searchedNum:" + searchedNum);
  9. return (new int[]{
  10. firstValidstation, searchedNum
  11. });
  12. }
  13. private int batchInsertStationToDb(int[] stations, List<FmStationItem> favoriteList) {
  14. if (null == stations) return 0;
  15. ArrayList<ContentProviderOperation> ops = new ArrayList<>();
  16. ContentResolver resolver = mContext.getContentResolver();
  17. for (int i = 0; i < stations.length; i++) {
  18. if (!FmUtils.isValidStation(stations[i]) || FmStation.isFavoriteStation(mContext, stations[i])) {
  19. Log.d(TAG, "station is favorite : " + stations[i]);
  20. continue;
  21. }
  22. ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(Station.CONTENT_URI);
  23. op.withYieldAllowed(false);
  24. Log.d(TAG, "station : " + stations[i]);
  25. ContentValues values = new ContentValues();
  26. values.clear();
  27. values.put(Station.FREQUENCY, stations[i]);
  28. values.put(Station.IS_SHOW, Station.IS_SCAN);
  29. op.withValues(values);
  30. ops.add(op.build());
  31. }
  32. Log.d(TAG, "ops size : " + ops.size());
  33. int stationNum = ops.size();
  34. if (stationNum > 0) {
  35. try {
  36. ContentProviderResult[] result = resolver.applyBatch(FmStation.AUTHORITY, ops);
  37. ops.clear();
  38. Log.d(TAG, "batch opreate db result count : " + result.length);
  39. } catch (Exception e) {
  40. Log.d(TAG, "Batch operate exception");
  41. e.printStackTrace();
  42. }
  43. } else {
  44. mContext.getContentResolver().notifyChange(FmStation.Station.CONTENT_URI, null);
  45. }
  46. return stationNum;
  47. }

搜索成功后,调用FmService.java-->notifyCurrentActivityStateChanged()通知FmFavoriteActivity.java更新界面

  1. private void refreshFmFavorityList(Cursor cursor) {
  2. if (cursor == null) {
  3. return;
  4. }
  5. mFavoriteList.clear();
  6. mOtherList.clear();
  7. mAllStationSet.clear();
  8. for (int i = 0; i < cursor.getCount(); i++) {
  9. FmStationItem item = new FmStationItem();
  10. if (cursor.moveToFirst()) {
  11. cursor.moveToPosition(i);
  12. item.stationFreq = cursor.getInt(cursor
  13. .getColumnIndex(FmStation.Station.FREQUENCY));
  14. item.stationName = cursor.getString(cursor
  15. .getColumnIndex(FmStation.Station.STATION_NAME));
  16. item.rt = cursor.getString(cursor
  17. .getColumnIndex(FmStation.Station.RADIO_TEXT));
  18. item.isFavorite = cursor.getInt(cursor
  19. .getColumnIndex(FmStation.Station.IS_FAVORITE));
  20. item.isShow = cursor.getInt(cursor
  21. .getColumnIndex(FmStation.Station.IS_SHOW));
  22. item.ps = cursor.getString(cursor
  23. .getColumnIndex(FmStation.Station.PROGRAM_SERVICE));
  24. if (item.isFavorite == 0) {
  25. mOtherList.add(item);
  26. } else {
  27. mFavoriteList.add(item);
  28. }
  29. mAllStationSet.add(item.stationFreq);
  30. }
  31. }
  32. checkSelectedStationSet();
  33. mMyAdapter.notifyDataSetChanged();
  34. Log.d(TAG, "refreshFmFavorityList mAllStationSet.size:" + mAllStationSet.size());
  35. }

通过MyFavoriteAdapter的notifyDataSetChanged()来刷新界面

 

3.收藏

  1. viewHolder.mImgLayout.setOnClickListener(new View.OnClickListener() {
  2. @Override
  3. public void onClick(View v) {
  4. /* bug568587, new feature FM new UI @{ */
  5. if (0 == isFavorite) {
  6. showToast(getString(R.string.toast_channel_added));
  7. addFavorite(stationFreq);
  8. } else {
  9. showToast(getString(R.string.toast_channel_deleted));
  10. deleteFromFavorite(stationFreq);
  11. }
  12. /* @} */
  13. }
  14. });

 

  1. /**
  2. * Add searched station as favorite station
  3. */
  4. public void addFavorite(int stationFreq) {
  5. // TODO it's on UI thread, change to sub thread
  6. // update the station name and station type in database
  7. // according the frequency
  8. operateFmFavoriteStation(stationFreq, ACTION_ADD_FAVORITE_TYPE);
  9. }
  1. private void operateFmFavoriteStation(int stationFreq, int type) {
  2. MyAsyncTask operateTask = new MyAsyncTask();
  3. operateTask.execute(ACTION_OPERATE_TYPE, stationFreq, type);
  4. }
  1. class MyAsyncTask extends AsyncTask<Integer, Void, Cursor> {
  2. @Override
  3. protected Cursor doInBackground(Integer... params) {
  4. Log.d(TAG, "operate database type:" + params[0]);
  5. if (params[0] == ACTION_QUERY_TYPE) {
  6. Cursor cursor = getData();
  7. return cursor;
  8. } else {
  9. Log.d(TAG, "stationFreq=" + params[1]);
  10. operateData(params[1], params[2]);
  11. return null;
  12. }
  13. }
  14. @Override
  15. protected void onPostExecute(Cursor cursor) {
  16. refreshFmFavorityList(cursor);
  17. if (cursor != null) {
  18. cursor.close();
  19. }
  20. }
  21. }
  1. private void operateData(int stationFreq, int type) {
  2. switch (type) {
  3. case ACTION_ADD_FAVORITE_TYPE:
  4. FmStation.addToFavorite(mContext, stationFreq);
  5. break;
  6. case ACTION_REMOVE_FROM_FAVORITE_TYPE:
  7. FmStation.removeFromFavorite(mContext, stationFreq);
  8. break;
  9. }
  1. /**
  2. * update db to mark it is a favorite frequency
  3. *
  4. * @param context The context
  5. * @param frequency The target frequency
  6. */
  7. public static void addToFavorite(Context context, int frequency) {
  8. ContentValues values = new ContentValues(1);
  9. values.put(Station.IS_FAVORITE, true);
  10. values.put(Station.IS_SHOW,Station.IS_SCAN);
  11. context.getContentResolver().update(
  12. Station.CONTENT_URI,
  13. values,
  14. Station.FREQUENCY + "=?",
  15. new String[] {
  16. String.valueOf(frequency)
  17. });
  18. }

4.播放流程

FmMainActivity.java

  1. case R.id.play_button:
  2. /**
  3. * bug500046, for monkey test.
  4. *
  5. * @{
  6. */
  7. if (null == mService) {
  8. Log.e(TAG, "onClick case playbutton, mService is null");
  9. return;
  10. }
  11. /**
  12. * @}
  13. */
  14. if (mService.getPowerStatus() == FmService.POWER_UP) {
  15. if(mService.isMuted()){
  16. mService.setMuteAsync(false,false);
  17. }else{
  18. powerDownFm();
  19. }
  20. } else {
  21. powerUpFm();
  22. }
  23. break;
  24. default:
  25. Log.d(TAG, "mButtonClickListener.onClick, invalid view id");
  26. break;

FmMainActivity.java -- >powerUpFm()

  1. /**
  2. * Power up FM
  3. */
  4. private void powerUpFm() {
  5. if(FmUtils.isAirplane(this)){
  6. changeToAirPlaneLayout();
  7. }else{
  8. refreshImageButton(false);
  9. refreshActionMenuItem(false);
  10. refreshPopupMenuItem(false);
  11. refreshPlayButton(false);
  12. mService.powerUpAsync(FmUtils.computeFrequency(mCurrentStationItem.stationFreq));
  13. }
  14. }

发送MSGID_POWERUP_FINISHED

  1. public void powerUpAsync(float frequency) {
  2. final int bundleSize = 1;
  3. mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED);
  4. mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED);
  5. Bundle bundle = new Bundle(bundleSize);
  6. bundle.putFloat(FM_FREQUENCY, frequency);
  7. Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_POWERUP_FINISHED);
  8. msg.setData(bundle);
  9. mFmServiceHandler.sendMessage(msg);
  10. }

FmService.java -->powerUpAsync()

  1. // power up
  2. case FmListener.MSGID_POWERUP_FINISHED:
  3. bundle = msg.getData();
  4. handlePowerUp(bundle);
  5. break;

FmService.java -->handlePowerUp()

  1. private void handlePowerUp(Bundle bundle) {
  2. boolean isPowerUp = false;
  3. boolean isSwitch = true;
  4. float curFrequency = bundle.getFloat(FM_FREQUENCY);
  5. if (FmUtils.isAirplane(this)) {
  6. Log.d(TAG, "handlePowerUp, airplane is on");
  7. return;
  8. }
  9. boolean isAntennaAvailable = isAntennaAvailable();
  10. if (!FmUtils.supportShortAntenna) {
  11. if (!isAntennaAvailable) {
  12. Log.d(TAG, "handlePowerUp, earphone is not ready");
  13. bundle = new Bundle(2);
  14. bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_SWITCH_ANTENNA);
  15. bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, false);
  16. notifyActivityStateChanged(bundle);
  17. return;
  18. }
  19. } else {
  20. Log.d(TAG, "handlePowerUp: wether earphone is plugged in -->" + isAntennaAvailable);
  21. switchAntenna(isAntennaAvailable ? 0 : 1);
  22. }
  23. if (powerUp(curFrequency)) {
  24. if (FmUtils.isFirstTimePlayFm(mContext)) {
  25. isPowerUp = firstPlaying(curFrequency);
  26. FmUtils.setIsFirstTimePlayFm(mContext);
  27. } else {
  28. /**
  29. * Bug546461 Tune radio again after power down then power up for brcm. Orig:
  30. * isPowerUp = playFrequency(curFrequency);
  31. * @{
  32. */
  33. if (mFmManager.tuneRadioAgain(curFrequency)) {
  34. isPowerUp = playFrequency(curFrequency);
  35. }
  36. /**
  37. * @}
  38. */
  39. }
  40. mPausedByTransientLossOfFocus = false;
  41. }
  42. bundle = new Bundle(1);
  43. bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERUP_FINISHED);
  44. notifyActivityStateChanged(bundle);
  45. }

FmService.java -->playFrequency()

  1. private boolean playFrequency(float frequency) {
  2. Log.d(TAG, "playFrequency");
  3. updatePlayingNotification();
  4. // Start the RDS thread if RDS is supported.
  5. // RDS function should be open status.
  6. if (isRdsSupported() && FmUtils.isRDSOpen(mContext)) {
  7. startRdsThread();
  8. }
  9. //bug492835, FM audio route change. if (!mWakeLock.isHeld()) { mWakeLock.acquire(); }
  10. if (mIsSpeakerUsed) {
  11. setForceUse(mIsSpeakerUsed);
  12. }
  13. enableFmAudio(true);
  14. setRds(true);
  15. setMute(false);
  16. return (mPowerStatus == POWER_UP);
  17. }

FmService.java -->enableFmAudio()

  1. private void enableFmAudio(boolean enable) {
  2. Log.d(TAG, "enableFmAudio:" + enable);
  3. if (enable) {
  4. if ((mPowerStatus != POWER_UP) || !mIsAudioFocusHeld) {
  5. Log.d(TAG, "enableFmAudio, current not available return.mIsAudioFocusHeld:"
  6. + mIsAudioFocusHeld);
  7. return;
  8. }
  9. ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
  10. mAudioManager.listAudioPatches(patches);
  11. if (mAudioPatch == null) {
  12. Log.d(TAG, "mAudioPatch == null");
  13. if (isPatchMixerToEarphone(patches)) {
  14. int status;
  15. stopRender();
  16. status = createAudioPatch();
  17. if (status != AudioManager.SUCCESS) {
  18. Log.d(TAG, "enableFmAudio: fallback as createAudioPatch failed");
  19. startRender();
  20. }
  21. } else {
  22. startRender();
  23. }
  24. }
  25. } else {
  26. releaseAudioPatch();
  27. stopRender();
  28. }
  29. }

FmService.java -->startRender()

  1. private synchronized void startRender() {
  2. //bug492835, FM audio route change.
  3. mFmManager.setAudioPathEnable(AudioPath.FM_AUDIO_PATH_HEADSET, true);
  4. mMediaSessionManager.setOnMediaKeyListener(mMediaKeyListener, null);
  5. Log.d(TAG, "startRender,setOnMediaKeyListener");
  6. mIsRender = true;
  7. synchronized (mRenderLock) {
  8. mRenderLock.notify();
  9. }
  10. }

FmManagerSelect.java -->setAudioPathEnable()

  1. public boolean setAudioPathEnable(AudioPath path, boolean enable) {
  2. if (mIsUseBrcmFmChip) {
  3. if (enable) {
  4. mFmManagerForBrcm.setAudioMode(FmAudioModeForBrcm.FM_AUDIO_MODE_BLEND);
  5. //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
  6. // AudioSystem.DEVICE_STATE_AVAILABLE, "", "");
  7. mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
  8. AudioSystem.DEVICE_STATE_AVAILABLE, "", "");
  9. } else {
  10. //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
  11. // AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
  12. mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
  13. AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
  14. }
  15. return mFmManagerForBrcm.setAudioPath(convertAudioPathForBrcm(path));
  16. } else {
  17. if (enable) {
  18. //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
  19. mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
  20. AudioSystem.DEVICE_STATE_AVAILABLE, "", "");
  21. return true;
  22. } else {
  23. //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
  24. mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
  25. AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
  26. return true;
  27. }
  28. }
  29. }

AudioManager.java-->setDeviceConnectionStateForFM()

  1. public void setDeviceConnectionStateForFM(int device, int state,
  2. String device_address, String device_name) {
  3. final IAudioService service = getService();
  4. try {
  5. service.setDeviceConnectionStateForFM(device, state, device_address, device_name,mICallBack);
  6. } catch (RemoteException e) {
  7. Log.e(TAG, "Dead object in setDeviceConnectionStateForFM ", e);
  8. }
  9. }

二,录音

录音是FmRecordActivity

点击start,调用 mService.startRecordingAsync();

  1. /**
  2. * Start recording
  3. */
  4. public void startRecordingAsync() {
  5. mFmServiceHandler.removeMessages(FmListener.MSGID_STARTRECORDING_FINISHED);
  6. mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STARTRECORDING_FINISHED);
  7. }
  1. case FmListener.MSGID_STARTRECORDING_FINISHED:
  2. startRecording();
  3. break;
  1. public void startRecording(Context context) {
  2. mRecordTime = 0;
  3. Uri dir = null;
  4. FileDescriptor fd = null;
  5. Uri externalStorageUri=null;
  6. /**
  7. * Bug529776 Check the main card state Original Android code:
  8. *
  9. * @{ // Check external storage if
  10. * (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
  11. * Log.e(TAG, "startRecording, no external storage available");
  12. * setError(ERROR_SDCARD_NOT_PRESENT); return; } /** @}
  13. */
  14. if (FmUtils.isExternalStorage()) {
  15. externalStorageUri =FmUtils.getCurrentAccessUri(FmUtils.FM_RECORD_STORAGE_PATH_NAME);
  16. }
  17. String recordingSdcard = FmUtils.getDefaultStoragePath();
  18. Log.d(TAG,"externalStorageUri="+externalStorageUri+" recordingSdcard="+recordingSdcard);
  19. /**
  20. * bug474747, recording path selection.
  21. *
  22. * @{
  23. */
  24. if (recordingSdcard == null
  25. || recordingSdcard.isEmpty()
  26. || (FmUtils.FM_RECORD_STORAGE_PATH_SDCARD.equals(FmUtils.FM_RECORD_STORAGE_PATH_NAME) && !Environment.MEDIA_MOUNTED
  27. .equals(EnvironmentEx.getExternalStoragePathState()))) {
  28. Log.e(TAG, "startRecording, no sdcard storage available");
  29. setError(ERROR_SDCARD_NOT_PRESENT);
  30. return;
  31. }
  32. /**
  33. * @}
  34. */
  35. // check whether have sufficient storage space, if not will notify
  36. // caller error message
  37. if (!FmUtils.hasEnoughSpace(recordingSdcard)) {
  38. setError(ERROR_SDCARD_INSUFFICIENT_SPACE);
  39. Log.e(TAG, "startRecording, SD card does not have sufficient space!!");
  40. return;
  41. }
  42. // get external storage directory
  43. File sdDir = new File(recordingSdcard);
  44. File recordingDir = new File(sdDir, FM_RECORD_FOLDER);
  45. // exist a file named FM Recording, so can't create FM recording folder
  46. if (recordingDir.exists() && !recordingDir.isDirectory()) {
  47. Log.e(TAG, "startRecording, a file with name \"" + FM_RECORD_FOLDER + "\" already exists!!");
  48. setError(ERROR_SDCARD_WRITE_FAILED);
  49. return;
  50. } else if (!recordingDir.exists()) { // try to create recording folder
  51. if (FmUtils.isExternalStorage()) {
  52. dir = getPathUri(context,recordingDir,externalStorageUri);
  53. } else {
  54. boolean mkdirResult = recordingDir.mkdir();
  55. if (!mkdirResult) { // create recording file failed
  56. setError(ERROR_RECORDER_INTERNAL);
  57. return;
  58. }
  59. }
  60. } else {
  61. if (FmUtils.isExternalStorage()) {
  62. dir = getPathUri(context,recordingDir,externalStorageUri);
  63. }
  64. }
  65. // create recording temporary file
  66. long curTime = System.currentTimeMillis();
  67. Date date = new Date(curTime);
  68. SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMddyyyy_HHmmss",
  69. Locale.ENGLISH);
  70. String time = simpleDateFormat.format(date);
  71. StringBuilder stringBuilder = new StringBuilder();
  72. /**
  73. * bug474741, recording format selection.
  74. *
  75. * @{
  76. */
  77. if (GLOBAL_RECORD_FORMAT_FLAG == 1) {
  78. stringBuilder.append(".").append(time).append(RECORDING_FILE_EXTENSION_3GPP).append(".tmp");
  79. } else if(GLOBAL_RECORD_FORMAT_FLAG == 2) {
  80. stringBuilder.append(".").append(time).append(RECORDING_FILE_EXTENSION_MP3).append(".tmp");
  81. } else {
  82. stringBuilder.append(".").append(time).append(RECORDING_FILE_EXTENSION_AMR).append(".tmp");
  83. }
  84. /**
  85. * @}
  86. */
  87. String name = stringBuilder.toString();
  88. mRecordFile = new File(recordingDir, name);
  89. if (FmUtils.isExternalStorage()) {
  90. try {
  91. Uri fileDoc = DocumentsContract.createDocument(context.getContentResolver(),dir
  92. ,DocumentsContract.Document.COLUMN_MIME_TYPE,name);
  93. FmUtils.saveTmpFileUri(fileDoc.toString());
  94. mPfd = context.getContentResolver().openFileDescriptor(fileDoc,"w");
  95. fd = mPfd.getFileDescriptor();
  96. if(fd == null) {
  97. Log.e(TAG,"fd is NULL");
  98. throw new IllegalArgumentException("Memory related error");
  99. }
  100. } catch(FileNotFoundException e) {
  101. Log.d(TAG,""+e);
  102. }
  103. } else {
  104. try {
  105. if (mRecordFile.createNewFile()) {
  106. Log.d(TAG, "startRecording, createNewFile success with path "
  107. + mRecordFile.getPath());
  108. }
  109. } catch (IOException e) {
  110. Log.e(TAG, "startRecording, IOException while createTempFile: " + e);
  111. e.printStackTrace();
  112. setError(ERROR_SDCARD_WRITE_FAILED);
  113. return;
  114. }
  115. }
  116. // set record parameter and start recording
  117. try {
  118. mRecorder = new MediaRecorder();
  119. mRecorder.setOnErrorListener(this);
  120. mRecorder.setOnInfoListener(this);
  121. // mRecorder.setAudioSource(MediaRecorder.AudioSource.RADIO_TUNER);
  122. mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
  123. /**
  124. * bug474741, recording format selection. Original Android code:
  125. * mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
  126. * mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); final int samplingRate =
  127. * 44100; mRecorder.setAudioSamplingRate(samplingRate); final int bitRate = 128000;
  128. * mRecorder.setAudioEncodingBitRate(bitRate); final int audiochannels = 2;
  129. * mRecorder.setAudioChannels(audiochannels);
  130. *
  131. * @{
  132. */
  133. if (1 == GLOBAL_RECORD_FORMAT_FLAG) {
  134. Log.d(TAG, "global_record_format: 3gpp");
  135. mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
  136. mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
  137. mRecorder.setAudioSamplingRate(44100);
  138. mRecorder.setAudioEncodingBitRate(128000);
  139. } else if(2 == GLOBAL_RECORD_FORMAT_FLAG) {
  140. Log.d(TAG, "global_record_format: mp3");
  141. mRecorder.setOutputFormat(11);
  142. mRecorder.setAudioEncoder(7);
  143. mRecorder.setAudioSamplingRate(44100);
  144. mRecorder.setAudioEncodingBitRate(320000);
  145. mRecorder.setAudioChannels(2);
  146. } else {
  147. Log.d(TAG, "global_record_format: amr_nb");
  148. mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
  149. mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
  150. mRecorder.setAudioSamplingRate(8000);
  151. mRecorder.setAudioEncodingBitRate(5900);
  152. }
  153. /**
  154. * @}
  155. */
  156. if (FmUtils.isExternalStorage()) {
  157. mRecorder.setOutputFile(fd);
  158. } else {
  159. mRecorder.setOutputFile(mRecordFile.getAbsolutePath());
  160. }
  161. mRecorder.prepare();
  162. mRecordStartTime = SystemClock.elapsedRealtime();
  163. mRecorder.start();
  164. mIsRecordingFileSaved = false;
  165. mFileListener= new FileListener(recordingDir.getPath());
  166. mFileListener.startWatching();
  167. } catch (IllegalStateException e) {
  168. Log.e(TAG, "startRecording, IllegalStateException while starting recording!", e);
  169. setError(ERROR_RECORDER_INTERNAL);
  170. return;
  171. } catch (IOException e) {
  172. Log.e(TAG, "startRecording, IOException while starting recording!", e);
  173. setError(ERROR_RECORDER_INTERNAL);
  174. return;
  175. }
  176. setState(STATE_RECORDING);
  177. }
  178. /**
  179. *Listening whether recording file is delete or not.
  180. *@}
  181. */
  182. private class FileListener extends FileObserver {
  183. public FileListener(String path) {
  184. super(path, FileObserver.MOVED_FROM | FileObserver.DELETE);
  185. Log.d(TAG, "FileListener path="+path);
  186. }
  187. @Override
  188. public void onEvent(int event, String path) {
  189. Log.d(TAG, "onEvent: event = "+event+"; path = "+path);
  190. switch (event) {
  191. case FileObserver.MOVED_FROM:
  192. case FileObserver.DELETE:
  193. if (path == null) {
  194. return ;
  195. }
  196. if ( getTmpFileName().equals(path)) {
  197. Log.d(TAG, "recording tmp file is deleted");
  198. discardRecording();
  199. }
  200. break;
  201. default:
  202. break;
  203. }
  204. }
  205. }

录音结束

  1. private void stopRecorder() {
  2. ......
  3. mRecorder.stop();
  4. ......
  5. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/250879
推荐阅读
相关标签
  

闽ICP备14008679号