blob: de2c6f778fcd3f866171426f979acc92a3ae47af [file] [log] [blame]
/*
* Copyright (C) 2019 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.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.SmsMessage;
import com.android.internal.util.Preconditions;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* Provides basic structure for platform to connect to the carrier messaging service.
* <p>
* <code>
* CarrierMessagingServiceWrapper carrierMessagingServiceWrapper =
* new CarrierMessagingServiceWrapperImpl();
* if (carrierMessagingServiceWrapper.bindToCarrierMessagingService(context, carrierPackageName)) {
* // wait for onServiceReady callback
* } else {
* // Unable to bind: handle error.
* }
* </code>
* <p> Upon completion {@link #disconnect} should be called to unbind the
* CarrierMessagingService.
* @hide
*/
@SystemApi
public final class CarrierMessagingServiceWrapper implements AutoCloseable {
// Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
// prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
private volatile ICarrierMessagingService mICarrierMessagingService;
private Runnable mOnServiceReadyCallback;
private Executor mServiceReadyCallbackExecutor;
private Context mContext;
/**
* Binds to the carrier messaging service under package {@code carrierPackageName}. This method
* should be called exactly once.
*
* @param context the context
* @param carrierPackageName the carrier package name
* @param executor the executor to run the callback.
* @param onServiceReadyCallback the callback when service becomes ready.
* @return true upon successfully binding to a carrier messaging service, false otherwise
* @hide
*/
@SystemApi
public boolean bindToCarrierMessagingService(@NonNull Context context,
@NonNull String carrierPackageName,
@NonNull @CallbackExecutor Executor executor,
@NonNull Runnable onServiceReadyCallback) {
Preconditions.checkState(mCarrierMessagingServiceConnection == null);
Objects.requireNonNull(context);
Objects.requireNonNull(carrierPackageName);
Objects.requireNonNull(executor);
Objects.requireNonNull(onServiceReadyCallback);
Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
intent.setPackage(carrierPackageName);
mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
mOnServiceReadyCallback = onServiceReadyCallback;
mServiceReadyCallbackExecutor = executor;
mContext = context;
return context.bindService(intent, mCarrierMessagingServiceConnection,
Context.BIND_AUTO_CREATE);
}
/**
* Unbinds the carrier messaging service. This method should be called exactly once.
*
* @hide
*/
@SystemApi
public void disconnect() {
Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
mContext.unbindService(mCarrierMessagingServiceConnection);
mCarrierMessagingServiceConnection = null;
mOnServiceReadyCallback = null;
mServiceReadyCallbackExecutor = null;
}
/**
* Called when connection with service is established.
*
* @param carrierMessagingService the carrier messaing service interface
*/
private void onServiceReady(ICarrierMessagingService carrierMessagingService) {
mICarrierMessagingService = carrierMessagingService;
if (mOnServiceReadyCallback != null && mServiceReadyCallbackExecutor != null) {
final long identity = Binder.clearCallingIdentity();
try {
mServiceReadyCallbackExecutor.execute(mOnServiceReadyCallback);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
/**
* Request the CarrierMessagingService to process an incoming SMS text or data message.
* The service will call callback.onFilterComplete with the filtering result.
*
* @param pdu the PDUs of the message
* @param format the format of the PDUs, typically "3gpp" or "3gpp2".
* See {@link SmsMessage#FORMAT_3GPP} and {@link SmsMessage#FORMAT_3GPP2} for
* more details.
* @param destPort the destination port of a data SMS. It will be -1 for text SMS
* @param subId SMS subscription ID of the SIM
* @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
@SystemApi
public void receiveSms(@NonNull MessagePdu pdu, @NonNull @SmsMessage.Format String format,
int destPort, int subId, @NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.filterSms(pdu, format, destPort, subId,
new CarrierMessagingCallbackInternal(callback, executor));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
/**
* Request sending a new text SMS from the device.
* The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send
* status.
*
* @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 CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}.
* @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
@SystemApi
public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
@CarrierMessagingService.SendRequest int sendSmsFlag,
@NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
Objects.requireNonNull(mICarrierMessagingService);
try {
mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag,
new CarrierMessagingCallbackInternal(callback, executor));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Request sending a new data SMS from the device.
* The service will call {@link ICarrierMessagingCallback#onSendSmsComplete} with the send
* status.
*
* @param data the data to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param destPort port number of the recipient of the message
* @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
* {@link CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}.
* @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
@SystemApi
public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
int destPort, @CarrierMessagingService.SendRequest int sendSmsFlag,
@NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
Objects.requireNonNull(mICarrierMessagingService);
try {
mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort,
sendSmsFlag, new CarrierMessagingCallbackInternal(
callback, executor));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Request sending a new multi-part text SMS from the device.
* The service will call {@link ICarrierMessagingCallback#onSendMultipartSmsComplete}
* with the send status.
*
* @param parts the parts of the multi-part text SMS 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 CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}.
* @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
@SystemApi
public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
@NonNull String destAddress,
@CarrierMessagingService.SendRequest int sendSmsFlag,
@NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
Objects.requireNonNull(mICarrierMessagingService);
try {
mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress,
sendSmsFlag, new CarrierMessagingCallbackInternal(callback, executor));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Request sending a new MMS PDU from the device.
* The service will call {@link ICarrierMessagingCallback#onSendMmsComplete} with the send
* status.
*
* @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 executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
@SystemApi
public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
Objects.requireNonNull(mICarrierMessagingService);
try {
mICarrierMessagingService.sendMms(pduUri, subId, location,
new CarrierMessagingCallbackInternal(callback, executor));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Request downloading a new MMS.
* The service will call {@link ICarrierMessagingCallback#onDownloadMmsComplete} with the
* download status.
*
* @param pduUri 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 executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
@SystemApi
public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
Objects.requireNonNull(mICarrierMessagingService);
try {
mICarrierMessagingService.downloadMms(pduUri, subId, location,
new CarrierMessagingCallbackInternal(callback, executor));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/** @hide */
@Override
public void close() {
disconnect();
}
/**
* A basic {@link ServiceConnection}.
*/
private final class CarrierMessagingServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
onServiceReady(ICarrierMessagingService.Stub.asInterface(service));
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
/**
* Callback wrapper used for response to requests exposed by
* {@link CarrierMessagingServiceWrapper}.
* @hide
*/
@SystemApi
public interface CarrierMessagingCallback {
/**
* Response callback for {@link CarrierMessagingServiceWrapper#receiveSms}.
* @param result a bitmask integer to indicate how the incoming text SMS should be handled
* by the platform. Bits set can be
* {@link CarrierMessagingService#RECEIVE_OPTIONS_DROP} and
* {@link CarrierMessagingService#
* RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE}.
* {@link CarrierMessagingService#onReceiveTextSms}.
*/
default void onReceiveSmsComplete(
@CarrierMessagingService.FilterCompleteResult int result) {
}
/**
* Response callback for {@link CarrierMessagingServiceWrapper#sendTextSms} and
* {@link CarrierMessagingServiceWrapper#sendDataSms}.
* @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
* {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
* and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
* @param messageRef message reference of the just-sent message. This field is applicable
* only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
*/
default void onSendSmsComplete(@CarrierMessagingService.SendResult
int result, int messageRef) {
}
/**
* Response callback for {@link CarrierMessagingServiceWrapper#sendMultipartTextSms}.
* @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
* {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
* and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
* @param messageRefs an array of message references, one for each part of the
* multipart SMS. This field is applicable only if result is
* {@link CarrierMessagingService#SEND_STATUS_OK}.
*/
default void onSendMultipartSmsComplete(@CarrierMessagingService.SendResult
int result, @Nullable int[] messageRefs) {
}
/**
* Response callback for {@link CarrierMessagingServiceWrapper#sendMms}.
* @param result send status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
* {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
* and {@link CarrierMessagingService#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 CarrierMessagingService#SEND_STATUS_OK}.
*/
default void onSendMmsComplete(@CarrierMessagingService.SendResult
int result, @Nullable byte[] sendConfPdu) {
}
/**
* Response callback for {@link CarrierMessagingServiceWrapper#downloadMms}.
* @param result download status, one of {@link CarrierMessagingService#DOWNLOAD_STATUS_OK},
* {@link CarrierMessagingService#DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK},
* and {@link CarrierMessagingService#DOWNLOAD_STATUS_ERROR}.
*/
default void onDownloadMmsComplete(@CarrierMessagingService.DownloadResult
int result) {
}
}
private final class CarrierMessagingCallbackInternal
extends ICarrierMessagingCallback.Stub {
final CarrierMessagingCallback mCarrierMessagingCallback;
final Executor mExecutor;
CarrierMessagingCallbackInternal(CarrierMessagingCallback callback,
final Executor executor) {
mCarrierMessagingCallback = callback;
mExecutor = executor;
}
@Override
public void onFilterComplete(int result) throws RemoteException {
mExecutor.execute(() -> mCarrierMessagingCallback.onReceiveSmsComplete(result));
}
@Override
public void onSendSmsComplete(int result, int messageRef) throws RemoteException {
mExecutor.execute(() -> mCarrierMessagingCallback.onSendSmsComplete(
result, messageRef));
}
@Override
public void onSendMultipartSmsComplete(int result, int[] messageRefs)
throws RemoteException {
mExecutor.execute(() -> mCarrierMessagingCallback.onSendMultipartSmsComplete(
result, messageRefs));
}
@Override
public void onSendMmsComplete(int result, byte[] sendConfPdu) throws RemoteException {
mExecutor.execute(() -> mCarrierMessagingCallback.onSendMmsComplete(
result, sendConfPdu));
}
@Override
public void onDownloadMmsComplete(int result) throws RemoteException {
mExecutor.execute(() -> mCarrierMessagingCallback.onDownloadMmsComplete(result));
}
}
}