content: /*
   * Copyright (C) 2016 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;
  
  import android.annotation.MainThread;
  import android.annotation.SdkConstant;
  import android.annotation.SystemApi;
  import android.app.PendingIntent;
  import android.app.Service;
  import android.content.Context;
  import android.content.Intent;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.IBinder;
  import android.os.Message;
  import android.os.Messenger;
  import android.os.RemoteException;
  import android.telecom.PhoneAccountHandle;
  import android.telecom.TelecomManager;
  import android.util.Log;
  
  /**
   * This service is implemented by dialer apps that wishes to handle OMTP or similar visual
   * voicemails. Telephony binds to this service when the cell service is first connected, a visual
   * voicemail SMS has been received, or when a SIM has been removed. Telephony will only bind to the
   * default dialer for such events (See {@link TelecomManager#getDefaultDialerPackage()}). The
   * {@link android.service.carrier.CarrierMessagingService} precedes the VisualVoicemailService in
   * the SMS filtering chain and may intercept the visual voicemail SMS before it reaches this
   * service.
   * <p>
   * To extend this class, The service must be declared in the manifest file with
   * the {@link android.Manifest.permission#BIND_VISUAL_VOICEMAIL_SERVICE} permission and include an
   * intent filter with the {@link #SERVICE_INTERFACE} action.
   * <p>
   * Below is an example manifest registration for a {@code VisualVoicemailService}.
   * <pre>
   * {@code
   * <service android:name="your.package.YourVisualVoicemailServiceImplementation"
   *          android:permission="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE">
   *      <intent-filter>
   *          <action android:name="android.telephony.VisualVoicemailService"/>
   *      </intent-filter>
   * </service>
   * }
   * </pre>
   */
  public abstract class VisualVoicemailService extends Service {
  
      private static final String TAG = "VvmService";
  
      /**
       * The {@link Intent} that must be declared as handled by the service.
       */
      @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
      public static final String SERVICE_INTERFACE = "android.telephony.VisualVoicemailService";
  
      /**
       * @hide
       */
      public static final int MSG_ON_CELL_SERVICE_CONNECTED = 1;
      /**
       * @hide
       */
      public static final int MSG_ON_SMS_RECEIVED = 2;
      /**
       * @hide
       */
      public static final int MSG_ON_SIM_REMOVED = 3;
      /**
       * @hide
       */
      public static final int MSG_TASK_ENDED = 4;
      /**
       * @hide
       */
      public static final int MSG_TASK_STOPPED = 5;
  
      /**
       * @hide
       */
      public static final String DATA_PHONE_ACCOUNT_HANDLE = "data_phone_account_handle";
      /**
       * @hide
       */
      public static final String DATA_SMS = "data_sms";
  
      /**
       * Represents a visual voicemail event which needs to be handled. While the task is being
       * processed telephony will hold a wakelock for the VisualVoicemailService. The service can
       * unblock the main thread and pass the task to a worker thread. Once the task is finished,
       * {@link VisualVoicemailTask#finish()} should be called to signal telephony to release the
       * resources. Telephony will call {@link VisualVoicemailService#onStopped(VisualVoicemailTask)}
       * when the task is going to be terminated before completion.
       *
       * @see #onCellServiceConnected(VisualVoicemailTask, PhoneAccountHandle)
       * @see #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)
       * @see #onSimRemoved(VisualVoicemailTask, PhoneAccountHandle)
       * @see #onStopped(VisualVoicemailTask)
       */
      public static class VisualVoicemailTask {
  
          private final int mTaskId;
          private final Messenger mReplyTo;
  
          private VisualVoicemailTask(Messenger replyTo, int taskId) {
              mTaskId = taskId;
              mReplyTo = replyTo;
          }
  
          /**
           * Call to signal telephony the task has completed. Must be called for every task.
           */
          public final void finish() {
              Message message = Message.obtain();
              try {
                  message.what = MSG_TASK_ENDED;
                  message.arg1 = mTaskId;
                  mReplyTo.send(message);
              } catch (RemoteException e) {
                  Log.e(TAG,
                          "Cannot send MSG_TASK_ENDED, remote handler no longer exist");
              }
          }
  
          @Override
          public boolean equals(Object obj) {
              if (!(obj instanceof VisualVoicemailTask)) {
                  return false;
              }
              return mTaskId == ((VisualVoicemailTask) obj).mTaskId;
          }
  
          @Override
          public int hashCode() {
              return mTaskId;
          }
      }
  
      /**
       * Handles messages sent by telephony.
       */
      private final Messenger mMessenger = new Messenger(new Handler() {
          @Override
          public void handleMessage(final Message msg) {
              final PhoneAccountHandle handle = msg.getData()
                      .getParcelable(DATA_PHONE_ACCOUNT_HANDLE);
              VisualVoicemailTask task = new VisualVoicemailTask(msg.replyTo, msg.arg1);
              switch (msg.what) {
                  case MSG_ON_CELL_SERVICE_CONNECTED:
                      onCellServiceConnected(task, handle);
                      break;
                  case MSG_ON_SMS_RECEIVED:
                      VisualVoicemailSms sms = msg.getData().getParcelable(DATA_SMS);
                      onSmsReceived(task, sms);
                      break;
                  case MSG_ON_SIM_REMOVED:
                      onSimRemoved(task, handle);
                      break;
                  case MSG_TASK_STOPPED:
                      onStopped(task);
                      break;
                  default:
                      super.handleMessage(msg);
                      break;
              }
          }
      });
  
      @Override
      public IBinder onBind(Intent intent) {
          return mMessenger.getBinder();
      }
  
      /**
       * Called when the cellular service is connected on a {@link PhoneAccountHandle} for the first
       * time, or when the carrier config has changed. It will not be called when the signal is lost
       * then restored.
       *
       * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
       * called when the task is completed.
       * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
       */
      @MainThread
      public abstract void onCellServiceConnected(VisualVoicemailTask task,
                                                  PhoneAccountHandle phoneAccountHandle);
  
      /**
       * Called when a SMS matching the {@link VisualVoicemailSmsFilterSettings} set by
       * {@link TelephonyManager#setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings)
       * }
       * is received.
       *
       * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
       * called when the task is completed.
       * @param sms The content of the received SMS.
       */
      @MainThread
      public abstract void onSmsReceived(VisualVoicemailTask task,
                                         VisualVoicemailSms sms);
  
      /**
       * Called when a SIM is removed.
       *
       * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
       * called when the task is completed.
       * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
       */
      @MainThread
      public abstract void onSimRemoved(VisualVoicemailTask task,
                                        PhoneAccountHandle phoneAccountHandle);
  
      /**
       * Called before the system is about to terminate a task. The service should persist any
       * necessary data and call finish on the task immediately.
       */
      @MainThread
      public abstract void onStopped(VisualVoicemailTask task);
  
      /**
       * Set the visual voicemail SMS filter settings for the VisualVoicemailService.
       * {@link #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be called when
       * a SMS matching the settings is received. The caller should have
       * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} and implements a
       * VisualVoicemailService.
       * <p>
       * <p>Requires Permission:
       * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
       *
       * @param phoneAccountHandle The account to apply the settings to.
       * @param settings The settings for the filter, or {@code null} to disable the filter.
       *
       * @hide
       */
      @SystemApi
      public static final void setSmsFilterSettings(Context context,
              PhoneAccountHandle phoneAccountHandle,
              VisualVoicemailSmsFilterSettings settings) {
          TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
          int subId = getSubId(context, phoneAccountHandle);
          if (settings == null) {
              telephonyManager.disableVisualVoicemailSmsFilter(subId);
          } else {
              telephonyManager.enableVisualVoicemailSmsFilter(subId, settings);
          }
      }
  
      /**
       * Send a visual voicemail SMS. The caller must be the current default dialer.
       * <p>
       * <p>Requires Permission:
       * {@link android.Manifest.permission#SEND_SMS SEND_SMS}
       *
       * @param phoneAccountHandle The account to send the SMS with.
       * @param number The destination number.
       * @param port The destination port for data SMS, or 0 for text SMS.
       * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
       * @param sentIntent The sent intent passed to the {@link SmsManager}
       *
       * @throws SecurityException if the caller is not the current default dialer
       *
       * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
       * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
       *
       * @hide
       */
      @SystemApi
      public static final void sendVisualVoicemailSms(Context context,
              PhoneAccountHandle phoneAccountHandle, String number,
              short port, String text, PendingIntent sentIntent) {
          TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
          telephonyManager.sendVisualVoicemailSmsForSubscriber(getSubId(context, phoneAccountHandle),
                  number, port, text, sentIntent);
      }
  
      private static int getSubId(Context context, PhoneAccountHandle phoneAccountHandle) {
          TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
          TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
          return telephonyManager
                  .getSubIdForPhoneAccount(telecomManager.getPhoneAccount(phoneAccountHandle));
      }
  
  }
  