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()中会加载布局

   @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ......
        initUiComponent();
        ......

    }

FmMainActivity.java-->initUiComponent()

    private void initUiComponent() {
        ......
        mMainLayout = (LinearLayout) findViewById(R.id.main_view);
        mNoHeadsetLayout = (RelativeLayout) findViewById(R.id.no_headset);
        ......

    }

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

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

    /**
     * change to no headset layout
     */
    private void changeToNoHeadsetLayout() {
        ......
        mMainLayout.setVisibility(View.GONE);
        ......

        mNoHeadsetLayout.setVisibility(View.VISIBLE);
        ......
    }

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

    @Override
    public void onStart() {
        super.onStart();
        ......
 
            } else if (isAntennaAvailable()) {
                changeToMainLayout();
            } else {
                changeToNoHeadsetLayout();
            }

        ......
    }

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

FmMainActivity.java-->isAntennaAvailable()

    private boolean isAntennaAvailable() {
        if (FmUtils.supportShortAntenna) {
            return true;
        } else {
            return isWiredHeadsetOn();
            //mAudioManager.isWiredHeadsetOn();
        }
    }

FmMainActivity.java-->isWiredHeadsetOn()

    private boolean isWiredHeadsetOn() {
        //bug939562 return true ,Only wired headset is plugged in
        AudioDeviceInfo[] audioDeviceInfo = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
        for (AudioDeviceInfo info : audioDeviceInfo) {
            //wired headset(with mic or not) is pluged in
            if ((info.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET)||(info.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES)){
                Log.d(TAG,"Wired headset is exist");
                return true;
            }
        }
        return false;
        //return true ,type-c or wired headset is plugged in
        //mAudioManager.isWiredHeadsetOn();
    }

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

2.插入耳机

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

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

    @Override
    public void onStart() {
        super.onStart();
        ......
        if (null == startForegroundService(new Intent(FmMainActivity.this, FmService.class))) {
            Log.e(TAG, "onStart, cannot start FM service");
            return;
        }
        mIsServiceStarted = true;
        mIsServiceBinded = bindService(new Intent(FmMainActivity.this, FmService.class),
                mServiceConnection, Context.BIND_AUTO_CREATE);
        ......
        
    }

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

    private final ServiceConnection mServiceConnection = new ServiceConnection() {

        /**
         * called by system when bind service
         * 
         * @param className component name
         * @param service service binder
         */
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = ((FmService.ServiceBinder) service).getService();
            if (null == mService) {
                Log.e(TAG, "onServiceConnected, mService is null");
                finish();
                return;
            }
            Log.d(TAG, "onServiceConnected, mService is not null");
            refreshRDSVisiable();
            mService.registerFmRadioListener(mFmRadioListener);
            mService.setFmActivityForground(mIsActivityForeground);
            //  bug568587, new feature FM new UI
            mService.setTimerListenr(new MyTimeCountListener());
 
            if (!mService.isServiceInited()) {
                mService.initService();
                powerUpFm();
            } else {
                if (mService.isDeviceOpen()) {
                    updateMenuStatus();
                } else {
                    // Normal case will not come here
                    // Need to exit FM for this case
                    exitService();
                    finish();
                }
            }
        }
        /**
         * When unbind service will call this method
         * 
         * @param className The component name
         */
        @Override
        public void onServiceDisconnected(ComponentName className) {
        }
    };

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

    private FmListener mFmRadioListener = new FmListener() {
        @Override
        public void onCallBack(Bundle bundle) {
            int flag = bundle.getInt(FmListener.CALLBACK_FLAG);
            if (flag == FmListener.MSGID_FM_EXIT) {
                mHandler.removeCallbacksAndMessages(null);
            }

            // remove tag message first, avoid too many same messages in queue.
            Message msg = mHandler.obtainMessage(flag);
            msg.setData(bundle);
            mHandler.removeMessages(flag);
            mHandler.sendMessage(msg);
        }
    };

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

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

    public void registerFmRadioListener(FmListener callback) {
        synchronized (mRecords) {
            // register callback in AudioProfileService, if the callback is
            // exist, just replace the event.
            Record record = null;
            int hashCode = callback.hashCode();
            final int n = mRecords.size();
            for (int i = 0; i < n; i++) {
                record = mRecords.get(i);
                if (hashCode == record.mHashCode) {
                    return;
                }
            }
            record = new Record();
            record.mHashCode = hashCode;
            record.mCallback = callback;
            mRecords.add(record);
        }
    }

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

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

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

