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.stub;
  
  import android.annotation.IntDef;
  import android.annotation.SystemApi;
  import android.os.RemoteException;
  import android.telephony.SmsManager;
  import android.telephony.SmsMessage;
  import android.telephony.ims.aidl.IImsSmsListener;
  import android.util.Log;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  
  /**
   * Base implementation for SMS over IMS.
   *
   * Any service wishing to provide SMS over IMS should extend this class and implement all methods
   * that the service supports.
   *
   * @hide
   */
  @SystemApi
  public class ImsSmsImplBase {
      private static final String LOG_TAG = "SmsImplBase";
  
      /** @hide */
      @IntDef({
              SEND_STATUS_OK,
              SEND_STATUS_ERROR,
              SEND_STATUS_ERROR_RETRY,
              SEND_STATUS_ERROR_FALLBACK
          })
      @Retention(RetentionPolicy.SOURCE)
      public @interface SendStatusResult {}
      /**
       * Message was sent successfully.
       */
      public static final int SEND_STATUS_OK = 1;
  
      /**
       * IMS provider failed to send the message and platform should not retry falling back to sending
       * the message using the radio.
       */
      public static final int SEND_STATUS_ERROR = 2;
  
      /**
       * IMS provider failed to send the message and platform should retry again after setting TP-RD
       * bit to high.
       */
      public static final int SEND_STATUS_ERROR_RETRY = 3;
  
      /**
       * IMS provider failed to send the message and platform should retry falling back to sending
       * the message using the radio.
       */
      public static final int SEND_STATUS_ERROR_FALLBACK = 4;
  
      /** @hide */
      @IntDef({
              DELIVER_STATUS_OK,
              DELIVER_STATUS_ERROR_GENERIC,
              DELIVER_STATUS_ERROR_NO_MEMORY,
              DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
          })
      @Retention(RetentionPolicy.SOURCE)
      public @interface DeliverStatusResult {}
      /**
       * Message was delivered successfully.
       */
      public static final int DELIVER_STATUS_OK = 1;
  
      /**
       * Message was not delivered.
       */
      public static final int DELIVER_STATUS_ERROR_GENERIC = 2;
  
      /**
       * Message was not delivered due to lack of memory.
       */
      public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3;
  
      /**
       * Message was not delivered as the request is not supported.
       */
      public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4;
  
      /** @hide */
      @IntDef({
              STATUS_REPORT_STATUS_OK,
              STATUS_REPORT_STATUS_ERROR
          })
      @Retention(RetentionPolicy.SOURCE)
      public @interface StatusReportResult {}
  
      /**
       * Status Report was set successfully.
       */
      public static final int STATUS_REPORT_STATUS_OK = 1;
  
      /**
       * Error while setting status report.
       */
      public static final int STATUS_REPORT_STATUS_ERROR = 2;
  
      // Lock for feature synchronization
      private final Object mLock = new Object();
      private IImsSmsListener mListener;
  
      /**
       * Registers a listener responsible for handling tasks like delivering messages.
       *
       * @param listener listener to register.
       *
       * @hide
       */
      public final void registerSmsListener(IImsSmsListener listener) {
          synchronized (mLock) {
              mListener = listener;
          }
      }
  
      /**
       * This method will be triggered by the platform when the user attempts to send an SMS. This
       * method should be implemented by the IMS providers to provide implementation of sending an SMS
       * over IMS.
       *
       * @param token unique token generated by the platform that should be used when triggering
       *             callbacks for this specific message.
       * @param messageRef the message reference.
       * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
       *               {@link SmsMessage#FORMAT_3GPP2}.
       * @param smsc the Short Message Service Center address.
       * @param isRetry whether it is a retry of an already attempted message or not.
       * @param pdu PDUs representing the contents of the message.
       */
      public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
              byte[] pdu) {
          // Base implementation returns error. Should be overridden.
          try {
              onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
                      SmsManager.RESULT_ERROR_GENERIC_FAILURE);
          } catch (RuntimeException e) {
              Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
          }
      }
  
      /**
       * This method will be triggered by the platform after
       * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
       * provider.
       *
       * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
       * @param result result of delivering the message. Valid values are:
       *  {@link #DELIVER_STATUS_OK},
       *  {@link #DELIVER_STATUS_ERROR_GENERIC},
       *  {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
       *  {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
       * @param messageRef the message reference
       */
      public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
          Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
      }
  
      /**
       * This method will be triggered by the platform after
       * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the
       * result to the IMS provider.
       *
       * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
       * @param result result of delivering the message. Valid values are:
       *  {@link #STATUS_REPORT_STATUS_OK},
       *  {@link #STATUS_REPORT_STATUS_ERROR}
       * @param messageRef the message reference
       */
      public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
          Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
      }
  
      /**
       * This method should be triggered by the IMS providers when there is an incoming message. The
       * platform will deliver the message to the messages database and notify the IMS provider of the
       * result by calling {@link #acknowledgeSms(int, int, int)}.
       *
       * This method must not be called before {@link #onReady()} is called or the call will fail. If
       * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
       * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
       * @param token unique token generated by IMS providers that the platform will use to trigger
       *              callbacks for this message.
       * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
       * {@link SmsMessage#FORMAT_3GPP2}.
       * @param pdu PDUs representing the contents of the message.
       * @throws RuntimeException if called before {@link #onReady()} is triggered.
       */
      public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
          synchronized (mLock) {
              if (mListener == null) {
                  throw new RuntimeException("Feature not ready.");
              }
              try {
                  mListener.onSmsReceived(token, format, pdu);
              } catch (RemoteException e) {
                  Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
                  SmsMessage message = SmsMessage.createFromPdu(pdu, format);
                  if (message != null && message.mWrappedSmsMessage != null) {
                      acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
                              DELIVER_STATUS_ERROR_GENERIC);
                  } else {
                      Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
                      acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
                  }
              }
          }
      }
  
      /**
       * This method should be triggered by the IMS providers to pass the result of the sent message
       * to the platform.
       *
       * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
       * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
       * @param status result of sending the SMS. Valid values are:
       *  {@link #SEND_STATUS_OK},
       *  {@link #SEND_STATUS_ERROR},
       *  {@link #SEND_STATUS_ERROR_RETRY},
       *  {@link #SEND_STATUS_ERROR_FALLBACK},
       * @param reason reason in case status is failure. Valid values are:
       *  {@link SmsManager#RESULT_ERROR_NONE},
       *  {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
       *  {@link SmsManager#RESULT_ERROR_RADIO_OFF},
       *  {@link SmsManager#RESULT_ERROR_NULL_PDU},
       *  {@link SmsManager#RESULT_ERROR_NO_SERVICE},
       *  {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
       *  {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
       *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
       *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
       *  {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
       *  {@link SmsManager#RESULT_NETWORK_REJECT},
       *  {@link SmsManager#RESULT_INVALID_ARGUMENTS},
       *  {@link SmsManager#RESULT_INVALID_STATE},
       *  {@link SmsManager#RESULT_NO_MEMORY},
       *  {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
       *  {@link SmsManager#RESULT_SYSTEM_ERROR},
       *  {@link SmsManager#RESULT_MODEM_ERROR},
       *  {@link SmsManager#RESULT_NETWORK_ERROR},
       *  {@link SmsManager#RESULT_ENCODING_ERROR},
       *  {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
       *  {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
       *  {@link SmsManager#RESULT_INTERNAL_ERROR},
       *  {@link SmsManager#RESULT_NO_RESOURCES},
       *  {@link SmsManager#RESULT_CANCELLED},
       *  {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
       *
       * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
       * connection to the framework is not available. If this happens attempting to send the SMS
       * should be aborted.
       */
      public final void onSendSmsResult(int token, int messageRef,  @SendStatusResult int status,
              int reason) throws RuntimeException {
          synchronized (mLock) {
              if (mListener == null) {
                  throw new RuntimeException("Feature not ready.");
              }
              try {
                  mListener.onSendSmsResult(token, messageRef, status, reason);
              } catch (RemoteException e) {
                  e.rethrowFromSystemServer();
              }
          }
      }
  
      /**
       * Sets the status report of the sent message.
       *
       * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
       * @param messageRef the message reference.
       * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
       * {@link SmsMessage#FORMAT_3GPP2}.
       * @param pdu PDUs representing the content of the status report.
       * @throws RuntimeException if called before {@link #onReady()} is triggered
       */
      public final void onSmsStatusReportReceived(int token, int messageRef, String format,
              byte[] pdu) throws RuntimeException{
          synchronized (mLock) {
              if (mListener == null) {
                  throw new RuntimeException("Feature not ready.");
              }
              try {
                  mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
              } catch (RemoteException e) {
                  Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
                  acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
              }
          }
      }
  
      /**
       * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
       * Provider.
       *
       * @return  the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
       * {@link SmsMessage#FORMAT_3GPP2}.
       */
      public String getSmsFormat() {
        return SmsMessage.FORMAT_3GPP;
      }
  
      /**
       * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
       * Any attempt by this class to access the framework before this method is called will return
       * with a {@link RuntimeException}.
       */
      public void onReady() {
          // Base Implementation - Should be overridden
      }
  }
  