Home | History | Annotate | Download | only in hal1
      1 /**
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.broadcastradio.hal1;
     18 
     19 import android.annotation.NonNull;
     20 import android.graphics.Bitmap;
     21 import android.graphics.BitmapFactory;
     22 import android.hardware.radio.ITuner;
     23 import android.hardware.radio.ITunerCallback;
     24 import android.hardware.radio.ProgramList;
     25 import android.hardware.radio.ProgramSelector;
     26 import android.hardware.radio.RadioManager;
     27 import android.os.IBinder;
     28 import android.os.RemoteException;
     29 import android.util.Slog;
     30 
     31 import java.util.Collections;
     32 import java.util.List;
     33 import java.util.Map;
     34 import java.util.Objects;
     35 
     36 class Tuner extends ITuner.Stub {
     37     private static final String TAG = "BroadcastRadioService.Tuner";
     38 
     39     /**
     40      * This field is used by native code, do not access or modify.
     41      */
     42     private final long mNativeContext;
     43 
     44     private final Object mLock = new Object();
     45     @NonNull private final TunerCallback mTunerCallback;
     46     @NonNull private final ITunerCallback mClientCallback;
     47     @NonNull private final IBinder.DeathRecipient mDeathRecipient;
     48 
     49     private boolean mIsClosed = false;
     50     private boolean mIsMuted = false;
     51     private int mRegion;
     52     private final boolean mWithAudio;
     53 
     54     Tuner(@NonNull ITunerCallback clientCallback, int halRev,
     55             int region, boolean withAudio, int band) {
     56         mClientCallback = clientCallback;
     57         mTunerCallback = new TunerCallback(this, clientCallback, halRev);
     58         mRegion = region;
     59         mWithAudio = withAudio;
     60         mNativeContext = nativeInit(halRev, withAudio, band);
     61         mDeathRecipient = this::close;
     62         try {
     63             mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0);
     64         } catch (RemoteException ex) {
     65             close();
     66         }
     67     }
     68 
     69     @Override
     70     protected void finalize() throws Throwable {
     71         nativeFinalize(mNativeContext);
     72         super.finalize();
     73     }
     74 
     75     private native long nativeInit(int halRev, boolean withAudio, int band);
     76     private native void nativeFinalize(long nativeContext);
     77     private native void nativeClose(long nativeContext);
     78 
     79     private native void nativeSetConfiguration(long nativeContext,
     80             @NonNull RadioManager.BandConfig config);
     81     private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region);
     82 
     83     private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel);
     84     private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel);
     85     private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector);
     86     private native void nativeCancel(long nativeContext);
     87 
     88     private native void nativeCancelAnnouncement(long nativeContext);
     89 
     90     private native boolean nativeStartBackgroundScan(long nativeContext);
     91     private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
     92             Map<String, String> vendorFilter);
     93 
     94     private native byte[] nativeGetImage(long nativeContext, int id);
     95 
     96     private native boolean nativeIsAnalogForced(long nativeContext);
     97     private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
     98 
     99     @Override
    100     public void close() {
    101         synchronized (mLock) {
    102             if (mIsClosed) return;
    103             mIsClosed = true;
    104             mTunerCallback.detach();
    105             mClientCallback.asBinder().unlinkToDeath(mDeathRecipient, 0);
    106             nativeClose(mNativeContext);
    107         }
    108     }
    109 
    110     @Override
    111     public boolean isClosed() {
    112         return mIsClosed;
    113     }
    114 
    115     private void checkNotClosedLocked() {
    116         if (mIsClosed) {
    117             throw new IllegalStateException("Tuner is closed, no further operations are allowed");
    118         }
    119     }
    120 
    121     private boolean checkConfiguredLocked() {
    122         if (mTunerCallback.isInitialConfigurationDone()) return true;
    123         Slog.w(TAG, "Initial configuration is still pending, skipping the operation");
    124         return false;
    125     }
    126 
    127     @Override
    128     public void setConfiguration(RadioManager.BandConfig config) {
    129         if (config == null) {
    130             throw new IllegalArgumentException("The argument must not be a null pointer");
    131         }
    132         synchronized (mLock) {
    133             checkNotClosedLocked();
    134             nativeSetConfiguration(mNativeContext, config);
    135             mRegion = config.getRegion();
    136         }
    137     }
    138 
    139     @Override
    140     public RadioManager.BandConfig getConfiguration() {
    141         synchronized (mLock) {
    142             checkNotClosedLocked();
    143             return nativeGetConfiguration(mNativeContext, mRegion);
    144         }
    145     }
    146 
    147     @Override
    148     public void setMuted(boolean mute) {
    149         if (!mWithAudio) {
    150             throw new IllegalStateException("Can't operate on mute - no audio requested");
    151         }
    152         synchronized (mLock) {
    153             checkNotClosedLocked();
    154             if (mIsMuted == mute) return;
    155             mIsMuted = mute;
    156             Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app");
    157         }
    158     }
    159 
    160     @Override
    161     public boolean isMuted() {
    162         if (!mWithAudio) {
    163             Slog.w(TAG, "Tuner did not request audio, pretending it was muted");
    164             return true;
    165         }
    166         synchronized (mLock) {
    167             checkNotClosedLocked();
    168             return mIsMuted;
    169         }
    170     }
    171 
    172     @Override
    173     public void step(boolean directionDown, boolean skipSubChannel) {
    174         synchronized (mLock) {
    175             checkNotClosedLocked();
    176             if (!checkConfiguredLocked()) return;
    177             nativeStep(mNativeContext, directionDown, skipSubChannel);
    178         }
    179     }
    180 
    181     @Override
    182     public void scan(boolean directionDown, boolean skipSubChannel) {
    183         synchronized (mLock) {
    184             checkNotClosedLocked();
    185             if (!checkConfiguredLocked()) return;
    186             nativeScan(mNativeContext, directionDown, skipSubChannel);
    187         }
    188     }
    189 
    190     @Override
    191     public void tune(ProgramSelector selector) {
    192         if (selector == null) {
    193             throw new IllegalArgumentException("The argument must not be a null pointer");
    194         }
    195         Slog.i(TAG, "Tuning to " + selector);
    196         synchronized (mLock) {
    197             checkNotClosedLocked();
    198             if (!checkConfiguredLocked()) return;
    199             nativeTune(mNativeContext, selector);
    200         }
    201     }
    202 
    203     @Override
    204     public void cancel() {
    205         synchronized (mLock) {
    206             checkNotClosedLocked();
    207             nativeCancel(mNativeContext);
    208         }
    209     }
    210 
    211     @Override
    212     public void cancelAnnouncement() {
    213         synchronized (mLock) {
    214             checkNotClosedLocked();
    215             nativeCancelAnnouncement(mNativeContext);
    216         }
    217     }
    218 
    219     @Override
    220     public Bitmap getImage(int id) {
    221         if (id == 0) {
    222             throw new IllegalArgumentException("Image ID is missing");
    223         }
    224 
    225         byte[] rawImage;
    226         synchronized (mLock) {
    227             rawImage = nativeGetImage(mNativeContext, id);
    228         }
    229         if (rawImage == null || rawImage.length == 0) {
    230             return null;
    231         }
    232 
    233         return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
    234     }
    235 
    236     @Override
    237     public boolean startBackgroundScan() {
    238         synchronized (mLock) {
    239             checkNotClosedLocked();
    240             return nativeStartBackgroundScan(mNativeContext);
    241         }
    242     }
    243 
    244     List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
    245         Map<String, String> sFilter = vendorFilter;
    246         synchronized (mLock) {
    247             checkNotClosedLocked();
    248             List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, sFilter);
    249             if (list == null) {
    250                 throw new IllegalStateException("Program list is not ready");
    251             }
    252             return list;
    253         }
    254     }
    255 
    256     @Override
    257     public void startProgramListUpdates(ProgramList.Filter filter) {
    258         mTunerCallback.startProgramListUpdates(filter);
    259     }
    260 
    261     @Override
    262     public void stopProgramListUpdates() {
    263         mTunerCallback.stopProgramListUpdates();
    264     }
    265 
    266     @Override
    267     public boolean isConfigFlagSupported(int flag) {
    268         return flag == RadioManager.CONFIG_FORCE_ANALOG;
    269     }
    270 
    271     @Override
    272     public boolean isConfigFlagSet(int flag) {
    273         if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
    274             synchronized (mLock) {
    275                 checkNotClosedLocked();
    276                 return nativeIsAnalogForced(mNativeContext);
    277             }
    278         }
    279         throw new UnsupportedOperationException("Not supported by HAL 1.x");
    280     }
    281 
    282     @Override
    283     public void setConfigFlag(int flag, boolean value) {
    284         if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
    285             synchronized (mLock) {
    286                 checkNotClosedLocked();
    287                 nativeSetAnalogForced(mNativeContext, value);
    288                 return;
    289             }
    290         }
    291         throw new UnsupportedOperationException("Not supported by HAL 1.x");
    292     }
    293 
    294     @Override
    295     public Map setParameters(Map parameters) {
    296         throw new UnsupportedOperationException("Not supported by HAL 1.x");
    297     }
    298 
    299     @Override
    300     public Map getParameters(List<String> keys) {
    301         throw new UnsupportedOperationException("Not supported by HAL 1.x");
    302     }
    303 }
    304