RadioService.java revision 80706bfa3dbf8e5d93012cc001c3d95e7bd66746
144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer/* 244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Copyright (C) 2016 The Android Open Source Project 344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License"); 544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * you may not use this file except in compliance with the License. 644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * You may obtain a copy of the License at 744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * http://www.apache.org/licenses/LICENSE-2.0 944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 1044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Unless required by applicable law or agreed to in writing, software 1144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS, 1244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * See the License for the specific language governing permissions and 1444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * limitations under the License 1544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 1644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 1744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerpackage com.android.car.radio; 1844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 1944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.app.Service; 2044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.content.Context; 2144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.content.Intent; 22fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczykimport android.hardware.radio.ProgramList; 2333ee142f84a813e09a1cdeb72c136c7f89602450Tomasz Wasilczykimport android.hardware.radio.ProgramSelector; 2444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.hardware.radio.RadioManager; 25dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczykimport android.hardware.radio.RadioManager.ProgramInfo; 2644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.hardware.radio.RadioMetadata; 2744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.hardware.radio.RadioTuner; 2844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.media.AudioAttributes; 2944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.media.AudioManager; 303423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczykimport android.os.Bundle; 3144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.Handler; 3244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.IBinder; 3344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.RemoteException; 3444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.SystemProperties; 3544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.annotation.Nullable; 3644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.text.TextUtils; 3744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.util.Log; 383423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczykimport android.support.v4.media.MediaBrowserCompat.MediaItem; 393423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczykimport android.support.v4.media.MediaBrowserServiceCompat; 40f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang 41b1c7c3ce9d0f647e2203f646928dd84edbb75c1cTomasz Wasilczykimport com.android.car.radio.media.Program; 423423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczykimport com.android.car.radio.media.BrowseTree; 433423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczykimport com.android.car.radio.media.TunerSession; 4444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.IRadioCallback; 4544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.IRadioManager; 4689dbd03c73e643523d9ed8b17554f0ab292c1213Tomasz Wasilczykimport com.android.car.radio.platform.ImageMemoryCache; 47dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczykimport com.android.car.radio.platform.ProgramInfoExt; 48b1c7c3ce9d0f647e2203f646928dd84edbb75c1cTomasz Wasilczykimport com.android.car.radio.platform.ProgramSelectorExt; 49e185ab731e91dbb8a1f16b36a58ace2c5593b00dTomasz Wasilczykimport com.android.car.radio.platform.RadioManagerExt; 506c62f1af9fcbf3e7ca00a701266a6185d9ed6d9fTomasz Wasilczykimport com.android.car.radio.platform.RadioTunerExt; 5144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 5244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport java.util.ArrayList; 53b1c7c3ce9d0f647e2203f646928dd84edbb75c1cTomasz Wasilczykimport java.util.HashSet; 5444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport java.util.List; 5544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 5644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer/** 5744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * A persistent {@link Service} that is responsible for opening and closing a {@link RadioTuner}. 5844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * All radio operations should be delegated to this class. To be notified of any changes in radio 5944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * metadata, register as a {@link android.hardware.radio.RadioTuner.Callback} on this Service. 6044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 6144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * <p>Utilize the {@link RadioBinder} to perform radio operations. 6244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 633423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczykpublic class RadioService extends MediaBrowserServiceCompat 643423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk implements AudioManager.OnAudioFocusChangeListener { 653423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk 663423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk private static String TAG = "BcRadioApp.uisrv"; 673423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk 683423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk public static String ACTION_UI_SERVICE = "com.android.car.radio.ACTION_UI_SERVICE"; 6944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 7044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 7144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * The amount of time to wait before re-trying to open the {@link #mRadioTuner}. 7244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 7344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private static final int RADIO_TUNER_REOPEN_DELAY_MS = 5000; 7444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 7519c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk private final Object mLock = new Object(); 7619c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk 7744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int mReOpenRadioTunerCount = 0; 7844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private final Handler mHandler = new Handler(); 7944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 8019c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk private RadioStorage mRadioStorage; 8119c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk private final RadioStorage.PresetsChangeListener mPresetsListener = this::onPresetsChanged; 8219c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk 8344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioTuner mRadioTuner; 8444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 8544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private boolean mRadioSuccessfullyInitialized; 8644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 87dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk private ProgramInfo mCurrentProgram; 88dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk private int mCurrentRadioBand = RadioManager.BAND_FM; 8944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 90e185ab731e91dbb8a1f16b36a58ace2c5593b00dTomasz Wasilczyk private RadioManagerExt mRadioManager; 9189dbd03c73e643523d9ed8b17554f0ab292c1213Tomasz Wasilczyk private ImageMemoryCache mImageCache; 9244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 93f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang private AudioManager mAudioManager; 9444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private AudioAttributes mRadioAudioAttributes; 9544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 963423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk private BrowseTree mBrowseTree; 973423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk private TunerSession mMediaSession; 98fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk private ProgramList mProgramList; 993423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk 10044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 10144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Whether or not this {@link RadioService} currently has audio focus, meaning it is the 10244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * primary driver of media. Usually, interaction with the radio will be prefaced with an 10344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * explicit request for audio focus. However, this is not ideal when muting the radio, so this 10444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * state needs to be tracked. 10544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 10644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private boolean mHasAudioFocus; 10744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 10844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 10944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * An internal {@link android.hardware.radio.RadioTuner.Callback} that will listen for 11044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * changes in radio metadata and pass these method calls through to 11144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link #mRadioTunerCallbacks}. 11244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 11344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioTuner.Callback mInternalRadioTunerCallback = new InternalRadioCallback(); 11444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private List<IRadioCallback> mRadioTunerCallbacks = new ArrayList<>(); 11544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 11644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 11744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public IBinder onBind(Intent intent) { 1183423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk if (ACTION_UI_SERVICE.equals(intent.getAction())) { 1193423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk return mBinder; 12044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 1213423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk return super.onBind(intent); 12244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 12344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 12444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 12544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onCreate() { 12644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer super.onCreate(); 12744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 12844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 12944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onCreate()"); 13044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 13144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 13219c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk mRadioStorage = RadioStorage.getInstance(this); 13319c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk 134f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 135f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang mRadioAudioAttributes = new AudioAttributes.Builder() 136f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang .setUsage(AudioAttributes.USAGE_MEDIA) 137f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) 138f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang .build(); 13944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 14089dbd03c73e643523d9ed8b17554f0ab292c1213Tomasz Wasilczyk mRadioManager = new RadioManagerExt(this); 14189dbd03c73e643523d9ed8b17554f0ab292c1213Tomasz Wasilczyk mImageCache = new ImageMemoryCache(mRadioManager, 1000); 142f3548bc1b56e17934a355a8c1847ac7efbc0d995Tomasz Wasilczyk mBrowseTree = new BrowseTree(this, mImageCache); 14389dbd03c73e643523d9ed8b17554f0ab292c1213Tomasz Wasilczyk mMediaSession = new TunerSession(this, mBrowseTree, mBinder, mImageCache); 1443423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk setSessionToken(mMediaSession.getSessionToken()); 1453423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk 146d768ae7c08f39a365a2be1bbe21e2248b41f60d0Tomasz Wasilczyk mBrowseTree.setAmFmRegionConfig(mRadioManager.getAmFmRegionConfig()); 147d768ae7c08f39a365a2be1bbe21e2248b41f60d0Tomasz Wasilczyk openRadioBandInternal(mCurrentRadioBand); 14819c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk 14919c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk mRadioStorage.addPresetsChangeListener(mPresetsListener); 15019c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk onPresetsChanged(); 15144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 15244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioSuccessfullyInitialized = true; 15344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 15444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 15544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 15644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onDestroy() { 15744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 15844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onDestroy()"); 15944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 16044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16119c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk mRadioStorage.removePresetsChangeListener(mPresetsListener); 1623423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk mMediaSession.release(); 16380706bfa3dbf8e5d93012cc001c3d95e7bd66746Tomasz Wasilczyk mRadioManager.getRadioTunerExt().close(); 16444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer close(); 16544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer super.onDestroy(); 16744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 16844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16919c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk private void onPresetsChanged() { 17019c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk synchronized (mLock) { 17119c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk mBrowseTree.setFavorites(new HashSet<>(mRadioStorage.getPresets())); 1726407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk mMediaSession.notifyFavoritesChanged(); 17319c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk } 17419c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk } 17519c3b08d2481a5c9bf540130632334d4eed73babTomasz Wasilczyk 17644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 17744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Opens the current radio band. Currently, this only supports FM and AM bands. 17844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 17944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @param radioBand One of {@link RadioManager#BAND_FM}, {@link RadioManager#BAND_AM}, 18044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#BAND_FM_HD} or {@link RadioManager#BAND_AM_HD}. 18144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@link RadioManager#STATUS_OK} if successful; otherwise, 18244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#STATUS_ERROR}. 18344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 18444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int openRadioBandInternal(int radioBand) { 18544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 18644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "openRadioBandInternal() audio focus request fail"); 18744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_ERROR; 18844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 18944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 19044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioBand = radioBand; 191e185ab731e91dbb8a1f16b36a58ace2c5593b00dTomasz Wasilczyk if (mRadioTuner == null) { 192e185ab731e91dbb8a1f16b36a58ace2c5593b00dTomasz Wasilczyk mRadioTuner = mRadioManager.openSession(mInternalRadioTunerCallback, null); 193fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk mProgramList = mRadioTuner.getDynamicProgramList(null); 194fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk mBrowseTree.setProgramList(mProgramList); 19544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 19644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 19744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 19844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "openRadioBandInternal() STATUS_OK"); 19944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 20044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 20144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Reset the counter for exponential backoff each time the radio tuner has been successfully 20244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // opened. 20344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mReOpenRadioTunerCount = 0; 20444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 20544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_OK; 20644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 20744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 20844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int requestAudioFocus() { 209f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang int status = mAudioManager.requestAudioFocus(this, mRadioAudioAttributes, 21044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer AudioManager.AUDIOFOCUS_GAIN, 0); 21144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 21244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 21344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "requestAudioFocus status: " + status); 21444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 21544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 21644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 21744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = true; 21844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 2196c62f1af9fcbf3e7ca00a701266a6185d9ed6d9fTomasz Wasilczyk mRadioManager.getRadioTunerExt().setMuted(false); 2206c62f1af9fcbf3e7ca00a701266a6185d9ed6d9fTomasz Wasilczyk 22144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 22244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 22344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioMuteChanged(false); 22444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 22544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "requestAudioFocus(); onRadioMuteChanged() notify failed: " 22644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + e.getMessage()); 22744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 22844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 22944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 23044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 23144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return status; 23244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 23344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 23444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void abandonAudioFocus() { 23544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 23644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "abandonAudioFocus()"); 23744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 23844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 239f334cefbfabd28cb294a378778c00f1975a00deeHongwei Wang mAudioManager.abandonAudioFocus(this, mRadioAudioAttributes); 24044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = false; 24144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 24244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 24344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 24444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioMuteChanged(true); 24544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 24644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "abandonAudioFocus(); onRadioMutechanged() notify failed: " 24744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + e.getMessage()); 24844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 24944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 25044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 25144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 25244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 25344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Closes any active {@link RadioTuner}s and releases audio focus. 25444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 25544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void close() { 25644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 25744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "close()"); 25844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 25944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 26044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer abandonAudioFocus(); 26144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 262fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk if (mProgramList != null) { 263fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk mProgramList.close(); 264fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk mProgramList = null; 265fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk } 26644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner != null) { 26744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.close(); 26844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner = null; 26944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 27044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 27144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 27244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 27344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onAudioFocusChange(int focusChange) { 27444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 27544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "focus change: " + focusChange); 27644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 27744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 27844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer switch (focusChange) { 27944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_GAIN: 28044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = true; 28144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer openRadioBandInternal(mCurrentRadioBand); 28244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 28344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 28444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // For a transient loss, just allow the focus to be released. The radio will stop 28544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // itself automatically. There is no need for an explicit abandon audio focus call 28644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // because this removes the AudioFocusChangeListener. 28744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 28844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 28944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = false; 29044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 29144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 29244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS: 29344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer close(); 29444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 29544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 29644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer default: 29744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Do nothing for all other cases. 29844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 29944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 30044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 30144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private IRadioManager.Stub mBinder = new IRadioManager.Stub() { 30244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 30344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Tunes the radio to the given frequency. To be notified of a successful tune, register 30444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * as a {@link android.hardware.radio.RadioTuner.Callback}. 30544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 30644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 30733ee142f84a813e09a1cdeb72c136c7f89602450Tomasz Wasilczyk public void tune(ProgramSelector sel) { 30833ee142f84a813e09a1cdeb72c136c7f89602450Tomasz Wasilczyk if (mRadioManager == null || sel == null 30944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 31044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 31144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 31333ee142f84a813e09a1cdeb72c136c7f89602450Tomasz Wasilczyk mRadioTuner.tune(sel); 31444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 31644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 31744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Seeks the radio forward. To be notified of a successful tune, register as a 31844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link android.hardware.radio.RadioTuner.Callback}. 31944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 32044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 32144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void seekForward() { 32244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null 32344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 32444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 32544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 32644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 32744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner == null) { 32844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int radioStatus = openRadioBandInternal(mCurrentRadioBand); 32944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (radioStatus == RadioManager.STATUS_ERROR) { 33044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 33144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 33244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 33344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 33444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.scan(RadioTuner.DIRECTION_UP, true); 33544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 33644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 33744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 33844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Seeks the radio backwards. To be notified of a successful tune, register as a 33944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link android.hardware.radio.RadioTuner.Callback}. 34044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 34144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 34244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void seekBackward() { 34344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null 34444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 34544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 34644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 34744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 34844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner == null) { 34944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int radioStatus = openRadioBandInternal(mCurrentRadioBand); 35044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (radioStatus == RadioManager.STATUS_ERROR) { 35144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 35244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 35544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, true); 35644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 3586baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk private boolean setMuted(boolean mute) { 35980706bfa3dbf8e5d93012cc001c3d95e7bd66746Tomasz Wasilczyk if (!mRadioManager.getRadioTunerExt().setMuted(mute)) return false; 36044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 3616baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk for (IRadioCallback callback : mRadioTunerCallbacks) { 3626baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk try { 3636baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk callback.onRadioMuteChanged(mute); 3646baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk } catch (RemoteException e) { 3656c62f1af9fcbf3e7ca00a701266a6185d9ed6d9fTomasz Wasilczyk Log.e(TAG, "onRadioMuteChanged callback failed", e); 36644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 36744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 36844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 3696baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk return true; 3706baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk } 37144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 3726baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk /** 3736baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk * Mutes the radio. 3746baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk * 3756baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk * @return {@code true} if the mute was successful. 3766baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk */ 3776baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk @Override 3786baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk public boolean mute() { 3796baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk return setMuted(true); 38044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 38144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 38244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 38344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Un-mutes the radio and causes audio to play. 38444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 38544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@code true} if the un-mute was successful. 38644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 38744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 38844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean unMute() { 3896baa9b3004a4a68c41206db235639dc3806be282Tomasz Wasilczyk if (!setMuted(false)) return false; 39044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 39144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return requestAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 39244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 39344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 39444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 39544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns {@code true} if the radio is currently muted. 39644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 39744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 39844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean isMuted() { 39980706bfa3dbf8e5d93012cc001c3d95e7bd66746Tomasz Wasilczyk return mRadioManager.getRadioTunerExt().isMuted(); 40044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 40144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 4026407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk @Override 4036407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk public void addFavorite(Program program) { 4046407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk mRadioStorage.storePreset(program); 4056407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk } 4066407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk 4076407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk @Override 4086407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk public void removeFavorite(ProgramSelector sel) { 4096407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk mRadioStorage.removePreset(sel); 4106407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk } 4116407b74e0ac4968f84463b2b9b9762d7fda68daeTomasz Wasilczyk 41244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 41344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Opens the radio for the given band. 41444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 41544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @param radioBand One of {@link RadioManager#BAND_FM}, {@link RadioManager#BAND_AM}, 41644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#BAND_FM_HD} or {@link RadioManager#BAND_AM_HD}. 41744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@link RadioManager#STATUS_OK} if successful; otherwise, 41844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#STATUS_ERROR}. 41944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 42044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 42144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public int openRadioBand(int radioBand) { 42244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 42344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "openRadioBand() for band: " + radioBand); 42444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 42544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 42644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null) { 42744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_ERROR; 42844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 42944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 43044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return openRadioBandInternal(radioBand); 43144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 43244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 43344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 43444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Adds the given {@link android.hardware.radio.RadioTuner.Callback} to be notified 43544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * of any radio metadata changes. 43644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 43744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 43844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void addRadioTunerCallback(IRadioCallback callback) { 43944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (callback == null) { 44044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 44144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 44244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 44344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTunerCallbacks.add(callback); 44444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 44544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 44644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 44744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Removes the given {@link android.hardware.radio.RadioTuner.Callback} from receiving 44844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * any radio metadata chagnes. 44944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 45044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 45144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void removeRadioTunerCallback(IRadioCallback callback) { 45244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (callback == null) { 45344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 45444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 45544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 45644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTunerCallbacks.remove(callback); 45744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 45844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 45944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 460dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk public ProgramInfo getCurrentProgramInfo() { 461dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk return mCurrentProgram; 46244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 46344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 46444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 46544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns {@code true} if the radio was able to successfully initialize. A value of 46644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@code false} here could mean that the {@code RadioService} was not able to connect to 46744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * the {@link RadioManager} or there were no radio modules on the current device. 46844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 46944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 47044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean isInitialized() { 47144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mRadioSuccessfullyInitialized; 47244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 47344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 47444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 47544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns {@code true} if the radio currently has focus and is therefore the application 47644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * that is supplying music. 47744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 47844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 47944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean hasFocus() { 48044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mHasAudioFocus; 48144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 48244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer }; 48344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 48444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 48544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * A extension of {@link android.hardware.radio.RadioTuner.Callback} that delegates to a 48644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * callback registered on this service. 48744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 48844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private class InternalRadioCallback extends RadioTuner.Callback { 48944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 490dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk public void onProgramInfoChanged(ProgramInfo info) { 49144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 492dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk Log.d(TAG, "Program info changed: " + info); 49344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 49444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 495dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk mCurrentProgram = info; 496dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk mMediaSession.notifyProgramInfoChanged(info); 49744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 498dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk for (IRadioCallback callback : mRadioTunerCallbacks) { 499dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk try { 500dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk callback.onCurrentProgramInfoChanged(info); 501dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk } catch (RemoteException e) { 502dc70a5cad0f5b3586a3ff2e94e902f7f6c45b5e8Tomasz Wasilczyk Log.e(TAG, "Failed to notify about changed radio station", e); 50344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 50444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 50544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 50644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 50744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 50844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onConfigurationChanged(RadioManager.BandConfig config) { 50944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 51044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onConfigurationChanged(): config: " + config); 51144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 51244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 51344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (config != null) { 51444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioBand = config.getType(); 51544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 51644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 51744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onConfigurationChanged(): config type: " + mCurrentRadioBand); 51844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 51944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 52044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 52144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 52244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 52344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 52444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioBandChanged(mCurrentRadioBand); 52544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 52644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 52744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onConfigurationChanged(); " 52844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + "Failed to notify IRadioCallbacks: " + e.getMessage()); 52944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 53044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 53144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 53244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 53344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onError(int status) { 53444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onError(); status: " + status); 53544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 53644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // If there is a hardware failure or the radio service died, then this requires a 53744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // re-opening of the radio tuner. 53844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (status == RadioTuner.ERROR_HARDWARE_FAILURE 53944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || status == RadioTuner.ERROR_SERVER_DIED) { 540fa786fd40b477e8175298740451fb15e2623d929Tomasz Wasilczyk close(); 54144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 54244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Attempt to re-open the RadioTuner. Each time the radio tuner fails to open, the 54344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // mReOpenRadioTunerCount will be incremented. 54444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHandler.removeCallbacks(mOpenRadioTunerRunnable); 54544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHandler.postDelayed(mOpenRadioTunerRunnable, 54644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mReOpenRadioTunerCount * RADIO_TUNER_REOPEN_DELAY_MS); 54744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 54844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mReOpenRadioTunerCount++; 54944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 55044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 55144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 55244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 55344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onError(status); 55444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 55544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 55644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onError(); Failed to notify IRadioCallbacks: " + e.getMessage()); 55744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 55844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 55944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 56044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 56144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onControlChanged(boolean control) { 56244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // If the radio loses control of the RadioTuner, then close it and allow it to be 56344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // re-opened when control has been gained. 56444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!control) { 56544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer close(); 56644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 56744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 56844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 56944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner == null) { 57044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer openRadioBandInternal(mCurrentRadioBand); 57144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 57244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 57344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 57444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 57544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private final Runnable mOpenRadioTunerRunnable = () -> openRadioBandInternal(mCurrentRadioBand); 5763423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk 5773423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk @Override 5783423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { 57978605bcf875f767a6a68f66cbedbd73eef8aafb2Tomasz Wasilczyk // TODO(b/75970985): check permissions, if necessary 58078605bcf875f767a6a68f66cbedbd73eef8aafb2Tomasz Wasilczyk return mBrowseTree.getRoot(); 5813423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk } 5823423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk 5833423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk @Override 5843423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) { 5853423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk mBrowseTree.loadChildren(parentMediaId, result); 5863423d7204260b1b5707d103956b6776bf46c996aTomasz Wasilczyk } 58744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer} 588