content: /*
   * Copyright (C) 2018 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.telephony.ims.compat.feature;
  
  import android.annotation.IntDef;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.os.IInterface;
  import android.os.RemoteException;
  import android.telephony.SubscriptionManager;
  import android.util.Log;
  
  import com.android.ims.internal.IImsFeatureStatusCallback;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.Collections;
  import java.util.Iterator;
  import java.util.Set;
  import java.util.WeakHashMap;
  
  /**
   * Base class for all IMS features that are supported by the framework.
   * @hide
   */
  public abstract class ImsFeature {
  
      private static final String LOG_TAG = "ImsFeature";
  
      // Invalid feature value
      public static final int INVALID = -1;
      // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
      // defined values in ImsServiceClass for compatibility purposes.
      public static final int EMERGENCY_MMTEL = 0;
      public static final int MMTEL = 1;
      public static final int RCS = 2;
      // Total number of features defined
      public static final int MAX = 3;
  
      // Integer values defining the state of the ImsFeature at any time.
      @IntDef(flag = true,
              value = {
                      STATE_NOT_AVAILABLE,
                      STATE_INITIALIZING,
                      STATE_READY,
              })
      @Retention(RetentionPolicy.SOURCE)
      public @interface ImsState {}
      public static final int STATE_NOT_AVAILABLE = 0;
      public static final int STATE_INITIALIZING = 1;
      public static final int STATE_READY = 2;
  
      private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
              new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
      private @ImsState int mState = STATE_NOT_AVAILABLE;
      private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
      protected Context mContext;
  
      public void setContext(Context context) {
          mContext = context;
      }
  
      public void setSlotId(int slotId) {
          mSlotId = slotId;
      }
  
      @UnsupportedAppUsage
      public int getFeatureState() {
          return mState;
      }
  
      @UnsupportedAppUsage
      protected final void setFeatureState(@ImsState int state) {
          if (mState != state) {
              mState = state;
              notifyFeatureState(state);
          }
      }
  
      public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
          if (c == null) {
              return;
          }
          try {
              // If we have just connected, send queued status.
              c.notifyImsFeatureStatus(mState);
              // Add the callback if the callback completes successfully without a RemoteException.
              synchronized (mStatusCallbacks) {
                  mStatusCallbacks.add(c);
              }
          } catch (RemoteException e) {
              Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
          }
      }
  
      public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
          if (c == null) {
              return;
          }
          synchronized (mStatusCallbacks) {
              mStatusCallbacks.remove(c);
          }
      }
  
      /**
       * Internal method called by ImsFeature when setFeatureState has changed.
       * @param state
       */
      private void notifyFeatureState(@ImsState int state) {
          synchronized (mStatusCallbacks) {
              for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
                   iter.hasNext(); ) {
                  IImsFeatureStatusCallback callback = iter.next();
                  try {
                      Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
                      callback.notifyImsFeatureStatus(state);
                  } catch (RemoteException e) {
                      // remove if the callback is no longer alive.
                      iter.remove();
                      Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
                  }
              }
          }
      }
  
      /**
       * Called when the feature is ready to use.
       */
      public abstract void onFeatureReady();
  
      /**
       * Called when the feature is being removed and must be cleaned up.
       */
      public abstract void onFeatureRemoved();
  
      /**
       * @return Binder instance
       */
      public abstract IInterface getBinder();
  }
  