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