content: /**
   * 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 android.hardware.radio;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.graphics.Bitmap;
  import android.os.RemoteException;
  import android.util.Log;
  
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  
  /**
   * Implements the RadioTuner interface by forwarding calls to radio service.
   */
  class TunerAdapter extends RadioTuner {
      private static final String TAG = "BroadcastRadio.TunerAdapter";
  
      @NonNull private final ITuner mTuner;
      @NonNull private final TunerCallbackAdapter mCallback;
      private boolean mIsClosed = false;
  
      private @RadioManager.Band int mBand;
  
      private ProgramList mLegacyListProxy;
      private Map<String, String> mLegacyListFilter;
  
      TunerAdapter(@NonNull ITuner tuner, @NonNull TunerCallbackAdapter callback,
              @RadioManager.Band int band) {
          mTuner = Objects.requireNonNull(tuner);
          mCallback = Objects.requireNonNull(callback);
          mBand = band;
      }
  
      @Override
      public void close() {
          synchronized (mTuner) {
              if (mIsClosed) {
                  Log.v(TAG, "Tuner is already closed");
                  return;
              }
              mIsClosed = true;
              if (mLegacyListProxy != null) {
                  mLegacyListProxy.close();
                  mLegacyListProxy = null;
              }
              mCallback.close();
          }
          try {
              mTuner.close();
          } catch (RemoteException e) {
              Log.e(TAG, "Exception trying to close tuner", e);
          }
      }
  
      @Override
      public int setConfiguration(RadioManager.BandConfig config) {
          if (config == null) return RadioManager.STATUS_BAD_VALUE;
          try {
              mTuner.setConfiguration(config);
              mBand = config.getType();
              return RadioManager.STATUS_OK;
          } catch (IllegalArgumentException e) {
              Log.e(TAG, "Can't set configuration", e);
              return RadioManager.STATUS_BAD_VALUE;
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return RadioManager.STATUS_DEAD_OBJECT;
          }
      }
  
      @Override
      public int getConfiguration(RadioManager.BandConfig[] config) {
          if (config == null || config.length != 1) {
              throw new IllegalArgumentException("The argument must be an array of length 1");
          }
          try {
              config[0] = mTuner.getConfiguration();
              return RadioManager.STATUS_OK;
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return RadioManager.STATUS_DEAD_OBJECT;
          }
      }
  
      @Override
      public int setMute(boolean mute) {
          try {
              mTuner.setMuted(mute);
          } catch (IllegalStateException e) {
              Log.e(TAG, "Can't set muted", e);
              return RadioManager.STATUS_ERROR;
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return RadioManager.STATUS_DEAD_OBJECT;
          }
          return RadioManager.STATUS_OK;
      }
  
      @Override
      public boolean getMute() {
          try {
              return mTuner.isMuted();
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return true;
          }
      }
  
      @Override
      public int step(int direction, boolean skipSubChannel) {
          try {
              mTuner.step(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
          } catch (IllegalStateException e) {
              Log.e(TAG, "Can't step", e);
              return RadioManager.STATUS_INVALID_OPERATION;
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return RadioManager.STATUS_DEAD_OBJECT;
          }
          return RadioManager.STATUS_OK;
      }
  
      @Override
      public int scan(int direction, boolean skipSubChannel) {
          try {
              mTuner.scan(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
          } catch (IllegalStateException e) {
              Log.e(TAG, "Can't scan", e);
              return RadioManager.STATUS_INVALID_OPERATION;
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return RadioManager.STATUS_DEAD_OBJECT;
          }
          return RadioManager.STATUS_OK;
      }
  
      @Override
      public int tune(int channel, int subChannel) {
          try {
              mTuner.tune(ProgramSelector.createAmFmSelector(mBand, channel, subChannel));
          } catch (IllegalStateException e) {
              Log.e(TAG, "Can't tune", e);
              return RadioManager.STATUS_INVALID_OPERATION;
          } catch (IllegalArgumentException e) {
              Log.e(TAG, "Can't tune", e);
              return RadioManager.STATUS_BAD_VALUE;
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return RadioManager.STATUS_DEAD_OBJECT;
          }
          return RadioManager.STATUS_OK;
      }
  
      @Override
      public void tune(@NonNull ProgramSelector selector) {
          try {
              mTuner.tune(selector);
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public int cancel() {
          try {
              mTuner.cancel();
          } catch (IllegalStateException e) {
              Log.e(TAG, "Can't cancel", e);
              return RadioManager.STATUS_INVALID_OPERATION;
          } catch (RemoteException e) {
              Log.e(TAG, "service died", e);
              return RadioManager.STATUS_DEAD_OBJECT;
          }
          return RadioManager.STATUS_OK;
      }
  
      @Override
      public void cancelAnnouncement() {
          try {
              mTuner.cancelAnnouncement();
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public int getProgramInformation(RadioManager.ProgramInfo[] info) {
          if (info == null || info.length != 1) {
              Log.e(TAG, "The argument must be an array of length 1");
              return RadioManager.STATUS_BAD_VALUE;
          }
  
          RadioManager.ProgramInfo current = mCallback.getCurrentProgramInformation();
          if (current == null) {
              Log.w(TAG, "Didn't get program info yet");
              return RadioManager.STATUS_INVALID_OPERATION;
          }
          info[0] = current;
          return RadioManager.STATUS_OK;
      }
  
      @Override
      public @Nullable Bitmap getMetadataImage(int id) {
          try {
              return mTuner.getImage(id);
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public boolean startBackgroundScan() {
          try {
              return mTuner.startBackgroundScan();
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public @NonNull List<RadioManager.ProgramInfo>
              getProgramList(@Nullable Map<String, String> vendorFilter) {
          synchronized (mTuner) {
              if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) {
                  Log.i(TAG, "Program list filter has changed, requesting new list");
                  mLegacyListProxy = new ProgramList();
                  mLegacyListFilter = vendorFilter;
  
                  mCallback.clearLastCompleteList();
                  mCallback.setProgramListObserver(mLegacyListProxy, () -> { });
                  try {
                      mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
                  } catch (RemoteException ex) {
                      throw new RuntimeException("service died", ex);
                  }
              }
  
              List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
              if (list == null) throw new IllegalStateException("Program list is not ready yet");
              return list;
          }
      }
  
      @Override
      public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
          synchronized (mTuner) {
              if (mLegacyListProxy != null) {
                  mLegacyListProxy.close();
                  mLegacyListProxy = null;
              }
              mLegacyListFilter = null;
  
              ProgramList list = new ProgramList();
              mCallback.setProgramListObserver(list, () -> {
                  try {
                      mTuner.stopProgramListUpdates();
                  } catch (IllegalStateException ex) {
                      // it's fine to not stop updates if tuner is already closed
                  } catch (RemoteException ex) {
                      Log.e(TAG, "Couldn't stop program list updates", ex);
                  }
              });
  
              try {
                  mTuner.startProgramListUpdates(filter);
              } catch (UnsupportedOperationException ex) {
                  Log.i(TAG, "Program list is not supported with this hardware");
                  return null;
              } catch (RemoteException ex) {
                  mCallback.setProgramListObserver(null, () -> { });
                  throw new RuntimeException("service died", ex);
              }
  
              return list;
          }
      }
  
      @Override
      public boolean isAnalogForced() {
          try {
              return isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG);
          } catch (UnsupportedOperationException ex) {
              throw new IllegalStateException(ex);
          }
      }
  
      @Override
      public void setAnalogForced(boolean isForced) {
          try {
              setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, isForced);
          } catch (UnsupportedOperationException ex) {
              throw new IllegalStateException(ex);
          }
      }
  
      @Override
      public boolean isConfigFlagSupported(@RadioManager.ConfigFlag int flag) {
          try {
              return mTuner.isConfigFlagSupported(flag);
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public boolean isConfigFlagSet(@RadioManager.ConfigFlag int flag) {
          try {
              return mTuner.isConfigFlagSet(flag);
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) {
          try {
              mTuner.setConfigFlag(flag, value);
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
          try {
              return mTuner.setParameters(Objects.requireNonNull(parameters));
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
          try {
              return mTuner.getParameters(Objects.requireNonNull(keys));
          } catch (RemoteException e) {
              throw new RuntimeException("service died", e);
          }
      }
  
      @Override
      public boolean isAntennaConnected() {
          return mCallback.isAntennaConnected();
      }
  
      @Override
      public boolean hasControl() {
          try {
              // don't rely on mIsClosed, as tuner might get closed internally
              return !mTuner.isClosed();
          } catch (RemoteException e) {
              return false;
          }
      }
  }
  