/* * Copyright (C) 2015 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 android.hardware.radio; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.os.Message; import java.lang.ref.WeakReference; import java.util.List; import java.util.UUID; /** * A RadioModule implements the RadioTuner interface for a broadcast radio tuner physically * present on the device and exposed by the radio HAL. * * @hide */ public class RadioModule extends RadioTuner { private long mNativeContext = 0; private int mId; private NativeEventHandlerDelegate mEventHandlerDelegate; RadioModule(int moduleId, RadioManager.BandConfig config, boolean withAudio, RadioTuner.Callback callback, Handler handler) { mId = moduleId; mEventHandlerDelegate = new NativeEventHandlerDelegate(callback, handler); native_setup(new WeakReference(this), config, withAudio); } private native void native_setup(Object module_this, RadioManager.BandConfig config, boolean withAudio); @Override protected void finalize() { native_finalize(); } private native void native_finalize(); boolean initCheck() { return mNativeContext != 0; } // RadioTuner implementation public native void close(); public native int setConfiguration(RadioManager.BandConfig config); public native int getConfiguration(RadioManager.BandConfig[] config); public native int setMute(boolean mute); public native boolean getMute(); public native int step(int direction, boolean skipSubChannel); public native int scan(int direction, boolean skipSubChannel); public native int tune(int channel, int subChannel); public native int cancel(); public native int getProgramInformation(RadioManager.ProgramInfo[] info); public native @NonNull List getProgramList(@Nullable String filter); public native boolean isAntennaConnected(); public native boolean hasControl(); /* keep in sync with radio_event_type_t in system/core/include/system/radio.h */ static final int EVENT_HW_FAILURE = 0; static final int EVENT_CONFIG = 1; static final int EVENT_ANTENNA = 2; static final int EVENT_TUNED = 3; static final int EVENT_METADATA = 4; static final int EVENT_TA = 5; static final int EVENT_AF_SWITCH = 6; static final int EVENT_EA = 7; static final int EVENT_CONTROL = 100; static final int EVENT_SERVER_DIED = 101; private class NativeEventHandlerDelegate { private final Handler mHandler; NativeEventHandlerDelegate(final RadioTuner.Callback callback, Handler handler) { // find the looper for our new event handler Looper looper; if (handler != null) { looper = handler.getLooper(); } else { looper = Looper.getMainLooper(); } // construct the event handler with this looper if (looper != null) { // implement the event handler delegate mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_HW_FAILURE: if (callback != null) { callback.onError(RadioTuner.ERROR_HARDWARE_FAILURE); } break; case EVENT_CONFIG: { RadioManager.BandConfig config = (RadioManager.BandConfig)msg.obj; switch(msg.arg1) { case RadioManager.STATUS_OK: if (callback != null) { callback.onConfigurationChanged(config); } break; default: if (callback != null) { callback.onError(RadioTuner.ERROR_CONFIG); } break; } } break; case EVENT_ANTENNA: if (callback != null) { callback.onAntennaState(msg.arg2 == 1); } break; case EVENT_AF_SWITCH: case EVENT_TUNED: { RadioManager.ProgramInfo info = (RadioManager.ProgramInfo)msg.obj; switch (msg.arg1) { case RadioManager.STATUS_OK: if (callback != null) { callback.onProgramInfoChanged(info); } break; case RadioManager.STATUS_TIMED_OUT: if (callback != null) { callback.onError(RadioTuner.ERROR_SCAN_TIMEOUT); } break; case RadioManager.STATUS_INVALID_OPERATION: default: if (callback != null) { callback.onError(RadioTuner.ERROR_CANCELLED); } break; } } break; case EVENT_METADATA: { RadioMetadata metadata = (RadioMetadata)msg.obj; if (callback != null) { callback.onMetadataChanged(metadata); } } break; case EVENT_TA: if (callback != null) { callback.onTrafficAnnouncement(msg.arg2 == 1); } break; case EVENT_EA: if (callback != null) { callback.onEmergencyAnnouncement(msg.arg2 == 1); } case EVENT_CONTROL: if (callback != null) { callback.onControlChanged(msg.arg2 == 1); } break; case EVENT_SERVER_DIED: if (callback != null) { callback.onError(RadioTuner.ERROR_SERVER_DIED); } break; default: // Should not happen break; } } }; } else { mHandler = null; } } Handler handler() { return mHandler; } } @SuppressWarnings("unused") private static void postEventFromNative(Object module_ref, int what, int arg1, int arg2, Object obj) { RadioModule module = (RadioModule)((WeakReference)module_ref).get(); if (module == null) { return; } NativeEventHandlerDelegate delegate = module.mEventHandlerDelegate; if (delegate != null) { Handler handler = delegate.handler(); if (handler != null) { Message m = handler.obtainMessage(what, arg1, arg2, obj); handler.sendMessage(m); } } } }