Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 49a78d91 authored by Mohamed Abdalkader's avatar Mohamed Abdalkader Committed by Android (Google) Code Review
Browse files

Merge "Implement ImsSmsDispatcher"

parents b54a27ae e2db420f
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -473,7 +473,17 @@ public class IccSmsInterfaceManager {
                "\n format=" + format +
                "\n receivedIntent=" + receivedIntent);
        }
        mDispatchersController.injectSmsPdu(pdu, format, receivedIntent);
        mDispatchersController.injectSmsPdu(pdu, format,
                result -> {
                    if (receivedIntent != null) {
                        try {
                            receivedIntent.send(result);
                        } catch (PendingIntent.CanceledException e) {
                            Rlog.d(LOG_TAG, "receivedIntent cancelled.");
                        }
                    }
                }
        );
    }

    /**
+164 −6
Original line number Diff line number Diff line
@@ -16,25 +16,134 @@

package com.android.internal.telephony;

import android.app.Activity;
import android.os.RemoteException;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.provider.Telephony.Sms;
import android.content.Intent;
import android.telephony.Rlog;

import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.ims.internal.IImsSmsListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.util.SMSDispatcherUtil;
import com.android.internal.telephony.gsm.SmsMessage;
import android.telephony.ims.internal.SmsImplBase;
import android.telephony.ims.internal.SmsImplBase.SendStatusResult;
import android.telephony.ims.internal.SmsImplBase.StatusReportResult;
import android.provider.Telephony.Sms.Intents;
import android.util.Pair;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import libcore.util.Nullable;

/**
 * Responsible for communications with {@link com.android.ims.ImsManager} to send/receive messages
 * over IMS.
 *
 * TODO: implement
 */
public class ImsSmsDispatcher extends SMSDispatcher {
    @VisibleForTesting
    public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>();
    @VisibleForTesting
    public AtomicInteger mNextToken = new AtomicInteger();

    private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
        @Override
        public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
                int reason) throws RemoteException {
            SmsTracker tracker = mTrackers.get(token);
            if (tracker == null) {
                throw new IllegalArgumentException("Invalid token.");
            }
            switch(reason) {
                case SmsImplBase.SEND_STATUS_OK:
                    tracker.onSent(mContext);
                    break;
                case SmsImplBase.SEND_STATUS_ERROR:
                    tracker.onFailed(mContext, reason, 0 /* errorCode */);
                    mTrackers.remove(token);
                    break;
                case SmsImplBase.SEND_STATUS_ERROR_RETRY:
                    tracker.mRetryCount += 1;
                    sendSms(tracker);
                    break;
                case SmsImplBase.SEND_STATUS_ERROR_FALLBACK:
                    fallbackToPstn(token, tracker);
                    break;
                default:
            }
        }

    /** {@inheritDoc} */
    protected ImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
        @Override
        public void onSmsStatusReportReceived(int token, int messageRef, String format, byte[] pdu)
                throws RemoteException {
            Rlog.d(TAG, "Status report received.");
            SmsTracker tracker = mTrackers.get(token);
            if (tracker == null) {
                throw new RemoteException("Invalid token.");
            }
            Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
                    tracker, format, pdu);
            Rlog.d(TAG, "Status report handle result, success: " + result.first +
                    "complete: " + result.second);
            try {
                getImsManager().acknowledgeSmsReport(
                        token,
                        messageRef,
                        result.first ? SmsImplBase.STATUS_REPORT_STATUS_OK
                                : SmsImplBase.STATUS_REPORT_STATUS_ERROR);
            } catch (ImsException e) {
                Rlog.e(TAG, "Failed to acknowledgeSmsReport(). Error: "
                        + e.getMessage());
            }
            if (result.second) {
                mTrackers.remove(token);
            }
        }

        @Override
        public void onSmsReceived(int token, String format, byte[] pdu)
                throws RemoteException {
            Rlog.d(TAG, "SMS received.");
            mSmsDispatchersController.injectSmsPdu(pdu, format, result -> {
                Rlog.d(TAG, "SMS handled result: " + result);
                try {
                    getImsManager().acknowledgeSms(token,
                            0,
                            result == Intents.RESULT_SMS_HANDLED
                                    ? SmsImplBase.STATUS_REPORT_STATUS_OK
                                    : SmsImplBase.DELIVER_STATUS_ERROR);
                } catch (ImsException e) {
                    Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
                }
            });
        }
    };

    public ImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
        super(phone, smsDispatchersController);
    }

    public boolean isAvailable() {
        // TODO: implement
        return false;
    }

    @Override
    protected String getFormat() {
        return null;
        try {
            return getImsManager().getSmsFormat();
        } catch (ImsException e) {
            Rlog.e(TAG, "Failed to get sms format. Error: " + e.getMessage());
            return SmsConstants.FORMAT_UNKNOWN;
        }
    }

    @Override
