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