监听到后,调用switchAntennaAsync()

    public void switchAntennaAsync(int antenna) {
        final int bundleSize = 1;
        mFmServiceHandler.removeMessages(FmListener.MSGID_SWITCH_ANTENNA);

        Bundle bundle = new Bundle(bundleSize);
        bundle.putInt(FmListener.SWITCH_ANTENNA_VALUE, antenna);
        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SWITCH_ANTENNA);
        msg.setData(bundle);
        mFmServiceHandler.sendMessage(msg);
    }
                case FmListener.MSGID_SWITCH_ANTENNA:
                    bundle = msg.getData();
                    int value = bundle.getInt(FmListener.SWITCH_ANTENNA_VALUE);

                    // if ear phone insert, need dismiss plugin earphone
                    // dialog
                    // if earphone plug out and it is not play recorder
                    // state, show plug dialog.

                    bundle.putInt(FmListener.CALLBACK_FLAG,
                            FmListener.MSGID_SWITCH_ANTENNA);
                    bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, (0 == value));
                    notifyActivityStateChanged(bundle);
                    break;
    private void notifyActivityStateChanged(Bundle bundle) {
        if (!mRecords.isEmpty()) {
            synchronized (mRecords) {
                Iterator<Record> iterator = mRecords.iterator();
                while (iterator.hasNext()) {
                    Record record = (Record) iterator.next();

                    FmListener listener = record.mCallback;

                    if (listener == null) {
                        iterator.remove();
                        return;
                    }

                    listener.onCallBack(bundle);
                }
            }
        }
    }

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

    private FmListener mFmRadioListener = new FmListener() {
        @Override
        public void onCallBack(Bundle bundle) {
            int flag = bundle.getInt(FmListener.CALLBACK_FLAG);
            if (flag == FmListener.MSGID_FM_EXIT) {
                mHandler.removeCallbacksAndMessages(null);
            }

            // remove tag message first, avoid too many same messages in queue.
            Message msg = mHandler.obtainMessage(flag);
            msg.setData(bundle);
            mHandler.removeMessages(flag);
            mHandler.sendMessage(msg);
        }
    };

从bundle里传过来是MSGID_SWITCH_ANTENNA

               case FmListener.MSGID_SWITCH_ANTENNA:
                    ......
                        if (hasAntenna) {
                            cancelNoHeadsetAnimation();
                            if (mIsActivityForeground) {
                                playMainAnimation();
                            } else {
                                changeToMainLayout();
                            }
                    ......

2.搜台功能

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
           case R.id.fm_station_list_refresh:
                if ((null != mService) && (!mService.isSeeking())) {
                    onOptionsItemSelectedScan(false);
                }
                break;

        }
        return super.onOptionsItemSelected(item);
    }

FmFavoriteActivity.java-->onOptionsItemSelectedScan()

    private void onOptionsItemSelectedScan(boolean fromCurrent) {
        if (null != mService) {
            showTipView(true);
            if (fromCurrent) {
                mService.startScanAsyncFromCurrent(mCurrentStationItem.stationFreq);
            } else {
                mService.startScanAsync();
            }
        }
    }

FmService.java-->startScanAsync()

    /**
     * Scan stations
     */
    public void startScanAsync() {
        mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED);
        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_SCAN_FINISHED);
    }
                // start scan
                case FmListener.MSGID_SCAN_FINISHED:
                     ......  
                        stations = startScan(start_freq);
                        /**
                         * @}
                         */
                    }
    private int[] startScan(int start_freq) {
        ......
            stations = mFmManager.autoScan(start_freq);
        ......
        return stations;
    }

FmManagerSelect.java -->autoScan()

    public int[] autoScan(int start_freq) {
        if (mIsUseBrcmFmChip) {
            return mFmManagerForBrcm.autoScanStation(start_freq);
        } else {
            short[] stationsInShort = null;
            int[] stations = null;
            stationsInShort = FmNative.autoScan(start_freq);
            if (null != stationsInShort) {
                int size = stationsInShort.length;
                stations = new int[size];
                for (int i = 0; i < size; i++) {
                    stations[i] = stationsInShort[i];
                }
            }
            return stations;
        }
    }

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

