blob: 9915c34fb58238b681a0bd1d7f50ffea8ed8c1bb [file] [log] [blame]
/**
* Copyright (C) 2017 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.server.radio;
import android.annotation.NonNull;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.util.Slog;
import java.util.List;
class Tuner extends ITuner.Stub {
// TODO(b/36863239): rename to RadioService.Tuner when native service goes away
private static final String TAG = "RadioServiceJava.Tuner";
/**
* This field is used by native code, do not access or modify.
*/
private final long mNativeContext;
@NonNull private final TunerCallback mTunerCallback;
private final Object mLock = new Object();
private boolean mIsClosed = false;
private boolean mIsMuted = false;
private int mRegion; // TODO(b/36863239): find better solution to manage regions
private final boolean mWithAudio;
Tuner(@NonNull ITunerCallback clientCallback, int halRev, int region, boolean withAudio) {
mTunerCallback = new TunerCallback(this, clientCallback, halRev);
mRegion = region;
mWithAudio = withAudio;
mNativeContext = nativeInit(halRev);
}
@Override
protected void finalize() throws Throwable {
nativeFinalize(mNativeContext);
super.finalize();
}
private native long nativeInit(int halRev);
private native void nativeFinalize(long nativeContext);
private native void nativeClose(long nativeContext);
private native void nativeSetConfiguration(long nativeContext,
@NonNull RadioManager.BandConfig config);
private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region);
private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel);
private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel);
private native void nativeTune(long nativeContext, int channel, int subChannel);
private native void nativeCancel(long nativeContext);
private native RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
private native boolean nativeStartBackgroundScan(long nativeContext);
private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
String filter);
private native boolean nativeIsAnalogForced(long nativeContext);
private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
private native boolean nativeIsAntennaConnected(long nativeContext);
@Override
public void close() {
synchronized (mLock) {
if (mIsClosed) return;
mTunerCallback.detach();
nativeClose(mNativeContext);
mIsClosed = true;
}
}
private void checkNotClosedLocked() {
if (mIsClosed) {
throw new IllegalStateException("Tuner is closed, no further operations are allowed");
}
}
@Override
public void setConfiguration(RadioManager.BandConfig config) {
if (config == null) {
throw new IllegalArgumentException("The argument must not be a null pointer");
}
synchronized (mLock) {
checkNotClosedLocked();
nativeSetConfiguration(mNativeContext, config);
mRegion = config.getRegion();
}
}
@Override
public RadioManager.BandConfig getConfiguration() {
synchronized (mLock) {
checkNotClosedLocked();
return nativeGetConfiguration(mNativeContext, mRegion);
}
}
@Override
public void setMuted(boolean mute) {
if (!mWithAudio) {
throw new IllegalStateException("Can't operate on mute - no audio requested");
}
synchronized (mLock) {
checkNotClosedLocked();
if (mIsMuted == mute) return;
mIsMuted = mute;
// TODO(b/34348946): notifify audio policy manager of media activity on radio audio
// device. This task is pulled directly from previous implementation of native service.
}
}
@Override
public boolean isMuted() {
if (!mWithAudio) {
Slog.w(TAG, "Tuner did not request audio, pretending it was muted");
return true;
}
synchronized (mLock) {
checkNotClosedLocked();
return mIsMuted;
}
}
@Override
public void step(boolean directionDown, boolean skipSubChannel) {
synchronized (mLock) {
checkNotClosedLocked();
nativeStep(mNativeContext, directionDown, skipSubChannel);
}
}
@Override
public void scan(boolean directionDown, boolean skipSubChannel) {
synchronized (mLock) {
checkNotClosedLocked();
nativeScan(mNativeContext, directionDown, skipSubChannel);
}
}
@Override
public void tune(int channel, int subChannel) {
synchronized (mLock) {
checkNotClosedLocked();
nativeTune(mNativeContext, channel, subChannel);
}
}
@Override
public void cancel() {
synchronized (mLock) {
checkNotClosedLocked();
nativeCancel(mNativeContext);
}
}
@Override
public RadioManager.ProgramInfo getProgramInformation() {
synchronized (mLock) {
checkNotClosedLocked();
return nativeGetProgramInformation(mNativeContext);
}
}
@Override
public boolean startBackgroundScan() {
synchronized (mLock) {
checkNotClosedLocked();
return nativeStartBackgroundScan(mNativeContext);
}
}
@Override
public List<RadioManager.ProgramInfo> getProgramList(String filter) {
synchronized (mLock) {
checkNotClosedLocked();
List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, filter);
if (list == null) {
throw new IllegalStateException("Program list is not ready");
}
return list;
}
}
@Override
public boolean isAnalogForced() {
synchronized (mLock) {
checkNotClosedLocked();
return nativeIsAnalogForced(mNativeContext);
}
}
@Override
public void setAnalogForced(boolean isForced) {
synchronized (mLock) {
checkNotClosedLocked();
nativeSetAnalogForced(mNativeContext, isForced);
}
}
@Override
public boolean isAntennaConnected() {
synchronized (mLock) {
checkNotClosedLocked();
return nativeIsAntennaConnected(mNativeContext);
}
}
}