blob: bf0631473405bbd02c229ffceaf8fa7ef13fdf63 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.radio;
import android.content.Context;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
import android.hardware.radio.RadioTuner;
import android.support.annotation.Nullable;
import android.util.Log;
import com.android.car.radio.service.RadioRds;
import com.android.car.radio.service.RadioStation;
import java.util.ArrayList;
import java.util.List;
/**
* A class that will scan for valid radio stations in the background and store those stations into
* the radio database so that available stations for a particular radio band can be pre-populated
* for the user.
*/
public class RadioBackgroundScanner extends RadioTuner.Callback {
private static final String TAG = "Em.BackgroundScanner";
private static final int INVALID_RADIO_CHANNEL = -1;
private RadioTuner mRadioTuner;
private final RadioManager mRadioManager;
private final RadioManager.AmBandConfig mAmConfig;
private final RadioManager.FmBandConfig mFmConfig;
private final RadioManager.ModuleProperties mRadioModule;
private final RadioStorage mRadioStorage;
private int mCurrentChannel;
private int mCurrentBand;
private int mStartingChannel = INVALID_RADIO_CHANNEL;
private List<RadioStation> mScannedStations = new ArrayList<>();
public RadioBackgroundScanner(Context context, RadioManager radioManager,
RadioManager.AmBandConfig amConfig, RadioManager.FmBandConfig fmConfig,
RadioManager.ModuleProperties module) {
mRadioManager = radioManager;
mAmConfig = amConfig;
mFmConfig = fmConfig;
mRadioModule = module;
mRadioStorage = RadioStorage.getInstance(context);
}
/**
* Notify this {@link RadioBackgroundScanner} that the current radio band has changed and
* a new scan should start.
*/
public void onRadioBandChanged(int radioBand) {
mStartingChannel = INVALID_RADIO_CHANNEL;
mCurrentBand = radioBand;
mScannedStations.clear();
RadioManager.BandConfig config = getRadioConfig(radioBand);
if (config == null) {
Log.w(TAG, "Cannot create config for radio band: " + radioBand);
return;
}
if (mRadioTuner != null) {
mRadioTuner.setConfiguration(config);
} else {
mRadioTuner = mRadioManager.openTuner(mRadioModule.getId(), config, true,
this /* callback */, null /* handler */);
}
}
/**
* Returns the proper {@link android.hardware.radio.RadioManager.BandConfig} for the given
* radio band. {@code null} is returned if the band is not suppored.
*/
@Nullable
private RadioManager.BandConfig getRadioConfig(int selectedRadioBand) {
switch (selectedRadioBand) {
case RadioManager.BAND_AM:
return mAmConfig;
case RadioManager.BAND_FM:
return mFmConfig;
// TODO: Support BAND_FM_HD and BAND_AM_HD.
default:
return null;
}
}
/**
* Replaces the given station in {@link #mScannedStations} or adds it to the end of the list if
* it does not exist.
*/
private void addOrReplaceInScannedStations(RadioStation station) {
int index = mScannedStations.indexOf(station);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Storing pre-scanned station: " + station);
}
if (index == -1) {
mScannedStations.add(station);
} else {
mScannedStations.set(index, station);
}
}
@Override
public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "onProgramInfoChanged(); info: " + info);
}
if (info == null) {
return;
}
mCurrentChannel = info.getChannel();
if (mStartingChannel == INVALID_RADIO_CHANNEL) {
mStartingChannel = mCurrentChannel;
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Starting scan from channel: " + mStartingChannel);
}
}
// Stop scanning if we have looped back to the starting station.
if (mStartingChannel == mCurrentChannel) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, String.format("Looped back around to starting channel %s; storing "
+ "%d pre-scanned stations", mStartingChannel, mScannedStations.size()));
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
for (RadioStation station : mScannedStations) {
Log.v(TAG, station.toString());
}
}
mStartingChannel = INVALID_RADIO_CHANNEL;
// Close the RadioTuner so that this class no longer receives any callbacks and store
// all scanned statiosn into the database.
mRadioTuner.close();
mRadioTuner = null;
mRadioStorage.storePreScannedStations(mCurrentBand, mScannedStations);
return;
}
RadioMetadata metadata = info.getMetadata();
// If there is no metadata, then directly store the radio information into the database.
// Otherwise, onMetadataChanged() can handle the storage.
if (metadata != null) {
onMetadataChanged(metadata);
} else {
RadioStation station = new RadioStation(mCurrentChannel, 0 /* subChannelNumber */,
mCurrentBand, null /* rds */);
addOrReplaceInScannedStations(station);
}
// Initialize another seek to the next valid station.
mRadioTuner.scan(RadioTuner.DIRECTION_UP, true);
}
@Override
public void onMetadataChanged(RadioMetadata metadata) {
if (metadata == null) {
return;
}
String stationInfo = metadata.getString(RadioMetadata.METADATA_KEY_RDS_PS);
RadioRds rds = new RadioRds(stationInfo, null /* songArtist */, null /* songTitle */);
RadioStation station = new RadioStation(mCurrentChannel, 0 /* subChannelNumber */,
mCurrentBand, rds);
addOrReplaceInScannedStations(station);
}
@Override
public void onConfigurationChanged(RadioManager.BandConfig config) {
if (config == null) {
return;
}
mCurrentBand = config.getType();
}
}