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.car.hardware.radio.CarRadioManager; 2144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.content.Context; 2244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.content.Intent; 2344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.content.pm.PackageManager; 2444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.hardware.radio.RadioManager; 2544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.hardware.radio.RadioMetadata; 2644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.hardware.radio.RadioTuner; 2744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.media.AudioAttributes; 2844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.media.AudioManager; 2944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.Handler; 3044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.IBinder; 3144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.RemoteException; 3244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.SystemProperties; 3344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.annotation.Nullable; 3444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.car.Car; 3544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.car.CarNotConnectedException; 369dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrupimport android.support.car.CarConnectionCallback; 3744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.car.media.CarAudioManager; 3844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.text.TextUtils; 3944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.util.Log; 4044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.demo.RadioDemo; 4144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.IRadioCallback; 4244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.IRadioManager; 4344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.RadioRds; 4444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.RadioStation; 4544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 4644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport java.util.ArrayList; 4744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport java.util.List; 4844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 4944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer/** 5044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * A persistent {@link Service} that is responsible for opening and closing a {@link RadioTuner}. 5144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * All radio operations should be delegated to this class. To be notified of any changes in radio 5244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * metadata, register as a {@link android.hardware.radio.RadioTuner.Callback} on this Service. 5344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 5444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * <p>Utilize the {@link RadioBinder} to perform radio operations. 5544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 5644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerpublic class RadioService extends Service implements AudioManager.OnAudioFocusChangeListener { 5744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private static String TAG = "Em.RadioService"; 5844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 5944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 6044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * The amount of time to wait before re-trying to open the {@link #mRadioTuner}. 6144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 6244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private static final int RADIO_TUNER_REOPEN_DELAY_MS = 5000; 6344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 6444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int mReOpenRadioTunerCount = 0; 6544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private final Handler mHandler = new Handler(); 6644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 6744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private Car mCarApi; 6844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioTuner mRadioTuner; 6944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 7044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private boolean mRadioSuccessfullyInitialized; 7144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int mCurrentRadioBand = RadioManager.BAND_FM; 7244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int mCurrentRadioChannel = RadioStorage.INVALID_RADIO_CHANNEL; 7344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 7444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private String mCurrentChannelInfo; 7544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private String mCurrentArtist; 7644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private String mCurrentSongTitle; 7744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 7844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioManager mRadioManager; 7944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioBackgroundScanner mBackgroundScanner; 8044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioManager.FmBandDescriptor mFmDescriptor; 8144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioManager.AmBandDescriptor mAmDescriptor; 8244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 8344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioManager.FmBandConfig mFmConfig; 8444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioManager.AmBandConfig mAmConfig; 8544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 8644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private final List<RadioManager.ModuleProperties> mModules = new ArrayList<>(); 8744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 8844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private CarAudioManager mCarAudioManager; 8944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private AudioAttributes mRadioAudioAttributes; 9044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 9144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 9244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Whether or not this {@link RadioService} currently has audio focus, meaning it is the 9344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * primary driver of media. Usually, interaction with the radio will be prefaced with an 9444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * explicit request for audio focus. However, this is not ideal when muting the radio, so this 9544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * state needs to be tracked. 9644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 9744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private boolean mHasAudioFocus; 9844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 9944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 10044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * An internal {@link android.hardware.radio.RadioTuner.Callback} that will listen for 10144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * changes in radio metadata and pass these method calls through to 10244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link #mRadioTunerCallbacks}. 10344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 10444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioTuner.Callback mInternalRadioTunerCallback = new InternalRadioCallback(); 10544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private List<IRadioCallback> mRadioTunerCallbacks = new ArrayList<>(); 10644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 10744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 10844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public IBinder onBind(Intent intent) { 10944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 11044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onBind(); Intent: " + intent); 11144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 11244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mBinder; 11344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 11444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 11544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 11644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onCreate() { 11744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer super.onCreate(); 11844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 11944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 12044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onCreate()"); 12144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 12244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 12344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Connection to car services does not work for non-automotive yet, so this call needs to 12444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // be guarded. 12544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { 1269dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup mCarApi = Car.createCar(this /* context */, mCarConnectionCallback); 12744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCarApi.connect(); 12844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 12944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 13044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (SystemProperties.getBoolean(RadioDemo.DEMO_MODE_PROPERTY, false)) { 13144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer initializeDemo(); 13244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } else { 13344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer initialze(); 13444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 13544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 13644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 13744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 13844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Initializes this service to use a demo {@link IRadioManager}. 13944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 14035dbc15d818fc64e8af86e40e38d4119fea1dd15Anthony Chen * @see RadioDemo 14144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 14244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void initializeDemo() { 14344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 14444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "initializeDemo()"); 14544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 14644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 14744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mBinder = RadioDemo.getInstance(this /* context */).createDemoManager(); 14844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 14944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 15044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 15144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Connects to the {@link RadioManager}. 15244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 15344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void initialze() { 15444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioManager = (RadioManager) getSystemService(Context.RADIO_SERVICE); 15544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 15644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 15744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "initialze(); mRadioManager: " + mRadioManager); 15844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 15944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null) { 16144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.w(TAG, "RadioManager could not be loaded."); 16244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 16344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 16444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int status = mRadioManager.listModules(mModules); 16644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (status != RadioManager.STATUS_OK) { 16744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.w(TAG, "Load modules failed with status: " + status); 16844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 16944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 17044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 17144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 17244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "initialze(); listModules complete: " + mModules); 17344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 17444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 17544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mModules.size() == 0) { 17644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.w(TAG, "No radio modules on device."); 17744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 17844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 17944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 18044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG); 18144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 18244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Load the possible radio bands. For now, just accept FM and AM bands. 18344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (RadioManager.BandDescriptor band : mModules.get(0).getBands()) { 18444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (isDebugLoggable) { 18544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "loading band: " + band.toString()); 18644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 18744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 1888414397997221ece54d8e3a61ba770c17c6ca688Tomasz Wasilczyk if (mFmDescriptor == null && band.isFmBand()) { 18944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mFmDescriptor = (RadioManager.FmBandDescriptor) band; 19044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 19144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 1928414397997221ece54d8e3a61ba770c17c6ca688Tomasz Wasilczyk if (mAmDescriptor == null && band.isAmBand()) { 19344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mAmDescriptor = (RadioManager.AmBandDescriptor) band; 19444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 19544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 19644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 19744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mFmDescriptor == null && mAmDescriptor == null) { 19844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.w(TAG, "No AM and FM radio bands could be loaded."); 19944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 20044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 20144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 20244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // TODO: Make stereo configurable depending on device. 20344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mFmConfig = new RadioManager.FmBandConfig.Builder(mFmDescriptor) 20444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer .setStereo(true) 20544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer .build(); 20644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mAmConfig = new RadioManager.AmBandConfig.Builder(mAmDescriptor) 20744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer .setStereo(true) 20844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer .build(); 20944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 21044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // If there is a second tuner on the device, then set it up as the background scanner. 2118414397997221ece54d8e3a61ba770c17c6ca688Tomasz Wasilczyk // TODO(b/63101896): we don't know if the second tuner is for the same medium, so we don't 2128414397997221ece54d8e3a61ba770c17c6ca688Tomasz Wasilczyk // set background scanner for now. 21344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 21444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioSuccessfullyInitialized = true; 21544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 21644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 21744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 21844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onDestroy() { 21944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 22044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onDestroy()"); 22144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 22244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 22344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer close(); 22444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 22544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarApi != null) { 22644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCarApi.disconnect(); 22744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 22844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 22944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer super.onDestroy(); 23044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 23144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 23244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 23344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Opens the current radio band. Currently, this only supports FM and AM bands. 23444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 23544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @param radioBand One of {@link RadioManager#BAND_FM}, {@link RadioManager#BAND_AM}, 23644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#BAND_FM_HD} or {@link RadioManager#BAND_AM_HD}. 23744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@link RadioManager#STATUS_OK} if successful; otherwise, 23844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#STATUS_ERROR}. 23944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 24044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int openRadioBandInternal(int radioBand) { 24144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 24244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "openRadioBandInternal() audio focus request fail"); 24344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_ERROR; 24444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 24544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 24644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioBand = radioBand; 24744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer RadioManager.BandConfig config = getRadioConfig(radioBand); 24844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 24944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (config == null) { 25044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.w(TAG, "Cannot create config for radio band: " + radioBand); 25144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_ERROR; 25244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 25344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 25444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner != null) { 25544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.setConfiguration(config); 25644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } else { 25744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner = mRadioManager.openTuner(mModules.get(0).getId(), config, true, 25844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mInternalRadioTunerCallback, null /* handler */); 25944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 26044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 26144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 26244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "openRadioBandInternal() STATUS_OK"); 26344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 26444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 26544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mBackgroundScanner != null) { 26644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mBackgroundScanner.onRadioBandChanged(radioBand); 26744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 26844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 26944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Reset the counter for exponential backoff each time the radio tuner has been successfully 27044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // opened. 27144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mReOpenRadioTunerCount = 0; 27244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 27344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_OK; 27444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 27544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 27644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 27744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns a {@link RadioRds} object that holds all the current radio metadata. If all the 27844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * metadata is empty, then {@code null} is returned. 27944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 28044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Nullable 28144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioRds createCurrentRadioRds() { 28244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (TextUtils.isEmpty(mCurrentChannelInfo) && TextUtils.isEmpty(mCurrentArtist) 28344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer && TextUtils.isEmpty(mCurrentSongTitle)) { 28444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return null; 28544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 28644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 28744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return new RadioRds(mCurrentChannelInfo, mCurrentArtist, mCurrentSongTitle); 28844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 28944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 29044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 29144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Creates a {@link RadioStation} that encapsulates all the information about the current 29244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * radio station. 29344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 29444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioStation createCurrentRadioStation() { 29544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // mCurrentRadioChannel can possibly be invalid if this class never receives a callback 29644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // for onProgramInfoChanged(). As a result, manually retrieve the information for the 29744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // current station from RadioTuner if this is the case. 29844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCurrentRadioChannel == RadioStorage.INVALID_RADIO_CHANNEL && mRadioTuner != null) { 29944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 30044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "createCurrentRadioStation(); invalid current radio channel. " 30144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + "Calling getProgramInformation for valid station"); 30244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 30344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 30444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // getProgramInformation() expects an array of size 1. 30544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer RadioManager.ProgramInfo[] info = new RadioManager.ProgramInfo[1]; 30644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int status = mRadioTuner.getProgramInformation(info); 30744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 30844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 30944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "getProgramInformation() status: " + status + "; info: " + info[0]); 31044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 31244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (status == RadioManager.STATUS_OK && info[0] != null) { 31344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioChannel = info[0].getChannel(); 31444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 31544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 31644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "program info channel: " + mCurrentRadioChannel); 31744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 32044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 32144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return new RadioStation(mCurrentRadioChannel, 0 /* subChannelNumber */, 32244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioBand, createCurrentRadioRds()); 32344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 32444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 32544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 32644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns the proper {@link android.hardware.radio.RadioManager.BandConfig} for the given 32744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * radio band. {@code null} is returned if the band is not suppored. 32844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 32944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Nullable 33044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioManager.BandConfig getRadioConfig(int selectedRadioBand) { 33144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer switch (selectedRadioBand) { 33244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case RadioManager.BAND_AM: 3338414397997221ece54d8e3a61ba770c17c6ca688Tomasz Wasilczyk case RadioManager.BAND_AM_HD: 33444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mAmConfig; 33544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case RadioManager.BAND_FM: 3368414397997221ece54d8e3a61ba770c17c6ca688Tomasz Wasilczyk case RadioManager.BAND_FM_HD: 33744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mFmConfig; 33844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 33944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer default: 34044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return null; 34144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 34244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 34344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 34444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int requestAudioFocus() { 34544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int status = AudioManager.AUDIOFOCUS_REQUEST_FAILED; 34644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 34744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer status = mCarAudioManager.requestAudioFocus(this, mRadioAudioAttributes, 34844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer AudioManager.AUDIOFOCUS_GAIN, 0); 34944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (CarNotConnectedException e) { 35044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "requestAudioFocus() failed", e); 35144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 35344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 35444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "requestAudioFocus status: " + status); 35544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 35744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 35844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = true; 35944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 36044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Receiving audio focus means that the radio is un-muted. 36144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 36244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 36344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioMuteChanged(false); 36444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 36544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "requestAudioFocus(); onRadioMuteChanged() notify failed: " 36644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + e.getMessage()); 36744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 36844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 36944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 37044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 37144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return status; 37244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 37344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 37444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void abandonAudioFocus() { 37544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 37644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "abandonAudioFocus()"); 37744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 37844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 37944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 38044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 38144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 38244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 38344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCarAudioManager.abandonAudioFocus(this, mRadioAudioAttributes); 38444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = false; 38544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 38644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 38744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 38844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioMuteChanged(true); 38944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 39044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "abandonAudioFocus(); onRadioMutechanged() notify failed: " 39144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + e.getMessage()); 39244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 39344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 39444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 39544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 39644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 39744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Closes any active {@link RadioTuner}s and releases audio focus. 39844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 39944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void close() { 40044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 40144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "close()"); 40244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 40344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 40444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer abandonAudioFocus(); 40544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 40644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner != null) { 40744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.close(); 40844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner = null; 40944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 41044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 41144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 41244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 41344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onAudioFocusChange(int focusChange) { 41444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 41544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "focus change: " + focusChange); 41644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 41744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 41844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer switch (focusChange) { 41944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_GAIN: 42044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = true; 42144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer openRadioBandInternal(mCurrentRadioBand); 42244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 42344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 42444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // For a transient loss, just allow the focus to be released. The radio will stop 42544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // itself automatically. There is no need for an explicit abandon audio focus call 42644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // because this removes the AudioFocusChangeListener. 42744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 42844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 42944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = false; 43044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 43144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 43244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS: 43344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer close(); 43444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 43544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 43644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer default: 43744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Do nothing for all other cases. 43844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 43944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 44044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 44144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 4429dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup * {@link CarConnectionCallback} that retrieves the {@link CarRadioManager}. 44344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 4449dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup private final CarConnectionCallback mCarConnectionCallback = 4459dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup new CarConnectionCallback() { 44644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 4479dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup public void onConnected(Car car) { 44844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 44944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "Car service connected."); 45044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 45144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 45244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // The CarAudioManager only needs to be retrieved once. 45344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 45444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCarAudioManager = (CarAudioManager) mCarApi.getCarManager( 45544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer android.car.Car.AUDIO_SERVICE); 45644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 45744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioAudioAttributes = mCarAudioManager.getAudioAttributesForCarUsage( 45844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer CarAudioManager.CAR_AUDIO_USAGE_RADIO); 45944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 46044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (CarNotConnectedException e) { 46144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer //TODO finish 46244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "Car not connected"); 46344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 46444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 46544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 46644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 4679dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup public void onDisconnected(Car car) { 46844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 46944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "Car service disconnected."); 47044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 47144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 47244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer }; 47344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 47444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private IRadioManager.Stub mBinder = new IRadioManager.Stub() { 47544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 47644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Tunes the radio to the given frequency. To be notified of a successful tune, register 47744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * as a {@link android.hardware.radio.RadioTuner.Callback}. 47844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 47944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 48044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void tune(RadioStation radioStation) { 48144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null || radioStation == null 48244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 48344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 48444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 48544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 48644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner == null || radioStation.getRadioBand() != mCurrentRadioBand) { 48744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int radioStatus = openRadioBandInternal(radioStation.getRadioBand()); 48844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (radioStatus == RadioManager.STATUS_ERROR) { 48944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 49044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 49144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 49244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 49344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int status = mRadioTuner.tune(radioStation.getChannelNumber(), 0 /* subChannel */); 49444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 49544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 49644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "Tuning to station: " + radioStation + "\n\tstatus: " + status); 49744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 49844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 49944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 50044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 50144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Seeks the radio forward. To be notified of a successful tune, register as a 50244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link android.hardware.radio.RadioTuner.Callback}. 50344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 50444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 50544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void seekForward() { 50644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null 50744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 50844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 50944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 51044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 51144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner == null) { 51244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int radioStatus = openRadioBandInternal(mCurrentRadioBand); 51344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (radioStatus == RadioManager.STATUS_ERROR) { 51444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 51544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 51644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 51744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 51844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.scan(RadioTuner.DIRECTION_UP, true); 51944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 52044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 52144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 52244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Seeks the radio backwards. To be notified of a successful tune, register as a 52344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link android.hardware.radio.RadioTuner.Callback}. 52444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 52544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 52644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void seekBackward() { 52744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null 52844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 52944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 53044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 53144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 53244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner == null) { 53344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int radioStatus = openRadioBandInternal(mCurrentRadioBand); 53444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (radioStatus == RadioManager.STATUS_ERROR) { 53544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 53644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 53744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 53844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 53944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, true); 54044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 54144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 54244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 54344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Mutes the radio. 54444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 54544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@code true} if the mute was successful. 54644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 54744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 54844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean mute() { 54944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null) { 55044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return false; 55144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 55244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 55344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 55444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 55544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "mute() called, but not connected to CarAudioManager"); 55644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 55744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return false; 55844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 55944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 56044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // If the radio does not currently have focus, then no need to do anything because the 56144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // radio won't be playing any sound. 56244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!mHasAudioFocus) { 56344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 56444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "mute() called, but radio does not currently have audio focus; " 56544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + "ignoring."); 56644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 56744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return false; 56844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 56944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 57044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer boolean muteSuccessful = false; 57144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 57244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 57344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer muteSuccessful = mCarAudioManager.setMediaMute(true); 57444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 57544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 57644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "setMediaMute(true) status: " + muteSuccessful); 57744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 57844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (CarNotConnectedException e) { 57944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "mute() failed: " + e.getMessage()); 58044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer e.printStackTrace(); 58144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 58244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 58344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (muteSuccessful && mRadioTunerCallbacks.size() > 0) { 58444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 58544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 58644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioMuteChanged(true); 58744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 58844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "mute() notify failed: " + e.getMessage()); 58944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 59044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 59144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 59244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 59344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return muteSuccessful; 59444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 59544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 59644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 59744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Un-mutes the radio and causes audio to play. 59844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 59944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@code true} if the un-mute was successful. 60044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 60144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 60244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean unMute() { 60344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null) { 60444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return false; 60544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 60644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 60744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 60844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 60944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "toggleMute() called, but not connected to CarAudioManager"); 61044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 61144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return false; 61244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 61344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 61444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Requesting audio focus will automatically un-mute the radio if it had been muted. 61544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return requestAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 61644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 61744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 61844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 61944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns {@code true} if the radio is currently muted. 62044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 62144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 62244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean isMuted() { 62344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!mHasAudioFocus) { 62444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return true; 62544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 62644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 62744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null) { 62844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return true; 62944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 63044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 63144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 63244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 63344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "isMuted() called, but not connected to CarAudioManager"); 63444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 63544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return true; 63644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 63744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 63844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer boolean isMuted = false; 63944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 64044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 64144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer isMuted = mCarAudioManager.isMediaMuted(); 64244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (CarNotConnectedException e) { 64344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "isMuted() failed: " + e.getMessage()); 64444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer e.printStackTrace(); 64544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 64644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 64744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return isMuted; 64844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 64944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 65044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 65144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Opens the radio for the given band. 65244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 65344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @param radioBand One of {@link RadioManager#BAND_FM}, {@link RadioManager#BAND_AM}, 65444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#BAND_FM_HD} or {@link RadioManager#BAND_AM_HD}. 65544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@link RadioManager#STATUS_OK} if successful; otherwise, 65644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioManager#STATUS_ERROR}. 65744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 65844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 65944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public int openRadioBand(int radioBand) { 66044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 66144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "openRadioBand() for band: " + radioBand); 66244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 66344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 66444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioManager == null) { 66544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_ERROR; 66644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 66744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 66844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return openRadioBandInternal(radioBand); 66944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 67044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 67144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 67244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Adds the given {@link android.hardware.radio.RadioTuner.Callback} to be notified 67344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * of any radio metadata changes. 67444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 67544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 67644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void addRadioTunerCallback(IRadioCallback callback) { 67744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (callback == null) { 67844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 67944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 68044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 68144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTunerCallbacks.add(callback); 68244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 68344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 68444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 68544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Removes the given {@link android.hardware.radio.RadioTuner.Callback} from receiving 68644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * any radio metadata chagnes. 68744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 68844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 68944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void removeRadioTunerCallback(IRadioCallback callback) { 69044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (callback == null) { 69144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 69244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 69344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 69444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTunerCallbacks.remove(callback); 69544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 69644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 69744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 69844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns a {@link RadioStation} that encapsulates the information about the current 69944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * station the radio is tuned to. 70044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 70144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 70244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public RadioStation getCurrentRadioStation() { 70344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return createCurrentRadioStation(); 70444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 70544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 70644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 70744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns {@code true} if the radio was able to successfully initialize. A value of 70844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@code false} here could mean that the {@code RadioService} was not able to connect to 70944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * the {@link RadioManager} or there were no radio modules on the current device. 71044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 71144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 71244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean isInitialized() { 71344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mRadioSuccessfullyInitialized; 71444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 71544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 71644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 71744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns {@code true} if the radio currently has focus and is therefore the application 71844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * that is supplying music. 71944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 72044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 72144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean hasFocus() { 72244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mHasAudioFocus; 72344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 72444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 72544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 72644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns {@code true} if the current radio module has dual tuners, meaning that a tuner 72744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * is available to scan for stations in the background. 72844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 72944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 73044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean hasDualTuners() { 73144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mModules.size() >= 2; 73244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 73344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer }; 73444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 73544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 73644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * A extension of {@link android.hardware.radio.RadioTuner.Callback} that delegates to a 73744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * callback registered on this service. 73844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 73944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private class InternalRadioCallback extends RadioTuner.Callback { 74044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 74144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onProgramInfoChanged(RadioManager.ProgramInfo info) { 74244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 74344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onProgramInfoChanged(); info: " + info); 74444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 74544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 74644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer clearMetadata(); 74744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 74844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (info != null) { 74944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioChannel = info.getChannel(); 75044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 75144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 75244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onProgramInfoChanged(); info channel: " + mCurrentRadioChannel); 75344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 75444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 75544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 75644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer RadioStation station = createCurrentRadioStation(); 75744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 75844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 75944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 76044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioStationChanged(station); 76144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 76244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 76344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onProgramInfoChanged(); " 76444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + "Failed to notify IRadioCallbacks: " + e.getMessage()); 76544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 76644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 76744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 76844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 76944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onMetadataChanged(RadioMetadata metadata) { 77044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 77144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onMetadataChanged(); metadata: " + metadata); 77244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 77344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 77444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer clearMetadata(); 77544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer updateMetadata(metadata); 77644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 77744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer RadioRds radioRds = createCurrentRadioRds(); 77844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 77944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 78044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 78144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioMetadataChanged(radioRds); 78244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 78344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 78444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onMetadataChanged(); " 78544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + "Failed to notify IRadioCallbacks: " + e.getMessage()); 78644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 78744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 78844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 78944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 79044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onConfigurationChanged(RadioManager.BandConfig config) { 79144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 79244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onConfigurationChanged(): config: " + config); 79344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 79444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 79544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer clearMetadata(); 79644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 79744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (config != null) { 79844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioBand = config.getType(); 79944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 80044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 80144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "onConfigurationChanged(): config type: " + mCurrentRadioBand); 80244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 80344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 80444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 80544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 80644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 80744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 80844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioBandChanged(mCurrentRadioBand); 80944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 81044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 81144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onConfigurationChanged(); " 81244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + "Failed to notify IRadioCallbacks: " + e.getMessage()); 81344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 81444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 81544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 81644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 81744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onError(int status) { 81844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onError(); status: " + status); 81944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 82044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // If there is a hardware failure or the radio service died, then this requires a 82144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // re-opening of the radio tuner. 82244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (status == RadioTuner.ERROR_HARDWARE_FAILURE 82344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer || status == RadioTuner.ERROR_SERVER_DIED) { 82444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner != null) { 82544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner.close(); 82644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioTuner = null; 82744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 82844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 82944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Attempt to re-open the RadioTuner. Each time the radio tuner fails to open, the 83044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // mReOpenRadioTunerCount will be incremented. 83144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHandler.removeCallbacks(mOpenRadioTunerRunnable); 83244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHandler.postDelayed(mOpenRadioTunerRunnable, 83344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mReOpenRadioTunerCount * RADIO_TUNER_REOPEN_DELAY_MS); 83444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 83544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mReOpenRadioTunerCount++; 83644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 83744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 83844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 83944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mRadioTunerCallbacks) { 84044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onError(status); 84144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 84244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 84344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "onError(); Failed to notify IRadioCallbacks: " + e.getMessage()); 84444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 84544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 84644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 84744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 84844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onControlChanged(boolean control) { 84944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // If the radio loses control of the RadioTuner, then close it and allow it to be 85044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // re-opened when control has been gained. 85144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!control) { 85244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer close(); 85344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 85444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 85544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 85644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mRadioTuner == null) { 85744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer openRadioBandInternal(mCurrentRadioBand); 85844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 85944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 86044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 86144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 86244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Sets all metadata fields to {@code null}. 86344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 86444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void clearMetadata() { 86544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentChannelInfo = null; 86644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentArtist = null; 86744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentSongTitle = null; 86844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 86944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 87044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 87144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Retrieves the relevant information off the given {@link RadioMetadata} object and 87244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * sets them correspondingly on {@link #mCurrentChannelInfo}, {@link #mCurrentArtist} 87344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * and {@link #mCurrentSongTitle}. 87444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 87544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void updateMetadata(RadioMetadata metadata) { 87644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (metadata != null) { 87744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentChannelInfo = metadata.getString(RadioMetadata.METADATA_KEY_RDS_PS); 87844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentArtist = metadata.getString(RadioMetadata.METADATA_KEY_ARTIST); 87944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentSongTitle = metadata.getString(RadioMetadata.METADATA_KEY_TITLE); 88044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 88144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 88244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, String.format("updateMetadata(): [channel info: %s, artist: %s, " 88344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer + "song title: %s]", mCurrentChannelInfo, mCurrentArtist, 88444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentSongTitle)); 88544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 88644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 88744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 88844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 88944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 89044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private final Runnable mOpenRadioTunerRunnable = () -> openRadioBandInternal(mCurrentRadioBand); 89144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer} 892