/* * Copyright (C) 2014 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.service.carrier; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.app.Service; import android.content.Intent; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import java.util.List; /** * A service that receives calls from the system when new SMS and MMS are * sent or received. * <p>To extend this class, you must declare the service in your manifest file with * the {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> * <pre> * <service android:name=".MyMessagingService" * android:label="@string/service_name" * android:permission="android.permission.BIND_CARRIER_SERVICES"> * <intent-filter> * <action android:name="android.service.carrier.CarrierMessagingService" /> * </intent-filter> * </service></pre> */ public abstract class CarrierMessagingService extends Service { /** * The {@link android.content.Intent} that must be declared as handled by the service. */ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String SERVICE_INTERFACE = "android.service.carrier.CarrierMessagingService"; /** * The default bitmask value passed to the callback of {@link #onReceiveTextSms} with all * {@code RECEIVE_OPTIONS_x} flags cleared to indicate that the message should be kept and a * new message notification should be shown. * * @see #RECEIVE_OPTIONS_DROP * @see #RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE */ public static final int RECEIVE_OPTIONS_DEFAULT = 0; /** * Used to set the flag in the bitmask passed to the callback of {@link #onReceiveTextSms} to * indicate that the inbound SMS should be dropped. */ public static final int RECEIVE_OPTIONS_DROP = 0x1; /** * Used to set the flag in the bitmask passed to the callback of {@link #onReceiveTextSms} to * indicate that a new message notification should not be shown to the user when the * credential-encrypted storage of the device is not available before the user unlocks the * phone. It is only applicable to devices that support file-based encryption. */ public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE = 0x2; /** * Indicates that an SMS or MMS message was successfully sent. */ public static final int SEND_STATUS_OK = 0; /** * SMS/MMS sending failed. We should retry via the carrier network. */ public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1; /** * SMS/MMS sending failed. We should not retry via the carrier network. */ public static final int SEND_STATUS_ERROR = 2; /** * Successfully downloaded an MMS message. */ public static final int DOWNLOAD_STATUS_OK = 0; /** * MMS downloading failed. We should retry via the carrier network. */ public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; /** * MMS downloading failed. We should not retry via the carrier network. */ public static final int DOWNLOAD_STATUS_ERROR = 2; /** * Flag to request SMS delivery status report. */ public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper(); /** * Override this method to filter inbound SMS messages. * * @param pdu the PDUs of the message * @param format the format of the PDUs, typically "3gpp" or "3gpp2" * @param destPort the destination port of a binary SMS, this will be -1 for text SMS * @param subId SMS subscription ID of the SIM * @param callback result callback. Call with {@code true} to keep an inbound SMS message and * deliver to SMS apps, and {@code false} to drop the message. * @deprecated Use {@link #onReceiveTextSms} instead. */ @Deprecated public void onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort, int subId, @NonNull ResultCallback<Boolean> callback) { // optional try { callback.onReceiveResult(true); } catch (RemoteException ex) { } } /** * Override this method to filter inbound SMS messages. * * <p>This method will be called once for every incoming text SMS. You can invoke the callback * with a bitmask to tell the platform how to handle the SMS. For a SMS received on a * file-based encryption capable device while the credential-encrypted storage is not available, * this method will be called for the second time when the credential-encrypted storage becomes * available after the user unlocks the phone, if the bit {@link #RECEIVE_OPTIONS_DROP} is not * set when invoking the callback. * * @param pdu the PDUs of the message * @param format the format of the PDUs, typically "3gpp" or "3gpp2" * @param destPort the destination port of a binary SMS, this will be -1 for text SMS * @param subId SMS subscription ID of the SIM * @param callback result callback. Call with a bitmask integer to indicate how the incoming * text SMS should be handled by the platform. Use {@link #RECEIVE_OPTIONS_DROP} and * {@link #RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE} * to set the flags in the bitmask. */ public void onReceiveTextSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort, int subId, @NonNull final ResultCallback<Integer> callback) { onFilterSms(pdu, format, destPort, subId, new ResultCallback<Boolean>() { @Override public void onReceiveResult(Boolean result) throws RemoteException { callback.onReceiveResult(result ? RECEIVE_OPTIONS_DEFAULT : RECEIVE_OPTIONS_DROP | RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE); } }); } /** * Override this method to intercept text SMSs sent from the device. * @deprecated Override {@link #onSendTextSms} below instead. * * @param text the text to send * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message * @param callback result callback. Call with a {@link SendSmsResult}. */ @Deprecated public void onSendTextSms( @NonNull String text, int subId, @NonNull String destAddress, @NonNull ResultCallback<SendSmsResult> callback) { // optional try { callback.onReceiveResult(new SendSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 0)); } catch (RemoteException ex) { } } /** * Override this method to intercept text SMSs sent from the device. * * @param text the text to send * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}. * @param callback result callback. Call with a {@link SendSmsResult}. */ public void onSendTextSms( @NonNull String text, int subId, @NonNull String destAddress, int sendSmsFlag, @NonNull ResultCallback<SendSmsResult> callback) { // optional onSendTextSms(text, subId, destAddress, callback); } /** * Override this method to intercept binary SMSs sent from the device. * @deprecated Override {@link #onSendDataSms} below instead. * * @param data the binary content * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message * @param destPort the destination port * @param callback result callback. Call with a {@link SendSmsResult}. */ @Deprecated public void onSendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress, int destPort, @NonNull ResultCallback<SendSmsResult> callback) { // optional try { callback.onReceiveResult(new SendSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 0)); } catch (RemoteException ex) { } } /** * Override this method to intercept binary SMSs sent from the device. * * @param data the binary content * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message * @param destPort the destination port * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}. * @param callback result callback. Call with a {@link SendSmsResult}. */ public void onSendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress, int destPort, int sendSmsFlag, @NonNull ResultCallback<SendSmsResult> callback) { // optional onSendDataSms(data, subId, destAddress, destPort, callback); } /** * Override this method to intercept long SMSs sent from the device. * @deprecated Override {@link #onSendMultipartTextSms} below instead. * * @param parts a {@link List} of the message parts * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message * @param callback result callback. Call with a {@link SendMultipartSmsResult}. */ @Deprecated public void onSendMultipartTextSms(@NonNull List<String> parts, int subId, @NonNull String destAddress, @NonNull ResultCallback<SendMultipartSmsResult> callback) { // optional try { callback.onReceiveResult( new SendMultipartSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null)); } catch (RemoteException ex) { } } /** * Override this method to intercept long SMSs sent from the device. * * @param parts a {@link List} of the message parts * @param subId SMS subscription ID of the SIM * @param destAddress phone number of the recipient of the message * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}. * @param callback result callback. Call with a {@link SendMultipartSmsResult}. */ public void onSendMultipartTextSms(@NonNull List<String> parts, int subId, @NonNull String destAddress, int sendSmsFlag, @NonNull ResultCallback<SendMultipartSmsResult> callback) { // optional onSendMultipartTextSms(parts, subId, destAddress, callback); } /** * Override this method to intercept MMSs sent from the device. * * @param pduUri the content provider URI of the PDU to send * @param subId SMS subscription ID of the SIM * @param location the optional URI to send this MMS PDU. If this is {code null}, * the PDU should be sent to the default MMSC URL. * @param callback result callback. Call with a {@link SendMmsResult}. */ public void onSendMms(@NonNull Uri pduUri, int subId, @Nullable Uri location, @NonNull ResultCallback<SendMmsResult> callback) { // optional try { callback.onReceiveResult(new SendMmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null)); } catch (RemoteException ex) { } } /** * Override this method to download MMSs received. * * @param contentUri the content provider URI of the PDU to be downloaded. * @param subId SMS subscription ID of the SIM * @param location the URI of the message to be downloaded. * @param callback result callback. Call with a status code which is one of * {@link #DOWNLOAD_STATUS_OK}, * {@link #DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK}, or {@link #DOWNLOAD_STATUS_ERROR}. */ public void onDownloadMms(@NonNull Uri contentUri, int subId, @NonNull Uri location, @NonNull ResultCallback<Integer> callback) { // optional try { callback.onReceiveResult(DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK); } catch (RemoteException ex) { } } @Override public @Nullable IBinder onBind(@NonNull Intent intent) { if (!SERVICE_INTERFACE.equals(intent.getAction())) { return null; } return mWrapper; } /** * The result of sending an MMS. */ public static final class SendMmsResult { private int mSendStatus; private byte[] mSendConfPdu; /** * Constructs a SendMmsResult with the MMS send result, and the SendConf PDU. * * @param sendStatus send status, one of {@link #SEND_STATUS_OK}, * {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and * {@link #SEND_STATUS_ERROR} * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message * was sent. sendConfPdu is ignored if the {@code result} is not * {@link #SEND_STATUS_OK}. */ public SendMmsResult(int sendStatus, @Nullable byte[] sendConfPdu) { mSendStatus = sendStatus; mSendConfPdu = sendConfPdu; } /** * Returns the send status of the just-sent MMS. * * @return the send status which is one of {@link #SEND_STATUS_OK}, * {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR} */ public int getSendStatus() { return mSendStatus; } /** * Returns the SendConf PDU, which confirms that the message was sent. * * @return the SendConf PDU */ public @Nullable byte[] getSendConfPdu() { return mSendConfPdu; } } /** * The result of sending an SMS. */ public static final class SendSmsResult { private final int mSendStatus; private final int mMessageRef; /** * Constructs a SendSmsResult with the send status and message reference for the * just-sent SMS. * * @param sendStatus send status, one of {@link #SEND_STATUS_OK}, * {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}. * @param messageRef message reference of the just-sent SMS. This field is applicable only * if send status is {@link #SEND_STATUS_OK}. */ public SendSmsResult(int sendStatus, int messageRef) { mSendStatus = sendStatus; mMessageRef = messageRef; } /** * Returns the message reference of the just-sent SMS. * * @return the message reference */ public int getMessageRef() { return mMessageRef; } /** * Returns the send status of the just-sent SMS. * * @return the send status */ public int getSendStatus() { return mSendStatus; } } /** * The result of sending a multipart SMS. */ public static final class SendMultipartSmsResult { private final int mSendStatus; private final int[] mMessageRefs; /** * Constructs a SendMultipartSmsResult with the send status and message references for the * just-sent multipart SMS. * * @param sendStatus send status, one of {@link #SEND_STATUS_OK}, * {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}. * @param messageRefs an array of message references, one for each part of the * multipart SMS. This field is applicable only if send status is * {@link #SEND_STATUS_OK}. */ public SendMultipartSmsResult(int sendStatus, @Nullable int[] messageRefs) { mSendStatus = sendStatus; mMessageRefs = messageRefs; } /** * Returns the message references of the just-sent multipart SMS. * * @return the message references, one for each part of the multipart SMS */ public @Nullable int[] getMessageRefs() { return mMessageRefs; } /** * Returns the send status of the just-sent SMS. * * @return the send status */ public int getSendStatus() { return mSendStatus; } } /** * A callback interface used to provide results asynchronously. */ public interface ResultCallback<T> { /** * Invoked when the result is available. * * @param result the result */ public void onReceiveResult(@NonNull T result) throws RemoteException; }; /** * A wrapper around ICarrierMessagingService to enable the carrier messaging app to implement * methods it cares about in the {@link ICarrierMessagingService} interface. */ private class ICarrierMessagingWrapper extends ICarrierMessagingService.Stub { @Override public void filterSms(MessagePdu pdu, String format, int destPort, int subId, final ICarrierMessagingCallback callback) { onReceiveTextSms(pdu, format, destPort, subId, new ResultCallback<Integer>() { @Override public void onReceiveResult(Integer options) throws RemoteException { callback.onFilterComplete(options); } }); } @Override public void sendTextSms(String text, int subId, String destAddress, int sendSmsFlag, final ICarrierMessagingCallback callback) { onSendTextSms(text, subId, destAddress, sendSmsFlag, new ResultCallback<SendSmsResult>() { @Override public void onReceiveResult(final SendSmsResult result) throws RemoteException { callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef()); } }); } @Override public void sendDataSms(byte[] data, int subId, String destAddress, int destPort, int sendSmsFlag, final ICarrierMessagingCallback callback) { onSendDataSms(data, subId, destAddress, destPort, sendSmsFlag, new ResultCallback<SendSmsResult>() { @Override public void onReceiveResult(final SendSmsResult result) throws RemoteException { callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef()); } }); } @Override public void sendMultipartTextSms(List<String> parts, int subId, String destAddress, int sendSmsFlag, final ICarrierMessagingCallback callback) { onSendMultipartTextSms(parts, subId, destAddress, sendSmsFlag, new ResultCallback<SendMultipartSmsResult>() { @Override public void onReceiveResult(final SendMultipartSmsResult result) throws RemoteException { callback.onSendMultipartSmsComplete( result.getSendStatus(), result.getMessageRefs()); } }); } @Override public void sendMms(Uri pduUri, int subId, Uri location, final ICarrierMessagingCallback callback) { onSendMms(pduUri, subId, location, new ResultCallback<SendMmsResult>() { @Override public void onReceiveResult(final SendMmsResult result) throws RemoteException { callback.onSendMmsComplete(result.getSendStatus(), result.getSendConfPdu()); } }); } @Override public void downloadMms(Uri pduUri, int subId, Uri location, final ICarrierMessagingCallback callback) { onDownloadMms(pduUri, subId, location, new ResultCallback<Integer>() { @Override public void onReceiveResult(Integer result) throws RemoteException { callback.onDownloadMmsComplete(result); } }); } } }