Home | History | Annotate | Download | only in radio
      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 android.hardware.radio;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.util.Log;
     24 
     25 import java.util.List;
     26 import java.util.Map;
     27 import java.util.Objects;
     28 
     29 /**
     30  * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
     31  */
     32 class TunerCallbackAdapter extends ITunerCallback.Stub {
     33     private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";
     34 
     35     private final Object mLock = new Object();
     36     @NonNull private final RadioTuner.Callback mCallback;
     37     @NonNull private final Handler mHandler;
     38 
     39     @Nullable ProgramList mProgramList;
     40 
     41     // cache for deprecated methods
     42     boolean mIsAntennaConnected = true;
     43     @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
     44     private boolean mDelayedCompleteCallback = false;
     45     @Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
     46 
     47     TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
     48         mCallback = callback;
     49         if (handler == null) {
     50             mHandler = new Handler(Looper.getMainLooper());
     51         } else {
     52             mHandler = handler;
     53         }
     54     }
     55 
     56     void close() {
     57         synchronized (mLock) {
     58             if (mProgramList != null) mProgramList.close();
     59         }
     60     }
     61 
     62     void setProgramListObserver(@Nullable ProgramList programList,
     63             @NonNull ProgramList.OnCloseListener closeListener) {
     64         Objects.requireNonNull(closeListener);
     65         synchronized (mLock) {
     66             if (mProgramList != null) {
     67                 Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
     68                 mProgramList.close();
     69             }
     70             mProgramList = programList;
     71             if (programList == null) return;
     72             programList.setOnCloseListener(() -> {
     73                 synchronized (mLock) {
     74                     if (mProgramList != programList) return;
     75                     mProgramList = null;
     76                     mLastCompleteList = null;
     77                     closeListener.onClose();
     78                 }
     79             });
     80             programList.addOnCompleteListener(() -> {
     81                 synchronized (mLock) {
     82                     if (mProgramList != programList) return;
     83                     mLastCompleteList = programList.toList();
     84                     if (mDelayedCompleteCallback) {
     85                         Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
     86                         sendBackgroundScanCompleteLocked();
     87                     }
     88                 }
     89             });
     90         }
     91     }
     92 
     93     @Nullable List<RadioManager.ProgramInfo> getLastCompleteList() {
     94         synchronized (mLock) {
     95             return mLastCompleteList;
     96         }
     97     }
     98 
     99     void clearLastCompleteList() {
    100         synchronized (mLock) {
    101             mLastCompleteList = null;
    102         }
    103     }
    104 
    105     @Nullable RadioManager.ProgramInfo getCurrentProgramInformation() {
    106         synchronized (mLock) {
    107             return mCurrentProgramInfo;
    108         }
    109     }
    110 
    111     boolean isAntennaConnected() {
    112         return mIsAntennaConnected;
    113     }
    114 
    115     @Override
    116     public void onError(int status) {
    117         mHandler.post(() -> mCallback.onError(status));
    118     }
    119 
    120     @Override
    121     public void onTuneFailed(int status, @Nullable ProgramSelector selector) {
    122         mHandler.post(() -> mCallback.onTuneFailed(status, selector));
    123 
    124         int errorCode;
    125         switch (status) {
    126             case RadioManager.STATUS_PERMISSION_DENIED:
    127             case RadioManager.STATUS_DEAD_OBJECT:
    128                 errorCode = RadioTuner.ERROR_SERVER_DIED;
    129                 break;
    130             case RadioManager.STATUS_ERROR:
    131             case RadioManager.STATUS_NO_INIT:
    132             case RadioManager.STATUS_BAD_VALUE:
    133             case RadioManager.STATUS_INVALID_OPERATION:
    134                 Log.i(TAG, "Got an error with no mapping to the legacy API (" + status
    135                         + "), doing a best-effort conversion to ERROR_SCAN_TIMEOUT");
    136             // fall through
    137             case RadioManager.STATUS_TIMED_OUT:
    138             default:
    139                 errorCode = RadioTuner.ERROR_SCAN_TIMEOUT;
    140         }
    141         mHandler.post(() -> mCallback.onError(errorCode));
    142     }
    143 
    144     @Override
    145     public void onConfigurationChanged(RadioManager.BandConfig config) {
    146         mHandler.post(() -> mCallback.onConfigurationChanged(config));
    147     }
    148 
    149     @Override
    150     public void onCurrentProgramInfoChanged(RadioManager.ProgramInfo info) {
    151         if (info == null) {
    152             Log.e(TAG, "ProgramInfo must not be null");
    153             return;
    154         }
    155 
    156         synchronized (mLock) {
    157             mCurrentProgramInfo = info;
    158         }
    159 
    160         mHandler.post(() -> {
    161             mCallback.onProgramInfoChanged(info);
    162 
    163             RadioMetadata metadata = info.getMetadata();
    164             if (metadata != null) mCallback.onMetadataChanged(metadata);
    165         });
    166     }
    167 
    168     @Override
    169     public void onTrafficAnnouncement(boolean active) {
    170         mHandler.post(() -> mCallback.onTrafficAnnouncement(active));
    171     }
    172 
    173     @Override
    174     public void onEmergencyAnnouncement(boolean active) {
    175         mHandler.post(() -> mCallback.onEmergencyAnnouncement(active));
    176     }
    177 
    178     @Override
    179     public void onAntennaState(boolean connected) {
    180         mIsAntennaConnected = connected;
    181         mHandler.post(() -> mCallback.onAntennaState(connected));
    182     }
    183 
    184     @Override
    185     public void onBackgroundScanAvailabilityChange(boolean isAvailable) {
    186         mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
    187     }
    188 
    189     private void sendBackgroundScanCompleteLocked() {
    190         mDelayedCompleteCallback = false;
    191         mHandler.post(() -> mCallback.onBackgroundScanComplete());
    192     }
    193 
    194     @Override
    195     public void onBackgroundScanComplete() {
    196         synchronized (mLock) {
    197             if (mLastCompleteList == null) {
    198                 Log.i(TAG, "Got onBackgroundScanComplete callback, but the "
    199                         + "program list didn't get through yet. Delaying it...");
    200                 mDelayedCompleteCallback = true;
    201                 return;
    202             }
    203             sendBackgroundScanCompleteLocked();
    204         }
    205     }
    206 
    207     @Override
    208     public void onProgramListChanged() {
    209         mHandler.post(() -> mCallback.onProgramListChanged());
    210     }
    211 
    212     @Override
    213     public void onProgramListUpdated(ProgramList.Chunk chunk) {
    214         synchronized (mLock) {
    215             if (mProgramList == null) return;
    216             mProgramList.apply(Objects.requireNonNull(chunk));
    217         }
    218     }
    219 
    220     @Override
    221     public void onParametersUpdated(Map parameters) {
    222         mHandler.post(() -> mCallback.onParametersUpdated(parameters));
    223     }
    224 }
    225