static native short[] autoScan(int start_freq);

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

    private int[] updateStations(int[] stations) {
        Log.d(TAG, "updateStations.firstValidstation:" + Arrays.toString(stations));
        int firstValidstation = mCurrentStationItem.stationFreq;
        FmStation.cleanSearchedStations(mContext);
        int stationNum = batchInsertStationToDb(stations, null);

        int searchedNum = (stations == null ? 0 : stations.length);
        Log.d(TAG, "updateStations.firstValidstation:" + firstValidstation +
                ",searchedNum:" + searchedNum);
        return (new int[]{
                firstValidstation, searchedNum
        });
    }


    private int batchInsertStationToDb(int[] stations, List<FmStationItem> favoriteList) {
        if (null == stations) return 0;
        ArrayList<ContentProviderOperation> ops = new ArrayList<>();
        ContentResolver resolver = mContext.getContentResolver();
        for (int i = 0; i < stations.length; i++) {
            if (!FmUtils.isValidStation(stations[i]) || FmStation.isFavoriteStation(mContext, stations[i])) {
                Log.d(TAG, "station is favorite : " + stations[i]);
                continue;
            }
            ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(Station.CONTENT_URI);
            op.withYieldAllowed(false);
            Log.d(TAG, "station : " + stations[i]);
            ContentValues values = new ContentValues();
            values.clear();
            values.put(Station.FREQUENCY, stations[i]);
            values.put(Station.IS_SHOW, Station.IS_SCAN);
            op.withValues(values);
            ops.add(op.build());
        }
        Log.d(TAG, "ops size : " + ops.size());
        int stationNum = ops.size();
        if (stationNum > 0) {
            try {
                ContentProviderResult[] result = resolver.applyBatch(FmStation.AUTHORITY, ops);
                ops.clear();
                Log.d(TAG, "batch opreate db result count : " + result.length);
            } catch (Exception e) {
                Log.d(TAG, "Batch operate exception");
                e.printStackTrace();
            }
        } else {
            mContext.getContentResolver().notifyChange(FmStation.Station.CONTENT_URI, null);
        }
        return stationNum;
    }

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

    private void refreshFmFavorityList(Cursor cursor) {
        if (cursor == null) {
            return;
        }
        mFavoriteList.clear();
        mOtherList.clear();
        mAllStationSet.clear();
        for (int i = 0; i < cursor.getCount(); i++) {
            FmStationItem item = new FmStationItem();
            if (cursor.moveToFirst()) {
                cursor.moveToPosition(i);
                item.stationFreq = cursor.getInt(cursor
                        .getColumnIndex(FmStation.Station.FREQUENCY));
                item.stationName = cursor.getString(cursor
                        .getColumnIndex(FmStation.Station.STATION_NAME));
                item.rt = cursor.getString(cursor
                        .getColumnIndex(FmStation.Station.RADIO_TEXT));
                item.isFavorite = cursor.getInt(cursor
                        .getColumnIndex(FmStation.Station.IS_FAVORITE));
                item.isShow = cursor.getInt(cursor
                        .getColumnIndex(FmStation.Station.IS_SHOW));
                item.ps = cursor.getString(cursor
                        .getColumnIndex(FmStation.Station.PROGRAM_SERVICE));
                if (item.isFavorite == 0) {
                    mOtherList.add(item);
                } else {
                    mFavoriteList.add(item);
                }
                mAllStationSet.add(item.stationFreq);
            }
        }
        checkSelectedStationSet();
        mMyAdapter.notifyDataSetChanged();
        Log.d(TAG, "refreshFmFavorityList mAllStationSet.size:" + mAllStationSet.size());
    }

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

