| /* |
| * 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)); |
| } |
| |
| } |