1a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang/*
2a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Copyright (C) 2014 The Android Open Source Project
3a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *
4a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Licensed under the Apache License, Version 2.0 (the "License");
5a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * you may not use this file except in compliance with the License.
6a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * You may obtain a copy of the License at
7a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *
8a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *      http://www.apache.org/licenses/LICENSE-2.0
9a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *
10a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Unless required by applicable law or agreed to in writing, software
11a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * distributed under the License is distributed on an "AS IS" BASIS,
12a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * See the License for the specific language governing permissions and
14a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * limitations under the License.
15a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */
16a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
17a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangpackage com.android.fmradio;
18a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
19a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.ActivityManager;
20a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.Notification;
21a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.Notification.BigTextStyle;
22a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.PendingIntent;
23a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.app.Service;
242816b93f7207ca62e83dda35716e8ce267e84197Benson Huangimport android.bluetooth.BluetoothAdapter;
252816b93f7207ca62e83dda35716e8ce267e84197Benson Huangimport android.bluetooth.BluetoothProfile;
26a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.BroadcastReceiver;
27a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.ContentResolver;
28a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.ContentValues;
29a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.Context;
30a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.Intent;
31a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.IntentFilter;
32a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.res.Configuration;
33a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.database.Cursor;
34a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.graphics.Bitmap;
35a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioDevicePort;
36a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioDevicePortConfig;
37a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioFormat;
38a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioManager;
39a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioManager.OnAudioFocusChangeListener;
40a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioManager.OnAudioPortUpdateListener;
41a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioMixPort;
42a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioPatch;
43a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioPort;
44a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioPortConfig;
45a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioRecord;
46a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioSystem;
47a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.AudioTrack;
48a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.media.MediaRecorder;
49a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.net.Uri;
50a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Binder;
51a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Bundle;
52a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Handler;
53a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.HandlerThread;
54a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.IBinder;
55a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Looper;
56a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Message;
57a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.PowerManager;
58a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.PowerManager.WakeLock;
59a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.text.TextUtils;
60a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.util.Log;
61a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
62a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport com.android.fmradio.FmStation.Station;
63a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
64a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.ArrayList;
65a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.Arrays;
66a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.HashMap;
67a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport java.util.Iterator;
68a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
69a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang/**
70a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Background service to control FM or do background tasks.
71a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */
72a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangpublic class FmService extends Service implements FmRecorder.OnRecorderStateChangedListener {
73a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Logging
74a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final String TAG = "FmService";
75a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
76a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Broadcast messages from other sounder APP to FM service
77a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final String SOUND_POWER_DOWN_MSG = "com.android.music.musicservicecommand";
7844ce72c72613263d4f2d026263f3ac53cf0cde8aBenson Huang    private static final String FM_SEEK_PREVIOUS = "fmradio.seek.previous";
7944ce72c72613263d4f2d026263f3ac53cf0cde8aBenson Huang    private static final String FM_SEEK_NEXT = "fmradio.seek.next";
8044ce72c72613263d4f2d026263f3ac53cf0cde8aBenson Huang    private static final String FM_TURN_OFF = "fmradio.turnoff";
81a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final String CMDPAUSE = "pause";
82a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
83a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // HandlerThread Keys
84a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final String FM_FREQUENCY = "frequency";
85a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final String OPTION = "option";
86a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final String RECODING_FILE_NAME = "name";
87a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
88a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // RDS events
89a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // PS
90a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int RDS_EVENT_PROGRAMNAME = 0x0008;
91a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // RT
92a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int RDS_EVENT_LAST_RADIOTEXT = 0x0040;
93a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // AF
94a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int RDS_EVENT_AF = 0x0080;
95a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
96a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Headset
97a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int HEADSET_PLUG_IN = 1;
98a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
99a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Notification id
100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int NOTIFICATION_ID = 1;
101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1025aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang    // ignore audio data
10387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    private static final int AUDIO_FRAMES_TO_IGNORE_COUNT = 3;
1045aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang
105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Set audio policy for FM
106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // should check AUDIO_POLICY_FORCE_FOR_MEDIA in audio_policy.h
107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int FOR_PROPRIETARY = 1;
108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Forced Use value
109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mForcedUseForMedia;
110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // FM recorder
112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    FmRecorder mFmRecorder = null;
113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private BroadcastReceiver mSdcardListener = null;
114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mRecordState = FmRecorder.STATE_INVALID;
115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mRecorderErrorType = -1;
116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // If eject record sdcard, should set Value false to not record.
117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Key is sdcard path(like "/storage/sdcard0"), V is to enable record or
118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // not.
119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private HashMap<String, Boolean> mSdcardStateMap = new HashMap<String, Boolean>();
120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // The show name in save dialog but saved in service
121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // If modify the save title it will be not null, otherwise it will be null
122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private String mModifiedRecordingName = null;
123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // record the listener list, will notify all listener in list
124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private ArrayList<Record> mRecords = new ArrayList<Record>();
125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // record FM whether in recording mode
126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsInRecordingMode = false;
127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // record sd card path when start recording
128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static String sRecordingSdcard = FmUtils.getDefaultStoragePath();
129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // RDS
131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // PS String
132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private String mPsString = "";
133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // RT String
134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private String mRtTextString = "";
135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Notification target class name
136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private String mTargetClassName = FmMainActivity.class.getName();
137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // RDS thread use to receive the information send by station
138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Thread mRdsThread = null;
139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // record whether RDS thread exit
140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsRdsThreadExit = false;
141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // State variables
143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether FM is in native scan state
144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsNativeScanning = false;
145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether FM is in scan thread
146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsScanning = false;
147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether FM is in seeking state
148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsNativeSeeking = false;
149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether FM is in native seek
150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsSeeking = false;
151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether searching progress is canceled
152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsStopScanCalled = false;
153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether is speaker used
154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsSpeakerUsed = false;
155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether device is open
156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsDeviceOpen = false;
157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record Power Status
158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mPowerStatus = POWER_DOWN;
159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public static int POWER_UP = 0;
161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public static int DURING_POWER_UP = 1;
162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public static int POWER_DOWN = 2;
163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Record whether service is init
164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsServiceInited = false;
165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Fm power down by loss audio focus,should make power down menu item can
166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // click
167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsPowerDown = false;
168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // distance is over 100 miles(160934.4m)
169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsDistanceExceed = false;
170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // FmMainActivity foreground
171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsFmMainForeground = true;
172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // FmFavoriteActivity foreground
1733dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang    private boolean mIsFmFavoriteForeground = false;
1744b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang    // FmRecordActivity foreground
1753dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang    private boolean mIsFmRecordForeground = false;
176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Instance variables
177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Context mContext = null;
178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private AudioManager mAudioManager = null;
179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private ActivityManager mActivityManager = null;
180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    //private MediaPlayer mFmPlayer = null;
181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private WakeLock mWakeLock = null;
182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Audio focus is held or not
183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsAudioFocusHeld = false;
184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Focus transient lost
185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mPausedByTransientLossOfFocus = false;
186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mCurrentStation = FmUtils.DEFAULT_STATION;
187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Headset plug state (0:long antenna plug in, 1:long antenna plug out)
188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mValueHeadSetPlug = 1;
189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // For bind service
190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final IBinder mBinder = new ServiceBinder();
191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Broadcast to receive the external event
192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private FmServiceBroadcastReceiver mBroadcastReceiver = null;
193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Async handler
194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private FmRadioServiceHandler mFmServiceHandler;
195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Lock for lose audio focus and receive SOUND_POWER_DOWN_MSG
196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // at the same time
197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // while recording call stop recording not finished(status is still
198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // RECORDING), but
199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // SOUND_POWER_DOWN_MSG will exitFm(), if it is RECORDING will discard the
200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // record.
201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // 1. lose audio focus -> stop recording(lock) -> set to IDLE and show save
202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // dialog
203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // 2. exitFm() -> check the record status, discard it if it is recording
204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // status(lock)
205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Add this lock the exitFm() while stopRecording()
206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Object mStopRecordingLock = new Object();
207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // The listener for exit, should finish favorite when exit FM
208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static OnExitListener sExitListener = null;
209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // The latest status for mute/unmute
210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsMuted = false;
211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Audio Patch
213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private AudioPatch mAudioPatch = null;
214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Object mRenderLock = new Object();
215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Notification.Builder mNotificationBuilder = null;
217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private BigTextStyle mNotificationStyle = null;
218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public IBinder onBind(Intent intent) {
221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mBinder;
222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * class use to return service instance
226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public class ServiceBinder extends Binder {
228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * get FM service instance
230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         *
231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * @return service instance
232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        FmService getService() {
234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return FmService.this;
235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Broadcast monitor external event, Other app want FM stop, Phone shut
240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * down, screen state, headset state
241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private class FmServiceBroadcastReceiver extends BroadcastReceiver {
243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void onReceive(Context context, Intent intent) {
246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            String action = intent.getAction();
247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            String command = intent.getStringExtra("command");
248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.d(TAG, "onReceive, action = " + action + " / command = " + command);
249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // other app want FM stop, stop FM
250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if ((SOUND_POWER_DOWN_MSG.equals(action) && CMDPAUSE.equals(command))) {
251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // need remove all messages, make power down will be execute
252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFmServiceHandler.removeCallbacksAndMessages(null);
253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                exitFm();
254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                stopSelf();
255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // phone shut down, so exit FM
256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (Intent.ACTION_SHUTDOWN.equals(action)) {
257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                /**
258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * here exitFm, system will send broadcast, system will shut
259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * down, so fm does not need call back to activity
260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 */
261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFmServiceHandler.removeCallbacksAndMessages(null);
262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                exitFm();
263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // screen on, if FM play, open rds
264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setRdsAsync(true);
266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // screen off, if FM play, close rds
267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setRdsAsync(false);
269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // switch antenna when headset plug in or plug out
270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // switch antenna should not impact audio focus status
272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mValueHeadSetPlug = (intent.getIntExtra("state", -1) == HEADSET_PLUG_IN) ? 0 : 1;
273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                switchAntennaAsync(mValueHeadSetPlug);
274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // Avoid Service is killed,and receive headset plug in
276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // broadcast again
277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (!mIsServiceInited) {
278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Log.d(TAG, "onReceive, mIsServiceInited is false");
279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    return;
280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                /*
282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * If ear phone insert and activity is
283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * foreground. power up FM automatic
284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 */
285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if ((0 == mValueHeadSetPlug) && isActivityForeground()) {
286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    powerUpAsync(FmUtils.computeFrequency(mCurrentStation));
287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else if (1 == mValueHeadSetPlug) {
288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED);
289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED);
290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED);
291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmServiceHandler.removeMessages(
292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            FmListener.MSGID_POWERDOWN_FINISHED);
293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmServiceHandler.removeMessages(
294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            FmListener.MSGID_POWERUP_FINISHED);
295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    focusChanged(AudioManager.AUDIOFOCUS_LOSS);
296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // Need check to switch to earphone mode for audio will
298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // change to AudioSystem.FORCE_NONE
299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    setForceUse(false);
300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // Notify UI change to earphone mode, false means not speaker mode
302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Bundle bundle = new Bundle(2);
303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putInt(FmListener.CALLBACK_FLAG,
304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            FmListener.LISTEN_SPEAKER_MODE_CHANGED);
305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putBoolean(FmListener.KEY_IS_SPEAKER_MODE, false);
306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    notifyActivityStateChanged(bundle);
307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Handle sdcard mount/unmount event. 1. Update the sdcard state map 2. If
314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * the recording sdcard is unmounted, need to stop and notify
315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private class SdcardListener extends BroadcastReceiver {
317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void onReceive(Context context, Intent intent) {
319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // If eject record sdcard, should set this false to not
320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // record.
321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            updateSdcardStateMap(intent);
322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mFmRecorder == null) {
324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Log.w(TAG, "SdcardListener.onReceive, mFmRecorder is null");
325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return;
326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            String action = intent.getAction();
329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (Intent.ACTION_MEDIA_EJECT.equals(action) ||
330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) {
331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // If not unmount recording sd card, do nothing;
332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (isRecordingCardUnmount(intent)) {
333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (mFmRecorder.getState() == FmRecorder.STATE_RECORDING) {
334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT);
335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFmRecorder.discardRecording();
336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    } else {
337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        Bundle bundle = new Bundle(2);
338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        bundle.putInt(FmListener.CALLBACK_FLAG,
339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmListener.LISTEN_RECORDSTATE_CHANGED);
340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        bundle.putInt(FmListener.KEY_RECORDING_STATE,
341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmRecorder.STATE_IDLE);
342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        notifyActivityStateChanged(bundle);
343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return;
346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * whether antenna available
352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true, antenna available; false, antenna not available
354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isAntennaAvailable() {
356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mAudioManager.isWiredHeadsetOn();
357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void setForceUse(boolean isSpeaker) {
360a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mForcedUseForMedia = isSpeaker ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
361a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        AudioSystem.setForceUse(FOR_PROPRIETARY, mForcedUseForMedia);
362a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsSpeakerUsed = isSpeaker;
363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set FM audio from speaker or not
367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param isSpeaker true if set FM audio from speaker
369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setSpeakerPhoneOn(boolean isSpeaker) {
3714b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang        Log.d(TAG, "setSpeakerPhoneOn " + isSpeaker);
372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setForceUse(isSpeaker);
373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
3752816b93f7207ca62e83dda35716e8ce267e84197Benson Huang    /**
3762816b93f7207ca62e83dda35716e8ce267e84197Benson Huang     * Check if BT headset is connected
3772816b93f7207ca62e83dda35716e8ce267e84197Benson Huang     * @return true if current is playing with BT headset
3782816b93f7207ca62e83dda35716e8ce267e84197Benson Huang     */
3792816b93f7207ca62e83dda35716e8ce267e84197Benson Huang    public boolean isBluetoothHeadsetInUse() {
3802816b93f7207ca62e83dda35716e8ce267e84197Benson Huang        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
3812816b93f7207ca62e83dda35716e8ce267e84197Benson Huang        int a2dpState = btAdapter.getProfileConnectionState(BluetoothProfile.HEADSET);
3822816b93f7207ca62e83dda35716e8ce267e84197Benson Huang        return (BluetoothProfile.STATE_CONNECTED == a2dpState
3832816b93f7207ca62e83dda35716e8ce267e84197Benson Huang                || BluetoothProfile.STATE_CONNECTING == a2dpState);
3842816b93f7207ca62e83dda35716e8ce267e84197Benson Huang    }
3852816b93f7207ca62e83dda35716e8ce267e84197Benson Huang
386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private synchronized void startRender() {
3877c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang        Log.d(TAG, "startRender " + AudioSystem.getForceUse(FOR_PROPRIETARY));
38844c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang
38944c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang       // need to create new audio record and audio play back track,
39044c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang       // because input/output device may be changed.
39144c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang       if (mAudioRecord != null) {
39244c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang           mAudioRecord.stop();
39319ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar           mAudioRecord.release();
39419ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar           mAudioRecord = null;
39544c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang       }
39644c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang       if (mAudioTrack != null) {
39744c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang           mAudioTrack.stop();
39819ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar           mAudioTrack.release();
39919ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar           mAudioTrack = null;
40044c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang       }
40144c2adb10bb88f0cecad35787789d0b212a6dda3Benson Huang       initAudioRecordSink();
4027c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang
403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsRender = true;
404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        synchronized (mRenderLock) {
405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mRenderLock.notify();
406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private synchronized void stopRender() {
4104b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang        Log.d(TAG, "stopRender");
411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsRender = false;
412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private synchronized void createRenderThread() {
415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mRenderThread == null) {
416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mRenderThread = new RenderThread();
417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mRenderThread.start();
418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private synchronized void exitRenderThread() {
422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        stopRender();
423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mRenderThread.interrupt();
424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mRenderThread = null;
425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Thread mRenderThread = null;
428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private AudioRecord mAudioRecord = null;
429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private AudioTrack mAudioTrack = null;
430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int SAMPLE_RATE = 44100;
431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int RECORD_BUF_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE,
434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            CHANNEL_CONFIG, AUDIO_FORMAT);
435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsRender = false;
436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    AudioDevicePort mAudioSource = null;
438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    AudioDevicePort mAudioSink = null;
439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean isRendering() {
441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsRender;
442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void startAudioTrack() {
445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioManager.listAudioPatches(patches);
448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioTrack.play();
449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void stopAudioTrack() {
453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioTrack.stop();
455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    class RenderThread extends Thread {
4595aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang        private int mCurrentFrame = 0;
4605aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang        private boolean isAudioFrameNeedIgnore() {
46187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            return mCurrentFrame < AUDIO_FRAMES_TO_IGNORE_COUNT;
4625aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang        }
4635aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang
464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void run() {
466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            try {
467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                byte[] buffer = new byte[RECORD_BUF_SIZE];
468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                while (!Thread.interrupted()) {
4695aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                    if (isRender()) {
47087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                        // Speaker mode or BT a2dp mode will come here and keep reading and writing.
47187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                        // If we want FM sound output from speaker or BT a2dp, we must record data
47287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                        // to AudioRecrd and write data to AudioTrack.
4735aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
4745aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                            mAudioRecord.startRecording();
4755aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        }
47687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang
477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            mAudioTrack.play();
479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        int size = mAudioRecord.read(buffer, 0, RECORD_BUF_SIZE);
4815aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        // check whether need to ignore first 3 frames audio data from AudioRecord
4825aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        // to avoid pop noise.
4835aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        if (isAudioFrameNeedIgnore()) {
4845aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                            mCurrentFrame += 1;
4855aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                            continue ;
4865aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        }
4877c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang                        if (size <= 0) {
4887c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang                            Log.e(TAG, "RenderThread read data from AudioRecord "
4897c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang                                    + "error size: " + size);
4907c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang                            continue;
4917c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang                        }
492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        byte[] tmpBuf = new byte[size];
493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        System.arraycopy(buffer, 0, tmpBuf, 0, size);
49487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                        // Check again to avoid noises, because mIsRender may be changed
49587171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                        // while AudioRecord is reading.
4965aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        if (isRender()) {
4975aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                            mAudioTrack.write(tmpBuf, 0, tmpBuf.length);
498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
4995aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                    } else {
50087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                        // Earphone mode will come here and wait.
5015aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        mCurrentFrame = 0;
50287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang
503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            mAudioTrack.stop();
505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
5075aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
5085aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                            mAudioRecord.stop();
5095aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang                        }
5105aba12dbabd8631187ff3135d8f1a692bd864b0fBenson Huang
511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        synchronized (mRenderLock) {
512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            mRenderLock.wait();
513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } catch (InterruptedException e) {
517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Log.d(TAG, "RenderThread.run, thread is interrupted, need exit thread");
518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } finally {
519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mAudioRecord.stop();
521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mAudioTrack.stop();
524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // A2dp or speaker mode should render
530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean isRender() {
531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return (mIsRender && (mPowerStatus == POWER_UP) && mIsAudioFocusHeld);
532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean isSpeakerPhoneOn() {
535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return (mForcedUseForMedia == AudioSystem.FORCE_SPEAKER);
536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * open FM device, should be call before power up
540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true if FM device open, false FM device not open
542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean openDevice() {
544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mIsDeviceOpen) {
545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsDeviceOpen = FmNative.openDev();
546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsDeviceOpen;
548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * close FM device
552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true if close FM device success, false close FM device failed
554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean closeDevice() {
556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean isDeviceClose = false;
557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsDeviceOpen) {
558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            isDeviceClose = FmNative.closeDev();
559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsDeviceOpen = !isDeviceClose;
560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // quit looper
562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.getLooper().quit();
563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return isDeviceClose;
564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * get FM device opened or not
568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true FM device opened, false FM device closed
570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isDeviceOpen() {
572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsDeviceOpen;
573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * power up FM, and make FM voice output from earphone
577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param frequency
579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void powerUpAsync(float frequency) {
581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED);
583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED);
584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putFloat(FM_FREQUENCY, frequency);
586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_POWERUP_FINISHED);
587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean powerUp(float frequency) {
592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus == POWER_UP) {
593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return true;
594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mWakeLock.isHeld()) {
596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mWakeLock.acquire();
597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!requestAudioFocus()) {
599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // activity used for update powerdown menu
600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPowerStatus = POWER_DOWN;
601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mPowerStatus = DURING_POWER_UP;
605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // if device open fail when chip reset, it need open device again before
607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // power up
608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mIsDeviceOpen) {
609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            openDevice();
610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!FmNative.powerUp(frequency)) {
613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPowerStatus = POWER_DOWN;
614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mPowerStatus = POWER_UP;
617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // need mute after power up
618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setMute(true);
619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return (mPowerStatus == POWER_UP);
621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean playFrequency(float frequency) {
624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mCurrentStation = FmUtils.computeStation(frequency);
625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        FmStation.setCurrentStation(mContext, mCurrentStation);
626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Add notification to the title bar.
627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        updatePlayingNotification();
628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Start the RDS thread if RDS is supported.
630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (isRdsSupported()) {
631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            startRdsThread();
632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mWakeLock.isHeld()) {
635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mWakeLock.acquire();
636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsSpeakerUsed != isSpeakerPhoneOn()) {
638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            setForceUse(mIsSpeakerUsed);
639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mRecordState != FmRecorder.STATE_PLAYBACK) {
641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            enableFmAudio(true);
642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setRds(true);
645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setMute(false);
646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return (mPowerStatus == POWER_UP);
648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * power down FM
652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void powerDownAsync() {
654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // if power down Fm, should remove message first.
655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // not remove all messages, because such as recorder message need
656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // to execute after or before power down
657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED);
658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED);
659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED);
660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED);
661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED);
662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_POWERDOWN_FINISHED);
663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Power down FM
667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true if power down success
669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean powerDown() {
671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus == POWER_DOWN) {
672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return true;
673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setMute(true);
676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setRds(false);
677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        enableFmAudio(false);
678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!FmNative.powerDown(0)) {
680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (isRdsSupported()) {
682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                stopRdsThread();
683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mWakeLock.isHeld()) {
686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mWakeLock.release();
687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Remove the notification in the title bar.
689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            removeNotification();
690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // activity used for update powerdown menu
693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mPowerStatus = POWER_DOWN;
694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (isRdsSupported()) {
696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stopRdsThread();
697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mWakeLock.isHeld()) {
700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mWakeLock.release();
701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Remove the notification in the title bar.
704a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        removeNotification();
705a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return true;
706a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
707a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
708a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public int getPowerStatus() {
709a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mPowerStatus;
710a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
711a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
712a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
713a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Tune to a station
714a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
715a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param frequency The frequency to tune
716a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
717a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true, success; false, fail.
718a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
719a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void tuneStationAsync(float frequency) {
720a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_TUNE_FINISHED);
721a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
722a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
723a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putFloat(FM_FREQUENCY, frequency);
724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_TUNE_FINISHED);
725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean tuneStation(float frequency) {
730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus == POWER_UP) {
731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            setRds(false);
732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            boolean bRet = FmNative.tune(frequency);
733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (bRet) {
734a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setRds(true);
735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mCurrentStation = FmUtils.computeStation(frequency);
736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                FmStation.setCurrentStation(mContext, mCurrentStation);
737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                updatePlayingNotification();
738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
739a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            setMute(false);
740a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return bRet;
741a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
742a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
743a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // if earphone is not insert, not power up
744a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!isAntennaAvailable()) {
745a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
746a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
747a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
748a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // if not power up yet, should powerup first
749a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean tune = false;
750a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
751a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (powerUp(frequency)) {
752a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            tune = playFrequency(frequency);
753a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
754a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
755a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return tune;
756a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
757a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
758a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
759a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Seek station according frequency and direction
760a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
761a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param frequency start frequency(100KHZ, 87.5)
762a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param isUp direction(true, next station; false, previous station)
763a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
764a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return the frequency after seek
765a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
766a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void seekStationAsync(float frequency, boolean isUp) {
767a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED);
768a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 2;
769a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
770a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putFloat(FM_FREQUENCY, frequency);
771a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putBoolean(OPTION, isUp);
772a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SEEK_FINISHED);
773a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
774a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
775a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
776a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
777a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private float seekStation(float frequency, boolean isUp) {
778a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus != POWER_UP) {
779a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return -1;
780a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
781a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
782a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setRds(false);
783a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsNativeSeeking = true;
784a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        float fRet = FmNative.seek(frequency, isUp);
785a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsNativeSeeking = false;
786a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // make mIsStopScanCalled false, avoid stop scan make this true,
787a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // when start scan, it will return null.
788a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsStopScanCalled = false;
789a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return fRet;
790a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
791a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
792a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
793a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Scan stations
794a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
795a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void startScanAsync() {
796a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED);
797a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_SCAN_FINISHED);
798a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
799a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
800a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int[] startScan() {
801a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int[] stations = null;
802a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
803a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setRds(false);
804a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setMute(true);
805a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        short[] stationsInShort = null;
806a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mIsStopScanCalled) {
807a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsNativeScanning = true;
808a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stationsInShort = FmNative.autoScan();
809a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsNativeScanning = false;
810a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
811a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
812a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setRds(true);
813a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsStopScanCalled) {
814a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Received a message to power down FM, or interrupted by a phone
815a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // call. Do not return any stations. stationsInShort = null;
816a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // if cancel scan, return invalid station -100
817a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stationsInShort = new short[] {
818a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                -100
819a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            };
820a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsStopScanCalled = false;
821a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
822a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
823a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != stationsInShort) {
824a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int size = stationsInShort.length;
825a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stations = new int[size];
826a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            for (int i = 0; i < size; i++) {
827a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                stations[i] = stationsInShort[i];
828a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
829a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
830a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return stations;
831a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
832a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
833a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
834a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check FM Radio is in scan progress or not
835a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
836a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return if in scan progress return true, otherwise return false.
837a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
838a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isScanning() {
839a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsScanning;
840a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
841a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
842a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
843a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Stop scan progress
844a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
845a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true if can stop scan, otherwise return false.
846a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
847a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean stopScan() {
848a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus != POWER_UP) {
849a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
850a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
851a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
852a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean bRet = false;
853a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SCAN_FINISHED);
854a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SEEK_FINISHED);
855a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsNativeScanning || mIsNativeSeeking) {
856a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsStopScanCalled = true;
857a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            bRet = FmNative.stopScan();
858a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
859a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return bRet;
860a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
861a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
862a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
863a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check FM is in seek progress or not
864a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
865a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true if in seek progress, otherwise return false.
866a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
867a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isSeeking() {
868a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsNativeSeeking;
869a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
870a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
871a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
872a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set RDS
873a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
874a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param on true, enable RDS; false, disable RDS.
875a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
876a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setRdsAsync(boolean on) {
877a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
878a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SET_RDS_FINISHED);
879a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
880a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putBoolean(OPTION, on);
881a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SET_RDS_FINISHED);
882a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
883a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
884a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
885a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
886a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int setRds(boolean on) {
887a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus != POWER_UP) {
888a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return -1;
889a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
890a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int ret = -1;
891a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (isRdsSupported()) {
892a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            ret = FmNative.setRds(on);
893a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
894a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return ret;
895a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
896a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
897a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
898a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get PS information
899a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
900a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return PS information
901a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
902a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public String getPs() {
903a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mPsString;
904a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
905a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
906a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
907a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get RT information
908a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
909a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return RT information
910a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
911a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public String getRtText() {
912a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mRtTextString;
913a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
914a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
915a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
916a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get AF frequency
917a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
918a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return AF frequency
919a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
920a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void activeAfAsync() {
921a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_ACTIVE_AF_FINISHED);
922a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_ACTIVE_AF_FINISHED);
923a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
924a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
925a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int activeAf() {
926a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus != POWER_UP) {
927a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.w(TAG, "activeAf, FM is not powered up");
928a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return -1;
929a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
930a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
931a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int frequency = FmNative.activeAf();
932a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return frequency;
933a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
934a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
935a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
936a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Mute or unmute FM voice
937a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
938a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param mute true for mute, false for unmute
939a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
940a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return (true, success; false, failed)
941a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
942a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setMuteAsync(boolean mute) {
943a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SET_MUTE_FINISHED);
944a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
945a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
946a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putBoolean(OPTION, mute);
947a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SET_MUTE_FINISHED);
948a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
949a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
950a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
951a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
952a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
953a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Mute or unmute FM voice
954a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
955a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param mute true for mute, false for unmute
956a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
957a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return (1, success; other, failed)
958a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
959a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public int setMute(boolean mute) {
960a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus != POWER_UP) {
961a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.w(TAG, "setMute, FM is not powered up");
962a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return -1;
963a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
964a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int iRet = FmNative.setMute(mute);
965a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsMuted = mute;
966a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return iRet;
967a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
968a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
969a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
970a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check the latest status is mute or not
971a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
972a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return (true, mute; false, unmute)
973a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
974a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isMuted() {
975a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsMuted;
976a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
977a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
978a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
979a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check whether RDS is support in driver
980a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
981a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return (true, support; false, not support)
982a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
983a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isRdsSupported() {
984a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean isRdsSupported = (FmNative.isRdsSupport() == 1);
985a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return isRdsSupported;
986a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
987a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
988a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
989a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check whether speaker used or not
990a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
991a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true if use speaker, otherwise return false
992a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
993a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isSpeakerUsed() {
994a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsSpeakerUsed;
995a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
996a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
997a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
998a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Initial service and current station
999a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1000a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param iCurrentStation current station frequency
1001a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1002a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void initService(int iCurrentStation) {
1003a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsServiceInited = true;
1004a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mCurrentStation = iCurrentStation;
1005a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1006a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1007a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1008a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check service is initialed or not
1009a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1010a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true if initialed, otherwise return false
1011a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1012a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isServiceInited() {
1013a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsServiceInited;
1014a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1015a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1016a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1017a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get FM service current station frequency
1018a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1019a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return Current station frequency
1020a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1021a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public int getFrequency() {
1022a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mCurrentStation;
1023a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1024a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1025a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1026a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set FM service station frequency
1027a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1028a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param station Current station
1029a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1030a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setFrequency(int station) {
1031a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mCurrentStation = station;
1032a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1033a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1034a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1035a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * resume FM audio
1036a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1037a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void resumeFmAudio() {
1038a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // If not check mIsAudioFocusHeld && power up, when scan canceled,
1039a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // this will be resume first, then execute power down. it will cause
1040a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // nosise.
1041a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsAudioFocusHeld && (mPowerStatus == POWER_UP)) {
1042a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            enableFmAudio(true);
1043a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1044a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1045a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1046a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1047a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Switch antenna There are two types of antenna(long and short) If long
1048a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * antenna(most is this type), must plug in earphone as antenna to receive
1049a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * FM. If short antenna, means there is a short antenna if phone already,
1050a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * can receive FM without earphone.
1051a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1052a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param antenna antenna (0, long antenna, 1 short antenna)
1053a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1054a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return (0, success; 1 failed; 2 not support)
1055a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1056a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void switchAntennaAsync(int antenna) {
1057a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
10586e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SWITCH_ANTENNA);
1059a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1060a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
10616e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang        bundle.putInt(FmListener.SWITCH_ANTENNA_VALUE, antenna);
10626e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SWITCH_ANTENNA);
1063a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
1064a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
1065a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1066a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1067a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1068a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Need native support whether antenna support interface.
1069a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1070a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param antenna antenna (0, long antenna, 1 short antenna)
1071a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1072a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return (0, success; 1 failed; 2 not support)
1073a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1074a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int switchAntenna(int antenna) {
1075a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // if fm not powerup, switchAntenna will flag whether has earphone
1076a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int ret = FmNative.switchAntenna(antenna);
1077a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return ret;
1078a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1079a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1080a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1081a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Start recording
1082a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1083a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void startRecordingAsync() {
1084a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_STARTRECORDING_FINISHED);
1085a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STARTRECORDING_FINISHED);
1086a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1087a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1088a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void startRecording() {
1089a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        sRecordingSdcard = FmUtils.getDefaultStoragePath();
1090a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (sRecordingSdcard == null || sRecordingSdcard.isEmpty()) {
1091a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.d(TAG, "startRecording, may be no sdcard");
1092a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT);
1093a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return;
1094a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1095a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1096a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mFmRecorder == null) {
1097a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFmRecorder = new FmRecorder();
1098a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFmRecorder.registerRecorderStateListener(FmService.this);
1099a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (isSdcardReady(sRecordingSdcard)) {
1102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFmRecorder.startRecording(mContext);
1103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
1104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            onRecorderError(FmRecorder.ERROR_SDCARD_NOT_PRESENT);
1105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean isSdcardReady(String sdcardPath) {
1109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mSdcardStateMap.isEmpty()) {
1110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mSdcardStateMap.get(sdcardPath) != null && !mSdcardStateMap.get(sdcardPath)) {
1111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Log.d(TAG, "isSdcardReady, return false");
1112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return false;
1113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return true;
1116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * stop recording
1120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void stopRecordingAsync() {
1122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_STOPRECORDING_FINISHED);
1123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_STOPRECORDING_FINISHED);
1124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean stopRecording() {
1127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mFmRecorder == null) {
1128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.e(TAG, "stopRecording, called without a valid recorder!!");
1129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
1130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        synchronized (mStopRecordingLock) {
1132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFmRecorder.stopRecording();
1133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return true;
1135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Save recording file according name or discard recording file if name is
1139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * null
1140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param newName New recording file name
1142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void saveRecordingAsync(String newName) {
1144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_SAVERECORDING_FINISHED);
1145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
1146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
1147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putString(RECODING_FILE_NAME, newName);
1148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_SAVERECORDING_FINISHED);
1149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
1150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
1151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void saveRecording(String newName) {
1154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mFmRecorder != null) {
1155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (newName != null) {
1156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFmRecorder.saveRecording(FmService.this, newName);
1157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return;
1158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFmRecorder.discardRecording();
1160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get record time
1165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return Record time
1167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public long getRecordTime() {
1169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mFmRecorder != null) {
1170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return mFmRecorder.getRecordTime();
1171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return 0;
1173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set recording mode
1177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param isRecording true, enter recoding mode; false, exit recording mode
1179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setRecordingModeAsync(boolean isRecording) {
1181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_RECORD_MODE_CHANED);
1182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
1183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
1184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putBoolean(OPTION, isRecording);
1185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_RECORD_MODE_CHANED);
1186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
1187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
1188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void setRecordingMode(boolean isRecording) {
1191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsInRecordingMode = isRecording;
1192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mFmRecorder != null) {
1193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (!isRecording) {
1194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mFmRecorder.getState() != FmRecorder.STATE_IDLE) {
1195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmRecorder.stopRecording();
1196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                resumeFmAudio();
1198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setMute(false);
1199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return;
1200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // reset recorder to unused status
1202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFmRecorder.resetRecorder();
1203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get current recording mode
1208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return if in recording mode return true, otherwise return false;
1210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean getRecordingMode() {
1212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsInRecordingMode;
1213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get record state
1217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return record state
1219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public int getRecorderState() {
1221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mFmRecorder) {
1222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return mFmRecorder.getState();
1223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return FmRecorder.STATE_INVALID;
1225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get recording file name
1229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return recording file name
1231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public String getRecordingName() {
1233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mFmRecorder) {
1234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return mFmRecorder.getRecordFileName();
1235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return null;
1237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
1240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onCreate() {
1241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        super.onCreate();
1242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mContext = getApplicationContext();
1243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
1244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
1245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
1246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
1247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mWakeLock.setReferenceCounted(false);
1248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        sRecordingSdcard = FmUtils.getDefaultStoragePath();
1249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        registerFmBroadcastReceiver();
1251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        registerSdcardReceiver();
1252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        registerAudioPortUpdateListener();
1253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        HandlerThread handlerThread = new HandlerThread("FmRadioServiceThread");
1255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        handlerThread.start();
1256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler = new FmRadioServiceHandler(handlerThread.getLooper());
1257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        openDevice();
1259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // set speaker to default status, avoid setting->clear data.
1260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setForceUse(mIsSpeakerUsed);
1261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        initAudioRecordSink();
1263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        createRenderThread();
1264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void registerAudioPortUpdateListener() {
1267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAudioPortUpdateListener == null) {
1268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioPortUpdateListener = new FmOnAudioPortUpdateListener();
1269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioManager.registerAudioPortUpdateListener(mAudioPortUpdateListener);
1270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void unregisterAudioPortUpdateListener() {
1274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAudioPortUpdateListener != null) {
1275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioManager.unregisterAudioPortUpdateListener(mAudioPortUpdateListener);
1276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioPortUpdateListener = null;
1277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
128087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    // This function may be called in different threads.
128187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    // Need to add "synchronized" to make sure mAudioRecord and mAudioTrack are the newest.
128287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    // Thread 1: onCreate() or startRender()
128387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    // Thread 2: onAudioPatchListUpdate() or startRender()
128487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    private synchronized void initAudioRecordSink() {
12851df5780e910ddc811b7b49ef6d9bdb303dccf08dEric Laurent        mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.RADIO_TUNER,
1286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, RECORD_BUF_SIZE);
1287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
1288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, RECORD_BUF_SIZE, AudioTrack.MODE_STREAM);
1289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
129119ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar    private synchronized int createAudioPatch() {
12924b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang        Log.d(TAG, "createAudioPatch");
129319ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar        int status = AudioManager.SUCCESS;
1294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAudioPatch != null) {
1295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.d(TAG, "createAudioPatch, mAudioPatch is not null, return");
129619ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar            return status;
1297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioSource = null;
1300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioSink = null;
1301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
1302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioManager.listAudioPorts(ports);
1303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        for (AudioPort port : ports) {
1304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (port instanceof AudioDevicePort) {
1305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int type = ((AudioDevicePort) port).type();
1306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                String name = AudioSystem.getOutputDeviceName(type);
1307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (type == AudioSystem.DEVICE_IN_FM_TUNER) {
1308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mAudioSource = (AudioDevicePort) port;
1309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else if (type == AudioSystem.DEVICE_OUT_WIRED_HEADSET ||
1310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        type == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
1311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mAudioSink = (AudioDevicePort) port;
1312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAudioSource != null && mAudioSink != null) {
1316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioDevicePortConfig sourceConfig = (AudioDevicePortConfig) mAudioSource
1317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    .activeConfig();
1318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioDevicePortConfig sinkConfig = (AudioDevicePortConfig) mAudioSink.activeConfig();
1319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioPatch[] audioPatchArray = new AudioPatch[] {null};
132019ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar            status = mAudioManager.createAudioPatch(audioPatchArray,
1321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    new AudioPortConfig[] {sourceConfig},
1322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    new AudioPortConfig[] {sinkConfig});
1323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioPatch = audioPatchArray[0];
1324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
132519ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar        return status;
1326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private FmOnAudioPortUpdateListener mAudioPortUpdateListener = null;
1329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private class FmOnAudioPortUpdateListener implements OnAudioPortUpdateListener {
1331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
1332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * Callback method called upon audio port list update.
1333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * @param portList the updated list of audio ports
1334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
1335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
1336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void onAudioPortListUpdate(AudioPort[] portList) {
13374b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang            // Ingore audio port update
1338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
1341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * Callback method called upon audio patch list update.
1342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         *
1343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * @param patchList the updated list of audio patches
1344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
1345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
1346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void onAudioPatchListUpdate(AudioPatch[] patchList) {
1347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mPowerStatus != POWER_UP) {
134887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                Log.d(TAG, "onAudioPatchListUpdate, not power up");
1349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return;
1350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (!mIsAudioFocusHeld) {
135387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                Log.d(TAG, "onAudioPatchListUpdate no audio focus");
1354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return;
1355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mAudioPatch != null) {
1358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
1359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mAudioManager.listAudioPatches(patches);
136087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                // When BT or WFD is connected, native will remove the patch (mixer -> device).
136187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                // Need to recreate AudioRecord and AudioTrack for this case.
136287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                if (isPatchMixerToDeviceRemoved(patches)) {
136387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                    Log.d(TAG, "onAudioPatchListUpdate reinit for BT or WFD connected");
136487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                    initAudioRecordSink();
136587171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                    startRender();
136687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                    return;
136787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                }
1368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (isPatchMixerToEarphone(patches)) {
1369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stopRender();
1370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else {
1371a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    releaseAudioPatch();
1372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    startRender();
1373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (mIsRender) {
1375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
1376a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mAudioManager.listAudioPatches(patches);
1377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (isPatchMixerToEarphone(patches)) {
137819ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    int status;
1379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stopAudioTrack();
1380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stopRender();
138119ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    status = createAudioPatch();
138219ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    if (status != AudioManager.SUCCESS){
138319ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                       Log.d(TAG, "onAudioPatchListUpdate: fallback as createAudioPatch failed");
138419ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                       startRender();
138519ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    }
1386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
1391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * Callback method called when the mediaserver dies
1392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
1393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
1394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void onServiceDied() {
1395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            enableFmAudio(false);
1396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private synchronized void releaseAudioPatch() {
1400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAudioPatch != null) {
14014b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang            Log.d(TAG, "releaseAudioPatch");
1402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioManager.releaseAudioPatch(mAudioPatch);
1403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioPatch = null;
1404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioSource = null;
1406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioSink = null;
1407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void registerFmBroadcastReceiver() {
1410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        IntentFilter filter = new IntentFilter();
1411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(SOUND_POWER_DOWN_MSG);
1412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(Intent.ACTION_SHUTDOWN);
1413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(Intent.ACTION_SCREEN_ON);
1414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(Intent.ACTION_SCREEN_OFF);
1415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(Intent.ACTION_HEADSET_PLUG);
1416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mBroadcastReceiver = new FmServiceBroadcastReceiver();
1417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        registerReceiver(mBroadcastReceiver, filter);
1418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void unregisterFmBroadcastReceiver() {
1421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mBroadcastReceiver) {
1422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            unregisterReceiver(mBroadcastReceiver);
1423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mBroadcastReceiver = null;
1424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
1428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onDestroy() {
1429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioManager.setParameters("AudioFmPreStop=1");
1430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setMute(true);
1431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // stop rds first, avoid blocking other native method
1432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (isRdsSupported()) {
1433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stopRdsThread();
1434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        unregisterFmBroadcastReceiver();
1436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        unregisterSdcardListener();
1437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        abandonAudioFocus();
1438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        exitFm();
1439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mFmRecorder) {
1440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFmRecorder = null;
1441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        exitRenderThread();
1443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        releaseAudioPatch();
1444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        unregisterAudioPortUpdateListener();
1445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        super.onDestroy();
1446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Exit FMRadio application
1450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void exitFm() {
1452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsAudioFocusHeld = false;
1453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Stop FM recorder if it is working
1454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mFmRecorder) {
1455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            synchronized (mStopRecordingLock) {
1456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int fmState = mFmRecorder.getState();
1457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (FmRecorder.STATE_RECORDING == fmState) {
1458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmRecorder.stopRecording();
1459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // When exit, we set the audio path back to earphone.
1464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsNativeScanning || mIsNativeSeeking) {
1465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stopScan();
1466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeCallbacksAndMessages(null);
1469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.removeMessages(FmListener.MSGID_FM_EXIT);
1470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendEmptyMessage(FmListener.MSGID_FM_EXIT);
1471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
1474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onConfigurationChanged(Configuration newConfig) {
1475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        super.onConfigurationChanged(newConfig);
1476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Change the notification string.
1477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus == POWER_UP) {
1478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            showPlayingNotification();
1479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
1483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public int onStartCommand(Intent intent, int flags, int startId) {
1484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int ret = super.onStartCommand(intent, flags, startId);
1485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (intent != null) {
1487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            String action = intent.getAction();
1488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (FM_SEEK_PREVIOUS.equals(action)) {
1489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                seekStationAsync(FmUtils.computeFrequency(mCurrentStation), false);
1490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (FM_SEEK_NEXT.equals(action)) {
1491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                seekStationAsync(FmUtils.computeFrequency(mCurrentStation), true);
1492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (FM_TURN_OFF.equals(action)) {
1493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                powerDownAsync();
1494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return START_NOT_STICKY;
1497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Start RDS thread to update RDS information
1501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void startRdsThread() {
1503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsRdsThreadExit = false;
1504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mRdsThread) {
1505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return;
1506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mRdsThread = new Thread() {
1508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public void run() {
1509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                while (true) {
1510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (mIsRdsThreadExit) {
1511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        break;
1512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
1513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int iRdsEvents = FmNative.readRds();
1515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (iRdsEvents != 0) {
1516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        Log.d(TAG, "startRdsThread, is rds events: " + iRdsEvents);
1517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
1518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (RDS_EVENT_PROGRAMNAME == (RDS_EVENT_PROGRAMNAME & iRdsEvents)) {
1520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        byte[] bytePS = FmNative.getPs();
1521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (null != bytePS) {
1522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            String ps = new String(bytePS).trim();
1523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            if (!mPsString.equals(ps)) {
1524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                updatePlayingNotification();
1525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            }
1526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            ContentValues values = null;
1527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            if (FmStation.isStationExist(mContext, mCurrentStation)) {
1528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values = new ContentValues(1);
1529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values.put(Station.PROGRAM_SERVICE, ps);
1530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmStation.updateStationToDb(mContext, mCurrentStation, values);
1531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            } else {
1532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values = new ContentValues(2);
1533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values.put(Station.FREQUENCY, mCurrentStation);
1534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values.put(Station.PROGRAM_SERVICE, ps);
1535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmStation.insertStationToDb(mContext, values);
1536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            }
1537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            setPs(ps);
1538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
1539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
1540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (RDS_EVENT_LAST_RADIOTEXT == (RDS_EVENT_LAST_RADIOTEXT & iRdsEvents)) {
1542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        byte[] byteLRText = FmNative.getLrText();
1543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (null != byteLRText) {
1544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            String rds = new String(byteLRText).trim();
15459dd97ba8b788592899438fd2c7d4ab88bfe3dccdBenson Huang                            if (!mRtTextString.equals(rds)) {
15469dd97ba8b788592899438fd2c7d4ab88bfe3dccdBenson Huang                                updatePlayingNotification();
15479dd97ba8b788592899438fd2c7d4ab88bfe3dccdBenson Huang                            }
1548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            setLRText(rds);
1549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            ContentValues values = null;
1550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            if (FmStation.isStationExist(mContext, mCurrentStation)) {
1551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values = new ContentValues(1);
1552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values.put(Station.RADIO_TEXT, rds);
1553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmStation.updateStationToDb(mContext, mCurrentStation, values);
1554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            } else {
1555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values = new ContentValues(2);
1556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values.put(Station.FREQUENCY, mCurrentStation);
1557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                values.put(Station.RADIO_TEXT, rds);
1558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmStation.insertStationToDb(mContext, values);
1559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            }
1560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
1561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
1562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (RDS_EVENT_AF == (RDS_EVENT_AF & iRdsEvents)) {
1564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        /*
1565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                         * add for rds AF
1566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                         */
1567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (mIsScanning || mIsSeeking) {
1568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            Log.d(TAG, "startRdsThread, seek or scan going, no need to tune here");
1569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        } else if (mPowerStatus == POWER_DOWN) {
1570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            Log.d(TAG, "startRdsThread, fm is power down, do nothing.");
1571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        } else {
1572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            int iFreq = FmNative.activeAf();
1573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            if (FmUtils.isValidStation(iFreq)) {
1574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                // if the new frequency is not equal to current
1575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                // frequency.
1576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                if (mCurrentStation != iFreq) {
1577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                    if (!mIsScanning && !mIsSeeking) {
1578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                        Log.d(TAG, "startRdsThread, seek or scan not going,"
1579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                                + "need to tune here");
1580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                        tuneStationAsync(FmUtils.computeFrequency(iFreq));
1581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                    }
1582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                }
1583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            }
1584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
1585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
1586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // Do not handle other events.
1587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // Sleep 500ms to reduce inquiry frequency
1588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    try {
1589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        final int hundredMillisecond = 500;
1590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        Thread.sleep(hundredMillisecond);
1591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    } catch (InterruptedException e) {
1592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        e.printStackTrace();
1593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
1594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        };
1597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mRdsThread.start();
1598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Stop RDS thread to stop listen station RDS change
1602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void stopRdsThread() {
1604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mRdsThread) {
1605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Must call closedev after stopRDSThread.
1606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsRdsThreadExit = true;
1607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mRdsThread = null;
1608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set PS information
1613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param ps The ps information
1615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void setPs(String ps) {
1617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (0 != mPsString.compareTo(ps)) {
1618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPsString = ps;
1619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Bundle bundle = new Bundle(3);
1620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_PS_CHANGED);
1621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            bundle.putString(FmListener.KEY_PS_INFO, mPsString);
1622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            notifyActivityStateChanged(bundle);
1623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } // else New PS is the same as current
1624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set RT information
1628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param lrtText The RT information
1630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void setLRText(String lrtText) {
1632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (0 != mRtTextString.compareTo(lrtText)) {
1633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mRtTextString = lrtText;
1634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Bundle bundle = new Bundle(3);
1635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RT_CHANGED);
1636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            bundle.putString(FmListener.KEY_RT_INFO, mRtTextString);
1637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            notifyActivityStateChanged(bundle);
1638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } // else New RT is the same as current
1639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Open or close FM Radio audio
1643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param enable true, open FM audio; false, close FM audio;
1645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void enableFmAudio(boolean enable) {
1647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (enable) {
1648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if ((mPowerStatus != POWER_UP) || !mIsAudioFocusHeld) {
1649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Log.d(TAG, "enableFmAudio, current not available return.mIsAudioFocusHeld:"
1650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    + mIsAudioFocusHeld);
1651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return;
1652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            startAudioTrack();
1655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
1656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAudioManager.listAudioPatches(patches);
1657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mAudioPatch == null) {
1658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (isPatchMixerToEarphone(patches)) {
165919ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    int status;
1660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stopAudioTrack();
1661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stopRender();
166219ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    status = createAudioPatch();
166319ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    if (status != AudioManager.SUCCESS){
166419ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                       Log.d(TAG, "enableFmAudio: fallback as createAudioPatch failed");
166519ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                       startRender();
166619ca9f8472208d429eff4e5c736d3084eb712b17Dhananjay Kumar                    }
1667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else {
1668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    startRender();
1669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
1672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            releaseAudioPatch();
1673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stopRender();
1674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    // Make sure patches count will not be 0
1678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean isPatchMixerToEarphone(ArrayList<AudioPatch> patches) {
1679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int deviceCount = 0;
1680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int deviceEarphoneCount = 0;
1681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        for (AudioPatch patch : patches) {
1682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioPortConfig[] sources = patch.sources();
1683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioPortConfig[] sinks = patch.sinks();
1684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioPortConfig sourceConfig = sources[0];
1685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioPortConfig sinkConfig = sinks[0];
1686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioPort sourcePort = sourceConfig.port();
1687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            AudioPort sinkPort = sinkConfig.port();
16887c38d14648a1b518a1bea44b4488bfe8e5add6e2Benson Huang            Log.d(TAG, "isPatchMixerToEarphone " + sourcePort + " ====> " + sinkPort);
1689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (sourcePort instanceof AudioMixPort && sinkPort instanceof AudioDevicePort) {
1690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                deviceCount++;
1691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int type = ((AudioDevicePort) sinkPort).type();
1692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (type == AudioSystem.DEVICE_OUT_WIRED_HEADSET ||
1693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        type == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
1694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    deviceEarphoneCount++;
1695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (deviceEarphoneCount == 1 && deviceCount == deviceEarphoneCount) {
1699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return true;
1700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return false;
1702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
170487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    // Check whether the patch (mixer -> device) is removed by native.
170587171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    // If no patch (mixer -> device), return true.
170687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    private boolean isPatchMixerToDeviceRemoved(ArrayList<AudioPatch> patches) {
170787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang        boolean noMixerToDevice = true;
170887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang        for (AudioPatch patch : patches) {
170987171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            AudioPortConfig[] sources = patch.sources();
171087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            AudioPortConfig[] sinks = patch.sinks();
171187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            AudioPortConfig sourceConfig = sources[0];
171287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            AudioPortConfig sinkConfig = sinks[0];
171387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            AudioPort sourcePort = sourceConfig.port();
171487171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            AudioPort sinkPort = sinkConfig.port();
171587171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang
171687171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            if (sourcePort instanceof AudioMixPort && sinkPort instanceof AudioDevicePort) {
171787171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                noMixerToDevice = false;
171887171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang                break;
171987171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang            }
172087171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang        }
172187171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang        return noMixerToDevice;
172287171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang    }
172387171fc8d875e8c32c0cc77913ae06879922a888Hochi Huang
1724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Show notification
1726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void showPlayingNotification() {
1728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (isActivityForeground() || mIsScanning
1729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                || (getRecorderState() == FmRecorder.STATE_RECORDING)) {
1730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.w(TAG, "showPlayingNotification, do not show main notification.");
1731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return;
1732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        String stationName = "";
173410f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang        String radioText = "";
1735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        ContentResolver resolver = mContext.getContentResolver();
1736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor cursor = null;
1737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        try {
1738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            cursor = resolver.query(
1739a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Station.CONTENT_URI,
1740a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    FmStation.COLUMNS,
1741a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Station.FREQUENCY + "=?",
1742a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    new String[] { String.valueOf(mCurrentStation) },
1743a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    null);
1744a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (cursor != null && cursor.moveToFirst()) {
1745a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // If the station name is not exist, show program service(PS) instead
1746a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                stationName = cursor.getString(cursor.getColumnIndex(Station.STATION_NAME));
1747a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (TextUtils.isEmpty(stationName)) {
1748a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stationName = cursor.getString(cursor.getColumnIndex(Station.PROGRAM_SERVICE));
1749a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
175010f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang                radioText = cursor.getString(cursor.getColumnIndex(Station.RADIO_TEXT));
1751a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1752a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else {
1753a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Log.d(TAG, "showPlayingNotification, cursor is null");
1754a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1755a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } finally {
1756a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (cursor != null) {
1757a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                cursor.close();
1758a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1759a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1760a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1761a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Intent aIntent = new Intent(Intent.ACTION_MAIN);
1762a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        aIntent.addCategory(Intent.CATEGORY_LAUNCHER);
1763a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1764a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        aIntent.setClassName(getPackageName(), mTargetClassName);
1765a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        PendingIntent pAIntent = PendingIntent.getActivity(mContext, 0, aIntent, 0);
1766a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1767a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null == mNotificationBuilder) {
1768a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNotificationBuilder = new Notification.Builder(mContext);
1769a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNotificationBuilder.setSmallIcon(R.drawable.ic_launcher);
1770a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNotificationBuilder.setShowWhen(false);
1771a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNotificationBuilder.setAutoCancel(true);
1772a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1773a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Intent intent = new Intent(FM_SEEK_PREVIOUS);
1774a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            intent.setClass(mContext, FmService.class);
1775a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            PendingIntent pIntent = PendingIntent.getService(mContext, 0, intent, 0);
1776a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNotificationBuilder.addAction(R.drawable.btn_fm_prevstation, "", pIntent);
1777a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            intent = new Intent(FM_TURN_OFF);
1778a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            intent.setClass(mContext, FmService.class);
1779a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            pIntent = PendingIntent.getService(mContext, 0, intent, 0);
1780a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNotificationBuilder.addAction(R.drawable.btn_fm_rec_stop_enabled, "", pIntent);
1781a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            intent = new Intent(FM_SEEK_NEXT);
1782a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            intent.setClass(mContext, FmService.class);
1783a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            pIntent = PendingIntent.getService(mContext, 0, intent, 0);
1784a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNotificationBuilder.addAction(R.drawable.btn_fm_nextstation, "", pIntent);
1785a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1786a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mNotificationBuilder.setContentIntent(pAIntent);
1787a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bitmap largeIcon = FmUtils.createNotificationLargeIcon(mContext,
1788a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                FmUtils.formatStation(mCurrentStation));
1789a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mNotificationBuilder.setLargeIcon(largeIcon);
1790a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Show FM Radio if empty
1791a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (TextUtils.isEmpty(stationName)) {
1792a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stationName = getString(R.string.app_name);
1793a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1794a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mNotificationBuilder.setContentTitle(stationName);
179510f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang        // If radio text is "" or null, we also need to update notification.
179610f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang        mNotificationBuilder.setContentText(radioText);
179710f39cd23b5d7e01655c6b0a2a9b3e59271127e6Benson Huang        Log.d(TAG, "showPlayingNotification PS:" + stationName + ", RT:" + radioText);
1798a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1799a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Notification n = mNotificationBuilder.build();
1800a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        n.flags &= ~Notification.FLAG_NO_CLEAR;
1801a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        startForeground(NOTIFICATION_ID, n);
1802a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1803a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1804a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1805a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Show notification
1806a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1807a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void showRecordingNotification(Notification notification) {
1808a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        startForeground(NOTIFICATION_ID, notification);
1809a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1810a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1811a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1812a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Remove notification
1813a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1814a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void removeNotification() {
1815a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        stopForeground(true);
1816a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1817a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1818a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1819a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Update notification
1820a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1821a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void updatePlayingNotification() {
1822a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus == POWER_UP) {
1823a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            showPlayingNotification();
1824a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1825a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1826a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1827a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1828a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Register sdcard listener for record
1829a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1830a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void registerSdcardReceiver() {
1831a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mSdcardListener == null) {
1832a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mSdcardListener = new SdcardListener();
1833a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1834a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        IntentFilter filter = new IntentFilter();
1835a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addDataScheme("file");
1836a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
1837a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
1838a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        filter.addAction(Intent.ACTION_MEDIA_EJECT);
1839a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        registerReceiver(mSdcardListener, filter);
1840a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1841a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1842a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void unregisterSdcardListener() {
1843a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != mSdcardListener) {
1844a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            unregisterReceiver(mSdcardListener);
1845a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1846a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1847a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1848a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void updateSdcardStateMap(Intent intent) {
1849a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        String action = intent.getAction();
1850a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        String sdcardPath = null;
1851a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Uri mountPointUri = intent.getData();
1852a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mountPointUri != null) {
1853a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            sdcardPath = mountPointUri.getPath();
1854a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (sdcardPath != null) {
1855a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (Intent.ACTION_MEDIA_EJECT.equals(action)) {
1856a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mSdcardStateMap.put(sdcardPath, false);
1857a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) {
1858a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mSdcardStateMap.put(sdcardPath, false);
1859a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
1860a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mSdcardStateMap.put(sdcardPath, true);
1861a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1862a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1863a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1864a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1865a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1866a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1867a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Notify FM recorder state
1868a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1869a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param state The current FM recorder state
1870a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1871a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
1872a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onRecorderStateChanged(int state) {
1873a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mRecordState = state;
1874a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(2);
1875a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RECORDSTATE_CHANGED);
1876a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.KEY_RECORDING_STATE, state);
1877a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        notifyActivityStateChanged(bundle);
1878a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1879a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1880a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1881a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Notify FM recorder error message
1882a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1883a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param error The recorder error type
1884a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1885a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
1886a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onRecorderError(int error) {
1887a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // if media server die, will not enable FM audio, and convert to
1888a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // ERROR_PLAYER_INATERNAL, call back to activity showing toast.
1889a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mRecorderErrorType = error;
1890a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1891a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(2);
1892a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.LISTEN_RECORDERROR);
1893a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.KEY_RECORDING_ERROR_TYPE, mRecorderErrorType);
1894a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        notifyActivityStateChanged(bundle);
1895a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1896a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1897a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1898a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check and go next(play or show tips) after recorder file play
1899a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * back finish.
1900a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Two cases:
1901a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * 1. With headset  -> play FM
1902a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * 2. Without headset -> show plug in earphone tips
1903a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1904a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void checkState() {
1905a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (isHeadSetIn()) {
1906a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // with headset
1907a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mPowerStatus == POWER_UP) {
1908a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                resumeFmAudio();
1909a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setMute(false);
1910a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else {
1911a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                powerUpAsync(FmUtils.computeFrequency(mCurrentStation));
1912a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1913a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
1914a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // without headset need show plug in earphone tips
1915a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            switchAntennaAsync(mValueHeadSetPlug);
1916a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1917a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1918a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1919a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1920a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check the headset is plug in or plug out
1921a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1922a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true for plug in; false for plug out
1923a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1924a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean isHeadSetIn() {
1925a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return (0 == mValueHeadSetPlug);
1926a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1927a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1928a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void focusChanged(int focusState) {
1929a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsAudioFocusHeld = false;
1930a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsNativeScanning || mIsNativeSeeking) {
1931a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // make stop scan from activity call to service.
1932a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // notifyActivityStateChanged(FMRadioListener.LISTEN_SCAN_CANCELED);
1933a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            stopScan();
1934a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1935a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1936a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // using handler thread to update audio focus state
1937a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        updateAudioFocusAync(focusState);
1938a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1939a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1940a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1941a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Request audio focus
1942a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1943a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true, success; false, fail;
1944a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1945a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean requestAudioFocus() {
19464f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang        if (FmUtils.getIsSpeakerModeOnFocusLost(mContext)) {
19474f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang            setForceUse(true);
19484f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang            FmUtils.setIsSpeakerModeOnFocusLost(mContext, false);
19494f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang        }
1950a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsAudioFocusHeld) {
1951a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return true;
1952a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1953a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1954a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int audioFocus = mAudioManager.requestAudioFocus(mAudioFocusChangeListener,
1955a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
1956a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsAudioFocusHeld = (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == audioFocus);
1957a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mIsAudioFocusHeld;
1958a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1959a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1960a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1961a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Abandon audio focus
1962a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1963a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void abandonAudioFocus() {
1964a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAudioManager.abandonAudioFocus(mAudioFocusChangeListener);
1965a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsAudioFocusHeld = false;
1966a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1967a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1968a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1969a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Use to interact with other voice related app
1970a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1971a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final OnAudioFocusChangeListener mAudioFocusChangeListener =
1972a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            new OnAudioFocusChangeListener() {
1973a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                /**
1974a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * Handle audio focus change ensure message FIFO
1975a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 *
1976a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * @param focusChange audio focus change state
1977a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 */
1978a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                @Override
1979a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                public void onAudioFocusChange(int focusChange) {
198087970e48c151b8b9533889f317dad79499ced1a0Benson Huang                    Log.d(TAG, "onAudioFocusChange " + focusChange);
1981a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    switch (focusChange) {
1982a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        case AudioManager.AUDIOFOCUS_LOSS:
1983a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            synchronized (this) {
1984a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                mAudioManager.setParameters("AudioFmPreStop=1");
1985a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                setMute(true);
1986a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                focusChanged(AudioManager.AUDIOFOCUS_LOSS);
1987a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            }
1988a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            break;
1989a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1990a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
1991a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            synchronized (this) {
1992a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                mAudioManager.setParameters("AudioFmPreStop=1");
1993a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                setMute(true);
1994a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                focusChanged(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
1995a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            }
1996a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            break;
1997a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1998a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        case AudioManager.AUDIOFOCUS_GAIN:
1999a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            synchronized (this) {
2000a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                updateAudioFocusAync(AudioManager.AUDIOFOCUS_GAIN);
2001a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            }
2002a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            break;
2003a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
200487970e48c151b8b9533889f317dad79499ced1a0Benson Huang                        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
200587970e48c151b8b9533889f317dad79499ced1a0Benson Huang                            synchronized (this) {
200687970e48c151b8b9533889f317dad79499ced1a0Benson Huang                                updateAudioFocusAync(
200787970e48c151b8b9533889f317dad79499ced1a0Benson Huang                                        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK);
200887970e48c151b8b9533889f317dad79499ced1a0Benson Huang                            }
200987970e48c151b8b9533889f317dad79499ced1a0Benson Huang                            break;
201087970e48c151b8b9533889f317dad79499ced1a0Benson Huang
2011a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        default:
2012a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            break;
2013a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2014a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2015a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            };
2016a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2017a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2018a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Audio focus changed, will send message to handler thread. synchronized to
2019a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * ensure one message can go in this method.
2020a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2021a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param focusState AudioManager state
2022a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2023a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private synchronized void updateAudioFocusAync(int focusState) {
2024a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int bundleSize = 1;
2025a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle = new Bundle(bundleSize);
2026a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.KEY_AUDIOFOCUS_CHANGED, focusState);
2027a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Message msg = mFmServiceHandler.obtainMessage(FmListener.MSGID_AUDIOFOCUS_CHANGED);
2028a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        msg.setData(bundle);
2029a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFmServiceHandler.sendMessage(msg);
2030a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2031a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2032a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2033a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Audio focus changed, update FM focus state.
2034a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2035a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param focusState AudioManager state
2036a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2037a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void updateAudioFocus(int focusState) {
2038a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        switch (focusState) {
2039a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case AudioManager.AUDIOFOCUS_LOSS:
2040a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPausedByTransientLossOfFocus = false;
2041a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // play back audio will output with music audio
2042a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // May be affect other recorder app, but the flow can not be
2043a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // execute earlier,
2044a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // It should ensure execute after start/stop record.
2045a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mFmRecorder != null) {
2046a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int fmState = mFmRecorder.getState();
2047a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // only handle recorder state, not handle playback state
2048a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (fmState == FmRecorder.STATE_RECORDING) {
2049a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFmServiceHandler.removeMessages(
2050a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmListener.MSGID_STARTRECORDING_FINISHED);
2051a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFmServiceHandler.removeMessages(
2052a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmListener.MSGID_STOPRECORDING_FINISHED);
2053a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        stopRecording();
2054a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2055a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2056a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                handlePowerDown();
20574f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang                forceToHeadsetMode();
2058a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
2059a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2060a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
2061a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mPowerStatus == POWER_UP) {
2062a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mPausedByTransientLossOfFocus = true;
2063a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2064a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // play back audio will output with music audio
2065a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // May be affect other recorder app, but the flow can not be
2066a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // execute earlier,
2067a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // It should ensure execute after start/stop record.
2068a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mFmRecorder != null) {
2069a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int fmState = mFmRecorder.getState();
2070a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (fmState == FmRecorder.STATE_RECORDING) {
2071a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFmServiceHandler.removeMessages(
2072a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmListener.MSGID_STARTRECORDING_FINISHED);
2073a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFmServiceHandler.removeMessages(
2074a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                FmListener.MSGID_STOPRECORDING_FINISHED);
2075a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        stopRecording();
2076a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2077a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2078a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                handlePowerDown();
20794f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang                forceToHeadsetMode();
2080a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
2081a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2082a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case AudioManager.AUDIOFOCUS_GAIN:
20834f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang                if (FmUtils.getIsSpeakerModeOnFocusLost(mContext)) {
20844f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang                    setForceUse(true);
20854f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang                    FmUtils.setIsSpeakerModeOnFocusLost(mContext, false);
20864f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang                }
2087a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if ((mPowerStatus != POWER_UP) && mPausedByTransientLossOfFocus) {
2088a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    final int bundleSize = 1;
2089a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmServiceHandler.removeMessages(FmListener.MSGID_POWERUP_FINISHED);
2090a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mFmServiceHandler.removeMessages(FmListener.MSGID_POWERDOWN_FINISHED);
2091a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Bundle bundle = new Bundle(bundleSize);
2092a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putFloat(FM_FREQUENCY, FmUtils.computeFrequency(mCurrentStation));
2093a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    handlePowerUp(bundle);
2094a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
209587970e48c151b8b9533889f317dad79499ced1a0Benson Huang                setMute(false);
209687970e48c151b8b9533889f317dad79499ced1a0Benson Huang                break;
209787970e48c151b8b9533889f317dad79499ced1a0Benson Huang
209887970e48c151b8b9533889f317dad79499ced1a0Benson Huang            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
209987970e48c151b8b9533889f317dad79499ced1a0Benson Huang                setMute(true);
2100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
2101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            default:
2103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
2104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
21074f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang    private void forceToHeadsetMode() {
21084f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang        if (mIsSpeakerUsed && isHeadSetIn()) {
21094f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang            AudioSystem.setForceUse(FOR_PROPRIETARY, AudioSystem.FORCE_NONE);
21104f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang            // save user's option to shared preferences.
21114f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang            FmUtils.setIsSpeakerModeOnFocusLost(mContext, true);
21124f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang        }
21134f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang    }
21144f33aa31c5cc96bfb7abca4ad1c8f6f8c04e46afBenson Huang
2115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * FM Radio listener record
2117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static class Record {
2119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int mHashCode; // hash code
2120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        FmListener mCallback; // call back
2121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Register FM Radio listener, activity get service state should call this
2125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * method register FM Radio listener
2126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param callback FM Radio listener
2128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void registerFmRadioListener(FmListener callback) {
2130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        synchronized (mRecords) {
2131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // register callback in AudioProfileService, if the callback is
2132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // exist, just replace the event.
2133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Record record = null;
2134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int hashCode = callback.hashCode();
2135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int n = mRecords.size();
2136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            for (int i = 0; i < n; i++) {
2137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                record = mRecords.get(i);
2138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (hashCode == record.mHashCode) {
2139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    return;
2140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            record = new Record();
2143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            record.mHashCode = hashCode;
2144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            record.mCallback = callback;
2145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mRecords.add(record);
2146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Call back from service to activity
2151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param bundle The message to activity
2153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void notifyActivityStateChanged(Bundle bundle) {
2155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mRecords.isEmpty()) {
2156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            synchronized (mRecords) {
2157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Iterator<Record> iterator = mRecords.iterator();
2158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                while (iterator.hasNext()) {
2159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Record record = (Record) iterator.next();
2160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    FmListener listener = record.mCallback;
2162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (listener == null) {
2164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        iterator.remove();
2165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        return;
2166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    listener.onCallBack(bundle);
2169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Call back from service to the current request activity
2176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Scan need only notify FmFavoriteActivity if current is FmFavoriteActivity
2177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param bundle The message to activity
2179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void notifyCurrentActivityStateChanged(Bundle bundle) {
2181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mRecords.isEmpty()) {
2182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.d(TAG, "notifyCurrentActivityStateChanged = " + mRecords.size());
2183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            synchronized (mRecords) {
2184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mRecords.size() > 0) {
2185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Record record  = mRecords.get(mRecords.size() - 1);
2186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    FmListener listener = record.mCallback;
2187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (listener == null) {
2188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mRecords.remove(record);
2189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        return;
2190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    listener.onCallBack(bundle);
2192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Unregister FM Radio listener
2199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param callback FM Radio listener
2201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void unregisterFmRadioListener(FmListener callback) {
2203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        remove(callback.hashCode());
2204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Remove call back according hash code
2208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param hashCode The call back hash code
2210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void remove(int hashCode) {
2212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        synchronized (mRecords) {
2213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Iterator<Record> iterator = mRecords.iterator();
2214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            while (iterator.hasNext()) {
2215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Record record = (Record) iterator.next();
2216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (record.mHashCode == hashCode) {
2217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    iterator.remove();
2218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Check recording sd card is unmount
2225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param intent The unmount sd card intent
2227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return true or false indicate whether current recording sd card is
2229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *         unmount or not
2230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isRecordingCardUnmount(Intent intent) {
2232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        String unmountSDCard = intent.getData().toString();
2233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Log.d(TAG, "unmount sd card file path: " + unmountSDCard);
2234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return unmountSDCard.equalsIgnoreCase("file://" + sRecordingSdcard) ? true : false;
2235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int[] updateStations(int[] stations) {
2238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Log.d(TAG, "updateStations.firstValidstation:" + Arrays.toString(stations));
2239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int firstValidstation = mCurrentStation;
2240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int stationNum = 0;
2242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (null != stations) {
2243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int searchedListSize = stations.length;
2244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mIsDistanceExceed) {
2245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                FmStation.cleanSearchedStations(mContext);
2246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                for (int j = 0; j < searchedListSize; j++) {
2247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int freqSearched = stations[j];
2248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (FmUtils.isValidStation(freqSearched) &&
2249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            !FmStation.isFavoriteStation(mContext, freqSearched)) {
2250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        FmStation.insertStationToDb(mContext, freqSearched, null);
2251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else {
2254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // get stations from db
2255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                stationNum = updateDBInLocation(stations);
2256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Log.d(TAG, "updateStations.firstValidstation:" + firstValidstation +
2260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                ",stationNum:" + stationNum);
2261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return (new int[] {
2262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                firstValidstation, stationNum
2263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        });
2264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * update DB, keep favorite and rds which is searched this time,
2268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * delete rds from db which is not searched this time.
2269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param stations
2270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return number of valid searched stations
2271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int updateDBInLocation(int[] stations) {
2273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int stationNum = 0;
2274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int searchedListSize = stations.length;
2275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        ArrayList<Integer> stationsInDB = new ArrayList<Integer>();
2276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor cursor = null;
2277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        try {
2278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // get non favorite stations
2279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            cursor = mContext.getContentResolver().query(Station.CONTENT_URI,
2280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    new String[] { FmStation.Station.FREQUENCY },
2281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    FmStation.Station.IS_FAVORITE + "=0",
2282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    null, FmStation.Station.FREQUENCY);
2283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if ((null != cursor) && cursor.moveToFirst()) {
2284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                do {
2286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int freqInDB = cursor.getInt(cursor.getColumnIndex(
2287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            FmStation.Station.FREQUENCY));
2288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stationsInDB.add(freqInDB);
2289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } while (cursor.moveToNext());
2290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else {
2292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Log.d(TAG, "updateDBInLocation, insertSearchedStation cursor is null");
2293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } finally {
2295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (null != cursor) {
2296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                cursor.close();
2297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int listSizeInDB = stationsInDB.size();
2301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // delete station if db frequency is not in searched list
2302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        for (int i = 0; i < listSizeInDB; i++) {
2303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int freqInDB = stationsInDB.get(i);
2304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            for (int j = 0; j < searchedListSize; j++) {
2305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int freqSearched = stations[j];
2306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (freqInDB == freqSearched) {
2307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (j == (searchedListSize - 1) && freqInDB != freqSearched) {
2310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // delete from db
2311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    FmStation.deleteStationInDb(mContext, freqInDB);
2312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // add to db if station is not in db
2317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        for (int j = 0; j < searchedListSize; j++) {
2318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int freqSearched = stations[j];
2319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (FmUtils.isValidStation(freqSearched)) {
2320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                stationNum++;
2321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (!stationsInDB.contains(freqSearched)
2322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        && !FmStation.isFavoriteStation(mContext, freqSearched)) {
2323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // insert to db
2324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    FmStation.insertStationToDb(mContext, freqSearched, "");
2325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
2326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return stationNum;
2329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * The background handler
2333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    class FmRadioServiceHandler extends Handler {
2335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public FmRadioServiceHandler(Looper looper) {
2336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            super(looper);
2337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
2340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void handleMessage(Message msg) {
2341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Bundle bundle;
2342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            boolean isPowerup = false;
2343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            boolean isSwitch = true;
2344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            switch (msg.what) {
2346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // power up
2348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_POWERUP_FINISHED:
2349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    handlePowerUp(bundle);
2351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // power down
2354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_POWERDOWN_FINISHED:
2355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    handlePowerDown();
2356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // fm exit
2359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_FM_EXIT:
2360a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (mIsSpeakerUsed) {
2361a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        setForceUse(false);
2362a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    powerDown();
2364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    closeDevice();
2365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = new Bundle(1);
2367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_FM_EXIT);
2368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    notifyActivityStateChanged(bundle);
2369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // Finish favorite when exit FM
2370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (sExitListener != null) {
2371a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        sExitListener.onExit();
2372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // switch antenna
23766e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang                case FmListener.MSGID_SWITCH_ANTENNA:
2377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
23786e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang                    int value = bundle.getInt(FmListener.SWITCH_ANTENNA_VALUE);
2379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // if ear phone insert, need dismiss plugin earphone
2381a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // dialog
2382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // if earphone plug out and it is not play recorder
2383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // state, show plug dialog.
2384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (0 == value) {
2385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // powerUpAsync(FMRadioUtils.computeFrequency(mCurrentStation));
2386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        bundle.putInt(FmListener.CALLBACK_FLAG,
23876e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang                                FmListener.MSGID_SWITCH_ANTENNA);
23886e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang                        bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, true);
2389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        notifyActivityStateChanged(bundle);
2390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    } else {
2391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // ear phone plug out, and recorder state is not
2392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // play recorder state,
2393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // show dialog.
2394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (mRecordState != FmRecorder.STATE_PLAYBACK) {
2395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            bundle.putInt(FmListener.CALLBACK_FLAG,
23966e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang                                    FmListener.MSGID_SWITCH_ANTENNA);
23976e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang                            bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, false);
2398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            notifyActivityStateChanged(bundle);
2399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
2400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // tune to station
2404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_TUNE_FINISHED:
2405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    float tuneStation = bundle.getFloat(FM_FREQUENCY);
2407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    boolean isTune = tuneStation(tuneStation);
2408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // if tune fail, pass current station to update ui
2409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (!isTune) {
2410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        tuneStation = FmUtils.computeFrequency(mCurrentStation);
2411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = new Bundle(3);
2413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putInt(FmListener.CALLBACK_FLAG,
2414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            FmListener.MSGID_TUNE_FINISHED);
2415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putBoolean(FmListener.KEY_IS_TUNE, isTune);
2416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putFloat(FmListener.KEY_TUNE_TO_STATION, tuneStation);
2417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    notifyActivityStateChanged(bundle);
2418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // seek to station
2421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_SEEK_FINISHED:
2422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mIsSeeking = true;
2424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    float seekStation = seekStation(bundle.getFloat(FM_FREQUENCY),
2425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            bundle.getBoolean(OPTION));
2426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    boolean isStationTunningSuccessed = false;
2427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int station = FmUtils.computeStation(seekStation);
2428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (FmUtils.isValidStation(station)) {
2429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        isStationTunningSuccessed = tuneStation(seekStation);
2430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // if tune fail, pass current station to update ui
2432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (!isStationTunningSuccessed) {
2433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        seekStation = FmUtils.computeFrequency(mCurrentStation);
2434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = new Bundle(2);
2436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putInt(FmListener.CALLBACK_FLAG,
2437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            FmListener.MSGID_TUNE_FINISHED);
2438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putBoolean(FmListener.KEY_IS_TUNE, isStationTunningSuccessed);
2439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putFloat(FmListener.KEY_TUNE_TO_STATION, seekStation);
2440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    notifyActivityStateChanged(bundle);
2441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mIsSeeking = false;
2442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // start scan
2445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_SCAN_FINISHED:
2446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int[] stations = null;
2447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int[] result = null;
2448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int scanTuneStation = 0;
2449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    boolean isScan = true;
2450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mIsScanning = true;
2451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (powerUp(FmUtils.DEFAULT_STATION_FLOAT)) {
2452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        stations = startScan();
2453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // check whether cancel scan
2456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if ((null != stations) && stations[0] == -100) {
2457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        isScan = false;
2458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        result = new int[] {
2459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                                -1, 0
2460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        };
2461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    } else {
2462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        result = updateStations(stations);
2463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        scanTuneStation = result[0];
2464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        tuneStation(FmUtils.computeFrequency(mCurrentStation));
2465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    /*
2468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                     * if there is stop command when scan, so it needs to mute
2469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                     * fm avoid fm sound come out.
2470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                     */
2471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (mIsAudioFocusHeld) {
2472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        setMute(false);
2473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
2474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = new Bundle(4);
2475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putInt(FmListener.CALLBACK_FLAG,
2476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            FmListener.MSGID_SCAN_FINISHED);
2477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    //bundle.putInt(FmListener.KEY_TUNE_TO_STATION, scanTuneStation);
2478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putInt(FmListener.KEY_STATION_NUM, result[1]);
2479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle.putBoolean(FmListener.KEY_IS_SCAN, isScan);
24807f521982b1b9c471af1c927a3a45901bb2cbd791Benson Huang
24817f521982b1b9c471af1c927a3a45901bb2cbd791Benson Huang                    mIsScanning = false;
2482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    // Only notify the newest request activity
2483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    notifyCurrentActivityStateChanged(bundle);
2484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // audio focus changed
2487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_AUDIOFOCUS_CHANGED:
2488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int focusState = bundle.getInt(FmListener.KEY_AUDIOFOCUS_CHANGED);
2490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    updateAudioFocus(focusState);
2491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_SET_RDS_FINISHED:
2494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    setRds(bundle.getBoolean(OPTION));
2496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_SET_MUTE_FINISHED:
2499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    setMute(bundle.getBoolean(OPTION));
2501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_ACTIVE_AF_FINISHED:
2504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    activeAf();
2505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                /********** recording **********/
2508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_STARTRECORDING_FINISHED:
2509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    startRecording();
2510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_STOPRECORDING_FINISHED:
2513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    stopRecording();
2514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_RECORD_MODE_CHANED:
2517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    setRecordingMode(bundle.getBoolean(OPTION));
2519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                case FmListener.MSGID_SAVERECORDING_FINISHED:
2522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    bundle = msg.getData();
2523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    saveRecording(bundle.getString(RECODING_FILE_NAME));
2524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                default:
2527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    break;
2528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * handle power down, execute power down and call back to activity.
2535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void handlePowerDown() {
2537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Bundle bundle;
2538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean isPowerdown = powerDown();
2539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle = new Bundle(1);
2540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERDOWN_FINISHED);
2541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        notifyActivityStateChanged(bundle);
2542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * handle power up, execute power up and call back to activity.
2546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param bundle power up frequency
2548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void handlePowerUp(Bundle bundle) {
2550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean isPowerUp = false;
2551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean isSwitch = true;
2552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        float curFrequency = bundle.getFloat(FM_FREQUENCY);
2553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!isAntennaAvailable()) {
2555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.d(TAG, "handlePowerUp, earphone is not ready");
2556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            bundle = new Bundle(2);
25576e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang            bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_SWITCH_ANTENNA);
25586e47fefd026fe63b8d23dea69034e7f5e4c18f5cBenson Huang            bundle.putBoolean(FmListener.KEY_IS_SWITCH_ANTENNA, false);
2559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            notifyActivityStateChanged(bundle);
2560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return;
2561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (powerUp(curFrequency)) {
2563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (FmUtils.isFirstTimePlayFm(mContext)) {
2564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                isPowerUp = firstPlaying(curFrequency);
2565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                FmUtils.setIsFirstTimePlayFm(mContext);
2566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else {
2567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                isPowerUp = playFrequency(curFrequency);
2568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPausedByTransientLossOfFocus = false;
2570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle = new Bundle(2);
2572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.CALLBACK_FLAG, FmListener.MSGID_POWERUP_FINISHED);
2573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        bundle.putInt(FmListener.KEY_TUNE_TO_STATION, mCurrentStation);
2574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        notifyActivityStateChanged(bundle);
2575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * check FM is foreground or background
2579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean isActivityForeground() {
25813dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang        return (mIsFmMainForeground || mIsFmFavoriteForeground || mIsFmRecordForeground);
2582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * mark FmMainActivity is foreground or not
2586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param isForeground
2587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setFmMainActivityForeground(boolean isForeground) {
2589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsFmMainForeground = isForeground;
2590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * mark FmFavoriteActivity activity is foreground or not
2594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param isForeground
2595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setFmFavoriteForeground(boolean isForeground) {
25973dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang        mIsFmFavoriteForeground = isForeground;
2598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
26014b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang     * mark FmRecordActivity activity is foreground or not
26024b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang     * @param isForeground
26034b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang     */
26044b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang    public void setFmRecordActivityForeground(boolean isForeground) {
26053dfd9a996a7d065a95fcee47e698c96163208e2cBenson Huang        mIsFmRecordForeground = isForeground;
26064b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang    }
26074b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang
26084b523a3c2a376bef6440eb9504a0d33875bad842Hochi Huang    /**
2609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get the recording sdcard path when staring record
2610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return sdcard path like "/storage/sdcard0"
2612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public static String getRecordingSdcard() {
2614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return sRecordingSdcard;
2615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * The listener interface for exit
2619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public interface OnExitListener {
2621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
2622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * When Service finish, should notify FmFavoriteActivity to finish
2623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
2624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        void onExit();
2625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Register the listener for exit
2629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param listener The listener want to know the exit event
2631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public static void registerExitListener(OnExitListener listener) {
2633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        sExitListener = listener;
2634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Unregister the listener for exit
2638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param listener The listener want to know the exit event
2640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public static void unregisterExitListener(OnExitListener listener) {
2642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        sExitListener = null;
2643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get the latest recording name the show name in save dialog but saved in
2647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * service
2648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return The latest recording name or null for not modified
2650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public String getModifiedRecordingName() {
2652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mModifiedRecordingName;
2653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set the latest recording name if modify the default name
2657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
2658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param name The latest recording name or null for not modified
2659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setModifiedRecordingName(String name) {
2661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mModifiedRecordingName = name;
2662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
2665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onTaskRemoved(Intent rootIntent) {
2666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        exitFm();
2667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        super.onTaskRemoved(rootIntent);
2668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean firstPlaying(float frequency) {
2671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPowerStatus != POWER_UP) {
2672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Log.w(TAG, "firstPlaying, FM is not powered up");
2673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
2674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        boolean isSeekTune = false;
2676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        float seekStation = FmNative.seek(frequency, false);
2677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int station = FmUtils.computeStation(seekStation);
2678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (FmUtils.isValidStation(station)) {
2679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            isSeekTune = FmNative.tune(seekStation);
2680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (isSeekTune) {
2681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                playFrequency(seekStation);
2682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
2683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // if tune fail, pass current station to update ui
2685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!isSeekTune) {
2686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            seekStation = FmUtils.computeFrequency(mCurrentStation);
2687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
2688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return isSeekTune;
2689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set the mIsDistanceExceed
2693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param exceed true is exceed, false is not exceed
2694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setDistanceExceed(boolean exceed) {
2696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsDistanceExceed = exceed;
2697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
2699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
2700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set notification class name
2701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param clsName The target class name of activity
2702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
2703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setNotificationClsName(String clsName) {
2704a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mTargetClassName = clsName;
2705a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
2706a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang}
2707