3.收藏

            viewHolder.mImgLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    /*  bug568587, new feature FM new UI @{ */
                    if (0 == isFavorite) {
                        showToast(getString(R.string.toast_channel_added));
                        addFavorite(stationFreq);
                    } else {
                        showToast(getString(R.string.toast_channel_deleted));
                        deleteFromFavorite(stationFreq);
                    }
                    /* @} */
                }
            });
    /**
     * Add searched station as favorite station
     */
    public void addFavorite(int stationFreq) {
        // TODO it's on UI thread, change to sub thread
        // update the station name and station type in database
        // according the frequency
        operateFmFavoriteStation(stationFreq, ACTION_ADD_FAVORITE_TYPE);
    }
    private void operateFmFavoriteStation(int stationFreq, int type) {
        MyAsyncTask operateTask = new MyAsyncTask();
        operateTask.execute(ACTION_OPERATE_TYPE, stationFreq, type);
    }
    class MyAsyncTask extends AsyncTask<Integer, Void, Cursor> {
        @Override
        protected Cursor doInBackground(Integer... params) {
            Log.d(TAG, "operate database type:" + params[0]);
            if (params[0] == ACTION_QUERY_TYPE) {
                Cursor cursor = getData();
                return cursor;
            } else {
                Log.d(TAG, "stationFreq=" + params[1]);
                operateData(params[1], params[2]);
                return null;
            }
        }

        @Override
        protected void onPostExecute(Cursor cursor) {
            refreshFmFavorityList(cursor);
            if (cursor != null) {
                cursor.close();
            }
        }
    }
    private void operateData(int stationFreq, int type) {
        switch (type) {
            case ACTION_ADD_FAVORITE_TYPE:
                FmStation.addToFavorite(mContext, stationFreq);
                break;
            case ACTION_REMOVE_FROM_FAVORITE_TYPE:
                FmStation.removeFromFavorite(mContext, stationFreq);
                break;

    }
    /**
     * update db to mark it is a favorite frequency
     * 
     * @param context The context
     * @param frequency The target frequency
     */
    public static void addToFavorite(Context context, int frequency) {
        ContentValues values = new ContentValues(1);
        values.put(Station.IS_FAVORITE, true);
        values.put(Station.IS_SHOW,Station.IS_SCAN);
        context.getContentResolver().update(
                Station.CONTENT_URI,
                values,
                Station.FREQUENCY + "=?",
                new String[] {
                    String.valueOf(frequency)
                });
    }

4.播放流程

FmMainActivity.java

case R.id.play_button:
                    /**
                     *  bug500046, for monkey test.
                     * 
                     * @{
                     */
                    if (null == mService) {
                        Log.e(TAG, "onClick case playbutton, mService is null");
                        return;
                    }
                    /**
                     * @}
                     */
                    if (mService.getPowerStatus() == FmService.POWER_UP) {
                        if(mService.isMuted()){
                            mService.setMuteAsync(false,false);
                        }else{
                            powerDownFm();
                        }
                    } else {
                        powerUpFm();
                    }
                    break;
                default:
                    Log.d(TAG, "mButtonClickListener.onClick, invalid view id");
                    break;

FmMainActivity.java -- >powerUpFm()

    /**
     * Power up FM
     */
    private void powerUpFm() {
        if(FmUtils.isAirplane(this)){
            changeToAirPlaneLayout();
        }else{
            refreshImageButton(false);
            refreshActionMenuItem(false);
            refreshPopupMenuItem(false);
            refreshPlayButton(false);
            mService.powerUpAsync(FmUtils.computeFrequency(mCurrentStationItem.stationFreq));
        }
    }

发送MSGID_POWERUP_FINISHED

    public void powerUpAsync(float frequency) {
        final int bundleSize = 1;
        mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED);
        mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED);
        Bundle bundle = new Bundle(bundleSize);
        bundle.putFloat(FM_FREQUENCY, frequency);
        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_POWERUP_FINISHED);
        msg.setData(bundle);
        mFmServiceHandler.sendMessage(msg);
    }

FmService.java -->powerUpAsync()


                // power up
                case FmListener.MSGID_POWERUP_FINISHED:
                    bundle = msg.getData();
                    handlePowerUp(bundle);
                    break;

FmService.java -->handlePowerUp()

    private void handlePowerUp(Bundle bundle) {
        boolean isPowerUp = false;
        boolean isSwitch = true;
        float curFrequency = bundle.getFloat(FM_FREQUENCY);

        if (FmUtils.isAirplane(this)) {
            Log.d(TAG, "handlePowerUp, airplane is on");
            return;
        }
        boolean isAntennaAvailable = isAntennaAvailable();
        if (!FmUtils.supportShortAntenna) {
            if (!isAntennaAvailable) {
                Log.d(TAG, "handlePowerUp, earphone is not ready");
                bundle = new Bundle(2);
                bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_SWITCH_ANTENNA);
                bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, false);
                notifyActivityStateChanged(bundle);
                return;
            }
        } else {
            Log.d(TAG, "handlePowerUp: wether earphone is plugged in -->" + isAntennaAvailable);
            switchAntenna(isAntennaAvailable ? 0 : 1);
        }
        if (powerUp(curFrequency)) {
            if (FmUtils.isFirstTimePlayFm(mContext)) {
                isPowerUp = firstPlaying(curFrequency);
                FmUtils.setIsFirstTimePlayFm(mContext);
            } else {
                /**
                 * Bug546461 Tune radio again after power down then power up for brcm. Orig:
                 * isPowerUp = playFrequency(curFrequency);
                 * @{
                 */
                if (mFmManager.tuneRadioAgain(curFrequency)) {
                    isPowerUp = playFrequency(curFrequency);
                }
                /**
                 * @}
                 */
            }
            mPausedByTransientLossOfFocus = false;
        }
        bundle = new Bundle(1);
        bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERUP_FINISHED);
        notifyActivityStateChanged(bundle);
    }

