/* * 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. *
* 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. *
* Below is an example manifest registration for a {@code VisualVoicemailService}. *
* {@code
*
*
*
*
*
* }
*
*/
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, android.telecom.PhoneAccountHandle.class);
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, android.telephony.VisualVoicemailSms.class);
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.
* *
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. *
*
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)); } }