VisualVoicemailService.java revision 70a3e828aa6a436ed8a7cb51727959c450e0d426
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.telephony;
18
19import android.annotation.MainThread;
20import android.annotation.SdkConstant;
21import android.annotation.SystemApi;
22import android.app.PendingIntent;
23import android.app.Service;
24import android.content.Context;
25import android.content.Intent;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Message;
30import android.os.Messenger;
31import android.os.RemoteException;
32import android.telecom.PhoneAccountHandle;
33import android.telecom.TelecomManager;
34import android.util.Log;
35
36/**
37 * This service is implemented by dialer apps that wishes to handle OMTP or similar visual
38 * voicemails. Telephony binds to this service when the cell service is first connected, a visual
39 * voicemail SMS has been received, or when a SIM has been removed. Telephony will only bind to the
40 * default dialer for such events (See {@link TelecomManager#getDefaultDialerPackage()}). The
41 * {@link android.service.carrier.CarrierMessagingService} precedes the VisualVoicemailService in
42 * the SMS filtering chain and may intercept the visual voicemail SMS before it reaches this
43 * service.
44 * <p>
45 * To extend this class, The service must be declared in the manifest file with
46 * the {@link android.Manifest.permission#BIND_VISUAL_VOICEMAIL_SERVICE} permission and include an
47 * intent filter with the {@link #SERVICE_INTERFACE} action.
48 * <p>
49 * Below is an example manifest registration for a {@code VisualVoicemailService}.
50 * <pre>
51 * {@code
52 * <service android:name="your.package.YourVisualVoicemailServiceImplementation"
53 *          android:permission="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE">
54 *      <intent-filter>
55 *          <action android:name="android.telephony.VisualVoicemailService"/>
56 *      </intent-filter>
57 * </service>
58 * }
59 * </pre>
60 */
61public abstract class VisualVoicemailService extends Service {
62
63    private static final String TAG = "VvmService";
64
65    /**
66     * The {@link Intent} that must be declared as handled by the service.
67     */
68    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
69    public static final String SERVICE_INTERFACE = "android.telephony.VisualVoicemailService";
70
71    /**
72     * @hide
73     */
74    public static final int MSG_ON_CELL_SERVICE_CONNECTED = 1;
75    /**
76     * @hide
77     */
78    public static final int MSG_ON_SMS_RECEIVED = 2;
79    /**
80     * @hide
81     */
82    public static final int MSG_ON_SIM_REMOVED = 3;
83    /**
84     * @hide
85     */
86    public static final int MSG_TASK_ENDED = 4;
87    /**
88     * @hide
89     */
90    public static final int MSG_TASK_STOPPED = 5;
91
92    /**
93     * @hide
94     */
95    public static final String DATA_PHONE_ACCOUNT_HANDLE = "data_phone_account_handle";
96    /**
97     * @hide
98     */
99    public static final String DATA_SMS = "data_sms";
100
101    /**
102     * Represents a visual voicemail event which needs to be handled. While the task is being
103     * processed telephony will hold a wakelock for the VisualVoicemailService. The service can
104     * unblock the main thread and pass the task to a worker thread. Once the task is finished,
105     * {@link VisualVoicemailTask#finish()} should be called to signal telephony to release the
106     * resources. Telephony will call {@link VisualVoicemailService#onStopped(VisualVoicemailTask)}
107     * when the task is going to be terminated before completion.
108     *
109     * @see #onCellServiceConnected(VisualVoicemailTask, PhoneAccountHandle)
110     * @see #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)
111     * @see #onSimRemoved(VisualVoicemailTask, PhoneAccountHandle)
112     * @see #onStopped(VisualVoicemailTask)
113     */
114    public static class VisualVoicemailTask {
115
116        private final int mTaskId;
117        private final Messenger mReplyTo;
118
119        private VisualVoicemailTask(Messenger replyTo, int taskId) {
120            mTaskId = taskId;
121            mReplyTo = replyTo;
122        }
123
124        /**
125         * Call to signal telephony the task has completed. Must be called for every task.
126         */
127        public final void finish() {
128            Message message = Message.obtain();
129            try {
130                message.what = MSG_TASK_ENDED;
131                message.arg1 = mTaskId;
132                mReplyTo.send(message);
133            } catch (RemoteException e) {
134                Log.e(TAG,
135                        "Cannot send MSG_TASK_ENDED, remote handler no longer exist");
136            }
137        }
138
139        @Override
140        public boolean equals(Object obj) {
141            if (!(obj instanceof VisualVoicemailTask)) {
142                return false;
143            }
144            return mTaskId == ((VisualVoicemailTask) obj).mTaskId;
145        }
146
147        @Override
148        public int hashCode() {
149            return mTaskId;
150        }
151    }
152
153    /**
154     * Handles messages sent by telephony.
155     */
156    private final Messenger mMessenger = new Messenger(new Handler() {
157        @Override
158        public void handleMessage(final Message msg) {
159            final PhoneAccountHandle handle = msg.getData()
160                    .getParcelable(DATA_PHONE_ACCOUNT_HANDLE);
161            VisualVoicemailTask task = new VisualVoicemailTask(msg.replyTo, msg.arg1);
162            switch (msg.what) {
163                case MSG_ON_CELL_SERVICE_CONNECTED:
164                    onCellServiceConnected(task, handle);
165                    break;
166                case MSG_ON_SMS_RECEIVED:
167                    VisualVoicemailSms sms = msg.getData().getParcelable(DATA_SMS);
168                    onSmsReceived(task, sms);
169                    break;
170                case MSG_ON_SIM_REMOVED:
171                    onSimRemoved(task, handle);
172                    break;
173                case MSG_TASK_STOPPED:
174                    onStopped(task);
175                    break;
176                default:
177                    super.handleMessage(msg);
178                    break;
179            }
180        }
181    });
182
183    @Override
184    public IBinder onBind(Intent intent) {
185        return mMessenger.getBinder();
186    }
187
188    /**
189     * Called when the cellular service is connected on a {@link PhoneAccountHandle} for the first
190     * time, or when the carrier config has changed. It will not be called when the signal is lost
191     * then restored.
192     *
193     * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
194     * called when the task is completed.
195     * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
196     */
197    @MainThread
198    public abstract void onCellServiceConnected(VisualVoicemailTask task,
199                                                PhoneAccountHandle phoneAccountHandle);
200
201    /**
202     * Called when a SMS matching the {@link VisualVoicemailSmsFilterSettings} set by
203     * {@link TelephonyManager#setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings)
204     * }
205     * is received.
206     *
207     * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
208     * called when the task is completed.
209     * @param sms The content of the received SMS.
210     */
211    @MainThread
212    public abstract void onSmsReceived(VisualVoicemailTask task,
213                                       VisualVoicemailSms sms);
214
215    /**
216     * Called when a SIM is removed.
217     *
218     * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
219     * called when the task is completed.
220     * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
221     */
222    @MainThread
223    public abstract void onSimRemoved(VisualVoicemailTask task,
224                                      PhoneAccountHandle phoneAccountHandle);
225
226    /**
227     * Called before the system is about to terminate a task. The service should persist any
228     * necessary data and call finish on the task immediately.
229     */
230    @MainThread
231    public abstract void onStopped(VisualVoicemailTask task);
232
233    /**
234     * Set the visual voicemail SMS filter settings for the VisualVoicemailService.
235     * {@link #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be called when
236     * a SMS matching the settings is received. The caller should have
237     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} and implements a
238     * VisualVoicemailService.
239     * <p>
240     * <p>Requires Permission:
241     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
242     *
243     * @param phoneAccountHandle The account to apply the settings to.
244     * @param settings The settings for the filter, or {@code null} to disable the filter.
245     *
246     * @hide
247     */
248    @SystemApi
249    public static final void setSmsFilterSettings(Context context,
250            PhoneAccountHandle phoneAccountHandle,
251            VisualVoicemailSmsFilterSettings settings) {
252        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
253        int subId = getSubId(context, phoneAccountHandle);
254        if (settings == null) {
255            telephonyManager.disableVisualVoicemailSmsFilter(subId);
256        } else {
257            telephonyManager.enableVisualVoicemailSmsFilter(subId, settings);
258        }
259    }
260
261    /**
262     * Send a visual voicemail SMS. The caller must be the current default dialer.
263     * <p>
264     * <p>Requires Permission:
265     * {@link android.Manifest.permission#SEND_SMS SEND_SMS}
266     *
267     * @param phoneAccountHandle The account to send the SMS with.
268     * @param number The destination number.
269     * @param port The destination port for data SMS, or 0 for text SMS.
270     * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
271     * @param sentIntent The sent intent passed to the {@link SmsManager}
272     *
273     * @throws SecurityException if the caller is not the current default dialer
274     *
275     * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
276     * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
277     *
278     * @hide
279     */
280    @SystemApi
281    public static final void sendVisualVoicemailSms(Context context,
282            PhoneAccountHandle phoneAccountHandle, String number,
283            short port, String text, PendingIntent sentIntent) {
284        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
285        telephonyManager.sendVisualVoicemailSmsForSubscriber(getSubId(context, phoneAccountHandle),
286                number, port, text, sentIntent);
287    }
288
289    private static int getSubId(Context context, PhoneAccountHandle phoneAccountHandle) {
290        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
291        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
292        return telephonyManager
293                .getSubIdForPhoneAccount(telecomManager.getPhoneAccount(phoneAccountHandle));
294    }
295
296}
297