FmService.java -->playFrequency()

    private boolean playFrequency(float frequency) {
        Log.d(TAG, "playFrequency");
        updatePlayingNotification();
        // Start the RDS thread if RDS is supported.
        // RDS function should be open status.
        if (isRdsSupported() && FmUtils.isRDSOpen(mContext)) {
            startRdsThread();
        }

        //bug492835, FM audio route change. if (!mWakeLock.isHeld()) { mWakeLock.acquire(); }
        if (mIsSpeakerUsed) {
            setForceUse(mIsSpeakerUsed);
        }
        enableFmAudio(true);
        setRds(true);
        setMute(false);

        return (mPowerStatus == POWER_UP);
    }

FmService.java -->enableFmAudio()

    private void enableFmAudio(boolean enable) {
        Log.d(TAG, "enableFmAudio:" + enable);
        if (enable) {
            if ((mPowerStatus != POWER_UP) || !mIsAudioFocusHeld) {
                Log.d(TAG, "enableFmAudio, current not available return.mIsAudioFocusHeld:"
                        + mIsAudioFocusHeld);
                return;
            }

            ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
            mAudioManager.listAudioPatches(patches);
            if (mAudioPatch == null) {
                Log.d(TAG, "mAudioPatch == null");
                if (isPatchMixerToEarphone(patches)) {
                    int status;
                    stopRender();
                    status = createAudioPatch();
                    if (status != AudioManager.SUCCESS) {
                        Log.d(TAG, "enableFmAudio: fallback as createAudioPatch failed");
                        startRender();
                    }
                } else {
                    startRender();
                }
            }
        } else {
            releaseAudioPatch();
            stopRender();
        }
    }

FmService.java -->startRender()

    private synchronized void startRender() {
        //bug492835, FM audio route change.
        mFmManager.setAudioPathEnable(AudioPath.FM_AUDIO_PATH_HEADSET, true);
        mMediaSessionManager.setOnMediaKeyListener(mMediaKeyListener, null);
        Log.d(TAG, "startRender,setOnMediaKeyListener");
        mIsRender = true;
        synchronized (mRenderLock) {
            mRenderLock.notify();
        }
    }

FmManagerSelect.java -->setAudioPathEnable()

    public boolean setAudioPathEnable(AudioPath path, boolean enable) {
        if (mIsUseBrcmFmChip) {
            if (enable) {
                mFmManagerForBrcm.setAudioMode(FmAudioModeForBrcm.FM_AUDIO_MODE_BLEND);
                //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
                //        AudioSystem.DEVICE_STATE_AVAILABLE, "", "");
                mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
                          AudioSystem.DEVICE_STATE_AVAILABLE, "", "");
            } else {
                //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
                //        AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
                mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
                          AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
	    }
            return mFmManagerForBrcm.setAudioPath(convertAudioPathForBrcm(path));
        } else {
            if (enable) {
                //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
                mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
		        AudioSystem.DEVICE_STATE_AVAILABLE, "", "");
                return true;
            } else {
                //AudioSystem.setDeviceConnectionState(AudioManager.DEVICE_OUT_FM_HEADSET,
                mAudioManager.setDeviceConnectionStateForFM(AudioManager.DEVICE_OUT_FM_HEADSET,
		        AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "");
                return true;
            }

        }
    }

AudioManager.java-->setDeviceConnectionStateForFM()

    public void setDeviceConnectionStateForFM(int device, int state,
                                                      String device_address, String device_name) {
        final IAudioService service = getService();
        try {
            service.setDeviceConnectionStateForFM(device, state, device_address, device_name,mICallBack);
        } catch (RemoteException e) {
            Log.e(TAG, "Dead object in setDeviceConnectionStateForFM ", e);
        }
    }