@@ -62,7 +171,56 @@ public class ImsSmsDispatcher extends SMSDispatcher {
    }

    @Override
    protected void sendSms(SmsTracker tracker) {
    public void sendSms(SmsTracker tracker) {
        Rlog.d(TAG, "sendSms: "
                + " mRetryCount=" + tracker.mRetryCount
                + " mMessageRef=" + tracker.mMessageRef
                + " SS=" + mPhone.getServiceState().getState());

        HashMap<String, Object> map = tracker.getData();

        byte[] pdu = (byte[]) map.get(MAP_KEY_PDU);
        byte smsc[] = (byte[]) map.get(MAP_KEY_SMSC);
        boolean isRetry = tracker.mRetryCount > 0;

        if (SmsConstants.FORMAT_3GPP.equals(getFormat()) && tracker.mRetryCount > 0) {
            // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
            //   TP-RD (bit 2) is 1 for retry
            //   and TP-MR is set to previously failed sms TP-MR
            if (((0x01 & pdu[0]) == 0x01)) {
                pdu[0] |= 0x04; // TP-RD
                pdu[1] = (byte) tracker.mMessageRef; // TP-MR
            }
        }

        int token = mNextToken.incrementAndGet();
        mTrackers.put(token, tracker);
        try {
            getImsManager().sendSms(
                    token,
                    tracker.mMessageRef,
                    getFormat(),
                    smsc != null ? new String(smsc) : null,
                    isRetry,
                    pdu);
        } catch (ImsException e) {
            Rlog.e(TAG, "sendSms failed. Falling back to PSTN. Error: " + e.getMessage());
            fallbackToPstn(token, tracker);
        }
    }

    private ImsManager getImsManager() {
        return ImsManager.getInstance(mContext, mPhone.getPhoneId());
    }

    @VisibleForTesting
    public void fallbackToPstn(int token, SmsTracker tracker) {
        mSmsDispatchersController.sendRetrySms(tracker);
        mTrackers.remove(token);
    }

    @Override
    protected boolean isCdmaMo() {
        return mSmsDispatchersController.isCdmaFormat(getFormat());
    }
}
+4 −6
Original line number Diff line number Diff line
@@ -603,9 +603,9 @@ public abstract class InboundSmsHandler extends StateMachine {
     */
    private void handleInjectSms(AsyncResult ar) {
        int result;
        PendingIntent receivedIntent = null;
        SmsDispatchersController.SmsInjectionCallback callback = null;
        try {
            receivedIntent = (PendingIntent) ar.userObj;
            callback = (SmsDispatchersController.SmsInjectionCallback) ar.userObj;
            SmsMessage sms = (SmsMessage) ar.result;
            if (sms == null) {
              result = Intents.RESULT_SMS_GENERIC_ERROR;
@@ -617,10 +617,8 @@ public abstract class InboundSmsHandler extends StateMachine {
            result = Intents.RESULT_SMS_GENERIC_ERROR;
        }

        if (receivedIntent != null) {
            try {
                receivedIntent.send(result);
            } catch (CanceledException e) { }
        if (callback != null) {
            callback.onSmsInjectedResult(result);
        }
    }

+24 −17
Original line number Diff line number Diff line
@@ -94,6 +94,13 @@ public abstract class SMSDispatcher extends Handler {
    static final String TAG = "SMSDispatcher";    // accessed from inner class
    static final boolean DBG = false;
    private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
    protected static final String MAP_KEY_PDU = "pdu";
    protected static final String MAP_KEY_SMSC = "smsc";
    protected static final String MAP_KEY_DEST_ADDR = "destAddr";
    protected static final String MAP_KEY_SC_ADDR = "scAddr";
    protected static final String MAP_KEY_DEST_PORT = "destPort";
    protected static final String MAP_KEY_DATA = "data";
    protected static final String MAP_KEY_TEXT = "text";

    private static final int PREMIUM_RULE_USE_SIM = 1;
    private static final int PREMIUM_RULE_USE_NETWORK = 2;
@@ -152,7 +159,7 @@ public abstract class SMSDispatcher extends Handler {
     */
    private static int sConcatenatedRef = new Random().nextInt(256);

    private SmsDispatchersController mSmsDispatchersController;
    protected SmsDispatchersController mSmsDispatchersController;

    /** Number of outgoing SmsTrackers waiting for user confirmation. */
    private int mPendingTrackerCount;
@@ -378,7 +385,7 @@ public abstract class SMSDispatcher extends Handler {
        @Override
        protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
            HashMap<String, Object> map = mTracker.getData();
            String text = (String) map.get("text");
            String text = (String) map.get(MAP_KEY_TEXT);

            if (text != null) {
                try {
@@ -410,8 +417,8 @@ public abstract class SMSDispatcher extends Handler {
        @Override
        protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
            HashMap<String, Object> map = mTracker.getData();
            byte[] data = (byte[]) map.get("data");
            int destPort = (int) map.get("destPort");
            byte[] data = (byte[]) map.get(MAP_KEY_DATA);
            int destPort = (int) map.get(MAP_KEY_DEST_PORT);

            if (data != null) {
                try {
@@ -1076,7 +1083,7 @@ public abstract class SMSDispatcher extends Handler {
    @VisibleForTesting
    public void sendRawPdu(SmsTracker tracker) {
        HashMap map = tracker.getData();
        byte pdu[] = (byte[]) map.get("pdu");
        byte pdu[] = (byte[]) map.get(MAP_KEY_PDU);

        if (mSmsSendDisabled) {
            Rlog.e(TAG, "Device does not support sending sms.");
@@ -1713,23 +1720,23 @@ public abstract class SMSDispatcher extends Handler {
    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
            String text, SmsMessageBase.SubmitPduBase pdu) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("destAddr", destAddr);
        map.put("scAddr", scAddr);
        map.put("text", text);
        map.put("smsc", pdu.encodedScAddress);
        map.put("pdu", pdu.encodedMessage);
        map.put(MAP_KEY_DEST_ADDR, destAddr);
        map.put(MAP_KEY_SC_ADDR, scAddr);
        map.put(MAP_KEY_TEXT, text);
        map.put(MAP_KEY_SMSC, pdu.encodedScAddress);
        map.put(MAP_KEY_PDU, pdu.encodedMessage);
        return map;
    }

    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
            int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("destAddr", destAddr);
        map.put("scAddr", scAddr);
        map.put("destPort", destPort);
        map.put("data", data);
        map.put("smsc", pdu.encodedScAddress);
        map.put("pdu", pdu.encodedMessage);
        map.put(MAP_KEY_DEST_ADDR, destAddr);
        map.put(MAP_KEY_SC_ADDR, scAddr);
        map.put(MAP_KEY_DEST_PORT, destPort);
        map.put(MAP_KEY_DATA, data);
        map.put(MAP_KEY_SMSC, pdu.encodedScAddress);
        map.put(MAP_KEY_PDU, pdu.encodedMessage);
        return map;
    }

@@ -1886,7 +1893,7 @@ public abstract class SMSDispatcher extends Handler {
        }
    }

    protected final boolean isCdmaMo() {
    protected boolean isCdmaMo() {
        return mSmsDispatchersController.isCdmaMo();
    }
}
+94 −27
Original line number Diff line number Diff line
@@ -16,17 +16,22 @@

package com.android.internal.telephony;

import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.SmsManager;
import android.util.Pair;

import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
@@ -40,7 +45,7 @@ import java.util.HashMap;
 *
 */
public class SmsDispatchersController extends Handler {
    private static final String TAG = "RIL_ImsSms";
    private static final String TAG = "SmsDispatchersController";

    /** Radio is ON */
    private static final int EVENT_RADIO_ON = 11;
@@ -53,6 +58,7 @@ public class SmsDispatchersController extends Handler {

    private SMSDispatcher mCdmaDispatcher;
    private SMSDispatcher mGsmDispatcher;
    private ImsSmsDispatcher mImsSmsDispatcher;

    private GsmInboundSmsHandler mGsmInboundSmsHandler;
    private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
@@ -78,6 +84,7 @@ public class SmsDispatchersController extends Handler {

        // Create dispatchers, inbound SMS handlers and
        // broadcast undelivered messages in raw table.
        mImsSmsDispatcher = new ImsSmsDispatcher(phone, this);
        mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
                storageMonitor, phone);
@@ -146,7 +153,6 @@ public class SmsDispatchersController extends Handler {
    }

    private void setImsSmsFormat(int format) {
        // valid format?
        switch (format) {
            case PhoneConstants.PHONE_TYPE_GSM:
                mImsSmsFormat = SmsConstants.FORMAT_3GPP;
@@ -172,13 +178,12 @@ public class SmsDispatchersController extends Handler {
     *
     * @param pdu is the byte array of pdu to be injected into android telephony layer
     * @param format is the format of SMS pdu (3gpp or 3gpp2)
     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
     *  broadcast when the message is successfully received by the
     *  android telephony layer. This intent is broadcasted at
     * @param callback if not NULL this callback is triggered when the message is successfully
     *                 received by the android telephony layer. This callback is triggered at
     *                 the same time an SMS received from radio is responded back.
     */
    @VisibleForTesting
    public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
    public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
        Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
        try {
            // TODO We need to decide whether we should allow injecting GSM(3gpp)
@@ -192,13 +197,11 @@ public class SmsDispatchersController extends Handler {
                if (msg == null) {
                    Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
                }
                if (receivedIntent != null) {
                    receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
                }
                callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
                return;
            }

            AsyncResult ar = new AsyncResult(receivedIntent, msg, null);
            AsyncResult ar = new AsyncResult(callback, msg, null);

            if (format.equals(SmsConstants.FORMAT_3GPP)) {
                Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
@@ -211,19 +214,11 @@ public class SmsDispatchersController extends Handler {
            } else {
                // Invalid pdu format.
                Rlog.e(TAG, "Invalid pdu format: " + format);
                if (receivedIntent != null) {
                    receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
                }
                callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
            }
        } catch (Exception e) {
            Rlog.e(TAG, "injectSmsPdu failed: ", e);
            try {
                if (receivedIntent != null) {
                    receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
                }
            } catch (CanceledException ex) {
                Rlog.e(TAG, "Failed to send result");
            }
            callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
        }
    }

@@ -307,8 +302,7 @@ public class SmsDispatchersController extends Handler {
        map.put("smsc", pdu.encodedScAddress);
        map.put("pdu", pdu.encodedMessage);

        SMSDispatcher dispatcher = (isCdmaFormat(newFormat))
                ? mCdmaDispatcher : mGsmDispatcher;
        SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? mCdmaDispatcher : mGsmDispatcher;

        tracker.mFormat = dispatcher.getFormat();
        dispatcher.sendSms(tracker);
@@ -344,7 +338,7 @@ public class SmsDispatchersController extends Handler {
     * @param format
     * @return true if format given is CDMA format, false otherwise.
     */
    private boolean isCdmaFormat(String format) {
    public boolean isCdmaFormat(String format) {
        return (mCdmaDispatcher.getFormat().equals(format));
    }

@@ -376,7 +370,10 @@ public class SmsDispatchersController extends Handler {
     */
    protected void sendData(String destAddr, String scAddr, int destPort,
                            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
        if (isCdmaMo()) {
        if (mImsSmsDispatcher.isAvailable()) {
            mImsSmsDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent,
                    deliveryIntent);
        } else if (isCdmaMo()) {
            mCdmaDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
        } else {
            mGsmDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
@@ -413,7 +410,10 @@ public class SmsDispatchersController extends Handler {
    public void sendText(String destAddr, String scAddr, String text,
                            PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
                            String callingPkg, boolean persistMessage) {
        if (isCdmaMo()) {
        if (mImsSmsDispatcher.isAvailable()) {
            mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                    messageUri, callingPkg, persistMessage);
        } else if (isCdmaMo()) {
            mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri,
                    callingPkg, persistMessage);
        } else {
@@ -454,7 +454,10 @@ public class SmsDispatchersController extends Handler {
            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
            boolean persistMessage) {
        if (isCdmaMo()) {
        if (mImsSmsDispatcher.isAvailable()) {
            mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
                    deliveryIntents, messageUri, callingPkg, persistMessage);
        } else if (isCdmaMo()) {
            mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents, deliveryIntents,
                    messageUri, callingPkg, persistMessage);
        } else {
@@ -492,4 +495,68 @@ public class SmsDispatchersController extends Handler {
    public SmsUsageMonitor getUsageMonitor() {
        return mUsageMonitor;
    }

    /**
     * Triggers the correct method for handling the sms status report based on the format.
     *
     * @param tracker the sms tracker.
     * @param format the format.
     * @param pdu the pdu of the report.
     * @return a Pair in which the first boolean is whether the report was handled successfully
     *          or not and the second boolean is whether processing the sms is complete and the
     *          tracker no longer need to be kept track of, false if we should expect more callbacks
     *          and the tracker should be kept.
     */
    public Pair<Boolean, Boolean> handleSmsStatusReport(SMSDispatcher.SmsTracker tracker,
            String format, byte[] pdu) {
        if (isCdmaFormat(format)) {
            return handleCdmaStatusReport(tracker, format, pdu);
        } else {
            return handleGsmStatusReport(tracker, format, pdu);
        }
    }

    private Pair<Boolean, Boolean> handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker,
            String format, byte[] pdu) {
        tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE);
        boolean success = triggerDeliveryIntent(tracker, format, pdu);
        return new Pair(success, true /* complete */);
    }

    private Pair<Boolean, Boolean> handleGsmStatusReport(SMSDispatcher.SmsTracker tracker,
            String format, byte[] pdu) {
        com.android.internal.telephony.gsm.SmsMessage sms =
                com.android.internal.telephony.gsm.SmsMessage.newFromCDS(pdu);
        boolean complete = false;
        boolean success = false;
        if (sms != null) {
            int tpStatus = sms.getStatus();
            if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
                // Update the message status (COMPLETE or FAILED)
                tracker.updateSentMessageStatus(mContext, tpStatus);
                complete = true;
            }
            success = triggerDeliveryIntent(tracker, format, pdu);
        }
        return new Pair(success, complete);
    }

    private boolean triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format,
                                          byte[] pdu) {
        PendingIntent intent = tracker.mDeliveryIntent;
        Intent fillIn = new Intent();
        fillIn.putExtra("pdu", pdu);
        fillIn.putExtra("format", format);
        try {
            intent.send(mContext, Activity.RESULT_OK, fillIn);
            return true;
        } catch (CanceledException ex) {
            return false;
        }
    }


    public interface SmsInjectionCallback {
        void onSmsInjectedResult(int result);
    }
}
Loading