二,录音

录音是FmRecordActivity

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

    /**
     * Start recording
     */
    public void startRecordingAsync() {
        mFmServiceHandler.removeMessages(FmListener.MSGID_STARTRECORDING_FINISHED);
        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STARTRECORDING_FINISHED);
    }
                case FmListener.MSGID_STARTRECORDING_FINISHED:
                    startRecording();
                    break;
public void startRecording(Context context) {
        mRecordTime = 0;
        Uri dir = null;
        FileDescriptor fd = null;
        Uri externalStorageUri=null;

        /**
         * Bug529776 Check the main card state Original Android code:
         * 
         * @{ // Check external storage if
         *    (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
         *    Log.e(TAG, "startRecording, no external storage available");
         *    setError(ERROR_SDCARD_NOT_PRESENT); return; } /** @}
         */
        if (FmUtils.isExternalStorage()) {
            externalStorageUri =FmUtils.getCurrentAccessUri(FmUtils.FM_RECORD_STORAGE_PATH_NAME);
        }

        String recordingSdcard = FmUtils.getDefaultStoragePath();
        Log.d(TAG,"externalStorageUri="+externalStorageUri+"   recordingSdcard="+recordingSdcard);

        /**
         *  bug474747, recording path selection.
         * 
         * @{
         */
        if (recordingSdcard == null
                || recordingSdcard.isEmpty()
                || (FmUtils.FM_RECORD_STORAGE_PATH_SDCARD.equals(FmUtils.FM_RECORD_STORAGE_PATH_NAME) && !Environment.MEDIA_MOUNTED
                        .equals(EnvironmentEx.getExternalStoragePathState()))) {
            Log.e(TAG, "startRecording, no sdcard storage available");
            setError(ERROR_SDCARD_NOT_PRESENT);
            return;
        }
        /**
         * @}
         */

        // check whether have sufficient storage space, if not will notify
        // caller error message
        if (!FmUtils.hasEnoughSpace(recordingSdcard)) {
            setError(ERROR_SDCARD_INSUFFICIENT_SPACE);
            Log.e(TAG, "startRecording, SD card does not have sufficient space!!");
            return;
        }

        // get external storage directory
        File sdDir = new File(recordingSdcard);
        File recordingDir = new File(sdDir, FM_RECORD_FOLDER);
        // exist a file named FM Recording, so can't create FM recording folder
        if (recordingDir.exists() && !recordingDir.isDirectory()) {
            Log.e(TAG, "startRecording, a file with name \"" + FM_RECORD_FOLDER + "\" already exists!!");
            setError(ERROR_SDCARD_WRITE_FAILED);
            return;
        } else if (!recordingDir.exists()) { // try to create recording folder
            if (FmUtils.isExternalStorage()) {
                dir = getPathUri(context,recordingDir,externalStorageUri);
            } else {
                boolean mkdirResult = recordingDir.mkdir();
                if (!mkdirResult) { // create recording file failed
                    setError(ERROR_RECORDER_INTERNAL);
                    return;
                }
            }
        } else {
            if (FmUtils.isExternalStorage()) {
                dir = getPathUri(context,recordingDir,externalStorageUri);
            }
        }
        // create recording temporary file
        long curTime = System.currentTimeMillis();
        Date date = new Date(curTime);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMddyyyy_HHmmss",
                Locale.ENGLISH);
        String time = simpleDateFormat.format(date);
        StringBuilder stringBuilder = new StringBuilder();
        /**
         *  bug474741, recording format selection.
         * 
         * @{
         */
        if (GLOBAL_RECORD_FORMAT_FLAG == 1) {
            stringBuilder.append(".").append(time).append(RECORDING_FILE_EXTENSION_3GPP).append(".tmp");
        } else if(GLOBAL_RECORD_FORMAT_FLAG == 2) {
            stringBuilder.append(".").append(time).append(RECORDING_FILE_EXTENSION_MP3).append(".tmp");
        } else {
	        stringBuilder.append(".").append(time).append(RECORDING_FILE_EXTENSION_AMR).append(".tmp");
	    }
        /**
         * @}
         */
        String name = stringBuilder.toString();
        mRecordFile = new File(recordingDir, name);
        if (FmUtils.isExternalStorage()) {
            try {
                Uri fileDoc = DocumentsContract.createDocument(context.getContentResolver(),dir
                    ,DocumentsContract.Document.COLUMN_MIME_TYPE,name);
                FmUtils.saveTmpFileUri(fileDoc.toString());
                mPfd = context.getContentResolver().openFileDescriptor(fileDoc,"w");
                fd = mPfd.getFileDescriptor();
                if(fd == null) {
                    Log.e(TAG,"fd is NULL");
                    throw new IllegalArgumentException("Memory related error");
                }
            } catch(FileNotFoundException e) {
                Log.d(TAG,""+e);
            }
        } else {
            try {
                if (mRecordFile.createNewFile()) {
                    Log.d(TAG, "startRecording, createNewFile success with path "
                            + mRecordFile.getPath());
                }
            } catch (IOException e) {
                Log.e(TAG, "startRecording, IOException while createTempFile: " + e);
                e.printStackTrace();
                setError(ERROR_SDCARD_WRITE_FAILED);
                return;
            }
        }
        // set record parameter and start recording
        try {
            mRecorder = new MediaRecorder();
            mRecorder.setOnErrorListener(this);
            mRecorder.setOnInfoListener(this);
            // mRecorder.setAudioSource(MediaRecorder.AudioSource.RADIO_TUNER);
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            /**
             *  bug474741, recording format selection. Original Android code:
             * mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
             * mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); final int samplingRate =
             * 44100; mRecorder.setAudioSamplingRate(samplingRate); final int bitRate = 128000;
             * mRecorder.setAudioEncodingBitRate(bitRate); final int audiochannels = 2;
             * mRecorder.setAudioChannels(audiochannels);
             * 
             * @{
             */
            if (1 == GLOBAL_RECORD_FORMAT_FLAG) {
                Log.d(TAG, "global_record_format: 3gpp");
                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
                mRecorder.setAudioSamplingRate(44100);
                mRecorder.setAudioEncodingBitRate(128000);
            } else if(2 == GLOBAL_RECORD_FORMAT_FLAG) {
                Log.d(TAG, "global_record_format: mp3");
                mRecorder.setOutputFormat(11);
                mRecorder.setAudioEncoder(7);
                mRecorder.setAudioSamplingRate(44100);
                mRecorder.setAudioEncodingBitRate(320000);
                mRecorder.setAudioChannels(2);
            } else {
                Log.d(TAG, "global_record_format: amr_nb");
                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
                mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                mRecorder.setAudioSamplingRate(8000);
                mRecorder.setAudioEncodingBitRate(5900);
            }
            /**
             * @}
             */
            if (FmUtils.isExternalStorage()) {
                mRecorder.setOutputFile(fd);
            } else {
                mRecorder.setOutputFile(mRecordFile.getAbsolutePath());
            }
            mRecorder.prepare();
            mRecordStartTime = SystemClock.elapsedRealtime();
            mRecorder.start();
            mIsRecordingFileSaved = false;
            mFileListener= new FileListener(recordingDir.getPath());
            mFileListener.startWatching();

        } catch (IllegalStateException e) {
            Log.e(TAG, "startRecording, IllegalStateException while starting recording!", e);
            setError(ERROR_RECORDER_INTERNAL);
            return;
        } catch (IOException e) {
            Log.e(TAG, "startRecording, IOException while starting recording!", e);
            setError(ERROR_RECORDER_INTERNAL);
            return;
        }
        setState(STATE_RECORDING);
    }

    /**
     *Listening whether recording file is delete or not.
     *@}
     */
    private class FileListener extends FileObserver {
        public FileListener(String path) {
            super(path, FileObserver.MOVED_FROM | FileObserver.DELETE);
            Log.d(TAG, "FileListener path="+path);
        }
        @Override
        public void onEvent(int event, String path) {
            Log.d(TAG, "onEvent: event = "+event+"; path = "+path);
            switch (event) {
                case FileObserver.MOVED_FROM:
                case FileObserver.DELETE:
                   if (path == null) {
                       return ;
                   }
                   if ( getTmpFileName().equals(path)) {
                       Log.d(TAG, "recording tmp file is deleted");
                       discardRecording();
                   }
                    break;
                default:
                    break;
            }
        }
    }

录音结束

    private void stopRecorder() {

        ......
                    mRecorder.stop();
        ......

    }

猜你喜欢

转载自blog.csdn.net/liu362732346/article/details/102665246