Cherry pick CL https://partner-android-review.googlesource.com/#/c/183045/.

Modification in SMS facade and Telephony state listener
Added support for sending SMS with charcters greater than 160
Added support for incoming SMS
Added support for SMS send and deliver status
Added more filds to  service state change event and data connection state event

Change-Id: Ib2e8e4186efd05183032442f6b8d6ffad74f0707
diff --git a/Common/src/com/googlecode/android_scripting/facade/SmsFacade.java b/Common/src/com/googlecode/android_scripting/facade/SmsFacade.java
index 4665148..e7c2209 100644
--- a/Common/src/com/googlecode/android_scripting/facade/SmsFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/SmsFacade.java
@@ -22,14 +22,19 @@
 import android.app.Service;

 import android.content.Context;

 import android.content.Intent;

+//import android.telephony.ServiceState;

 import android.telephony.SmsManager;

 import android.telephony.SmsMessage;

-import android.net.ConnectivityManager;

-import android.provider.Telephony.Sms;

+import android.provider.Telephony.Sms.Intents;

+import android.content.BroadcastReceiver;

+import android.content.IntentFilter;

+import android.os.Bundle;

+import android.app.Activity;

 

 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;

 import com.googlecode.android_scripting.rpc.Rpc;

 import com.googlecode.android_scripting.rpc.RpcParameter;

+import com.googlecode.android_scripting.Log;

 

 /**

  * Exposes SmsManager functionality.

@@ -37,20 +42,58 @@
  */

 public class SmsFacade extends RpcReceiver {

 

-    private final AndroidFacade mAndroidFacade;

     private final EventFacade mEventFacade;

     private final SmsManager mSms;

     private final Service mService;

-    private final ConnectivityManager mConnect;

+    private IntentFilter mSmsReceived;

+    private BroadcastReceiver mSmsSendListener;

+    private BroadcastReceiver mSmsIncomingListener;

+    private int mNumExpectedSentEvents;

+    private int mNumReceivedSentEvents;

+    private int mNumExpectedDeliveredEvents;

+    private int mNumReceivedDeliveredEvents;

+    private Intent mSendIntent;

+    private Intent mDeliveredIntent;

+

+    private static final String MESSAGE_STATUS_DELIVERED_ACTION =

+            "com.googlecode.android_scripting.sms.MESSAGE_STATUS_DELIVERED";

+    private static final String MESSAGE_SENT_ACTION =

+            "com.googlecode.android_scripting.sms.MESSAGE_SENT";

+    private static final String MESSAGE_RECEIVED_ACTION =

+            "android.provider.Telephony.SMS_RECEIVED";

+    private final int MAX_MESSAGE_LENGTH = 160;

+    private final int INTERNATIONAL_NUMBER_LENGTH = 12;

+    private final int DOMESTIC_NUMBER_LENGTH = 10;

 

     public SmsFacade(FacadeManager manager) {

         super(manager);

         mService = manager.getService();

         mSms = SmsManager.getDefault();

-        mConnect = (ConnectivityManager) mService

-                .getSystemService(Context.CONNECTIVITY_SERVICE);

-        mAndroidFacade = manager.getReceiver(AndroidFacade.class);

         mEventFacade = manager.getReceiver(EventFacade.class);

+        mSmsSendListener = new SmsSendListener();

+        mSmsIncomingListener = new SmsIncomingListener();

+        mNumExpectedSentEvents = 0;

+        mNumReceivedSentEvents = 0;

+        mNumExpectedDeliveredEvents = 0;

+        mNumReceivedDeliveredEvents = 0;

+

+        mSendIntent = new Intent(MESSAGE_SENT_ACTION);

+        mDeliveredIntent = new Intent(MESSAGE_STATUS_DELIVERED_ACTION);

+        IntentFilter filter = new IntentFilter();

+        filter.addAction(MESSAGE_SENT_ACTION);

+        filter.addAction(MESSAGE_STATUS_DELIVERED_ACTION);

+        mService.registerReceiver(mSmsSendListener, filter);

+    }

+

+    @Rpc(description = "Starts tracking incoming SMS.")

+    public void smsStartTrackingIncomingMessage() {

+        mSmsReceived = new IntentFilter(MESSAGE_RECEIVED_ACTION);

+        mService.registerReceiver(mSmsIncomingListener, mSmsReceived);

+    }

+

+    @Rpc(description = "Stops tracking incoming SMS.")

+    public void smsStopTrackingIncomingMessage() {

+        mService.unregisterReceiver(mSmsIncomingListener);

     }

 

     @Rpc(description = "Send a text message to a specified number.")

@@ -58,9 +101,26 @@
             @RpcParameter(name = "phoneNumber")

             String phoneNumber,

             @RpcParameter(name = "message")

-            String message) {

-        PendingIntent pi = PendingIntent.getActivity(mService, 0, new Intent(mService, Sms.class), 0);

-        mSms.sendTextMessage(phoneNumber, null, message, pi, null);

+            String message,

+            @RpcParameter(name = "deliveryReportRequired")

+            Boolean deliveryReportRequired) {

+

+        if(message.length() > MAX_MESSAGE_LENGTH) {

+            ArrayList<String> messagesParts = mSms.divideMessage(message);

+            mNumExpectedSentEvents = mNumExpectedDeliveredEvents = messagesParts.size();

+            ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();

+            ArrayList<PendingIntent> deliveredIntents = new ArrayList<PendingIntent>();

+            for (int i = 0; i < messagesParts.size(); i++) {

+                sentIntents.add(PendingIntent.getBroadcast(mService, 0, mSendIntent, 0));

+                deliveredIntents.add(PendingIntent.getBroadcast(mService, 0, mDeliveredIntent, 0));

+            }

+            mSms.sendMultipartTextMessage(phoneNumber, null, messagesParts, sentIntents, deliveredIntents);

+        } else {

+            mNumExpectedSentEvents = mNumExpectedDeliveredEvents = 1;

+            PendingIntent sentIntent = PendingIntent.getBroadcast(mService, 0, mSendIntent, 0);

+            PendingIntent deliveredIntent = PendingIntent.getBroadcast(mService, 0, mDeliveredIntent, 0);

+            mSms.sendTextMessage(phoneNumber, null, message, sentIntent, deliveredIntent);

+        }

     }

 

     @Rpc(description = "Retrieves all messages currently stored on ICC.")

@@ -68,7 +128,117 @@
         return SmsManager.getAllMessagesFromIcc();

     }

 

+    private class SmsSendListener extends BroadcastReceiver {

+        @Override

+        public void onReceive(Context context, Intent intent) {

+            Bundle event = new Bundle();

+            event.putString("Type", "SmsDeliverStatus");

+            String action = intent.getAction();

+            int resultCode = getResultCode();

+            if (MESSAGE_STATUS_DELIVERED_ACTION.equals(action)) {

+                if (resultCode == Activity.RESULT_OK) {

+                    mNumReceivedDeliveredEvents++;

+                    if(mNumReceivedDeliveredEvents == mNumExpectedDeliveredEvents ) {

+                        Log.d("SMS Message delivered successfully");

+                        mEventFacade.postEvent("onSmsDeliverSuccess", event);

+                        mNumReceivedDeliveredEvents = 0;

+                    }

+                } else {

+                    Log.e("SMS Message delivery failed");

+                    // TODO . Need to find the reason for failure from pdu

+                    mEventFacade.postEvent("onSmsDeliverFailure", event);

+                }

+            } else if (MESSAGE_SENT_ACTION.equals(action)) {

+                if (resultCode == Activity.RESULT_OK) {

+                    mNumReceivedSentEvents++;

+                    if(mNumReceivedSentEvents == mNumExpectedSentEvents ) {

+                        event.putString("Type", "SmsSentSuccess");

+                        Log.d("SMS Message sent successfully");

+                       mEventFacade.postEvent("onSmsSentSuccess", event);

+                       mNumReceivedSentEvents = 0;

+                    }

+                } else {

+                    Log.e("SMS Message send failed");

+                    event.putString("Type", "SmsSentFailure");

+                    switch(resultCode) {

+                        case SmsManager.RESULT_ERROR_GENERIC_FAILURE:

+                            event.putString("Reason", "GenericFailure");

+                            break;

+                        case SmsManager.RESULT_ERROR_RADIO_OFF :

+                            event.putString("Reason", "RadioOff");

+                            break;

+                        case SmsManager.RESULT_ERROR_NULL_PDU:

+                            event.putString("Reason", "NullPdu");

+                            break;

+                        case SmsManager.RESULT_ERROR_NO_SERVICE :

+                            event.putString("Reason", "NoService");

+                            break;

+                        case SmsManager.RESULT_ERROR_LIMIT_EXCEEDED  :

+                            event.putString("Reason", "LimitExceeded");

+                            break;

+                        case SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE :

+                            event.putString("Reason", "FdnCheckFailure");

+                            break;

+                        default:

+                            event.putString("Reason", "Unknown");

+                            break;

+                    }

+                    mEventFacade.postEvent("onSmsSentFailure", event);

+                }

+            }

+            event.clear();

+        }

+    }

+

+    private class SmsIncomingListener extends BroadcastReceiver {

+        @Override

+        public void onReceive(Context context, Intent intent) {

+            String action = intent.getAction();

+            if (MESSAGE_RECEIVED_ACTION.equals(action)) {

+                Log.d("New SMS Received");

+                Bundle extras = intent.getExtras();

+                if (extras != null) {

+                    Bundle event = new Bundle();

+                    event.putString("Type", "NewSmsReceived");

+                    SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);

+                    StringBuilder smsMsg = new StringBuilder();

+

+                    SmsMessage sms = msgs[0];

+                    String sender = sms.getOriginatingAddress();

+                    event.putString("Sender", formatPhoneNumber(sender));

+

+                    for (int i = 0; i < msgs.length; i++) {

+                        sms = msgs[i];

+                        smsMsg.append(sms.getMessageBody());

+                    }

+                    event.putString("Text", smsMsg.toString());

+                    mEventFacade.postEvent("onSmsReceived", event);

+                    event.clear();

+                }

+            }

+        }

+    }

+

+    String formatPhoneNumber(String phoneNumber) {

+        String senderNumberStr = null;

+        int len = phoneNumber.length();

+        if (len > 0) {

+            /**

+             * Currently this incomingNumber modification is specific for US numbers.

+             */

+            if ((INTERNATIONAL_NUMBER_LENGTH == len) && ('+' == phoneNumber.charAt(0))) {

+                senderNumberStr = phoneNumber.substring(1);

+            } else if (DOMESTIC_NUMBER_LENGTH == len) {

+                senderNumberStr = '1' + phoneNumber;

+            } else {

+                senderNumberStr = phoneNumber;

+            }

+        }

+        return senderNumberStr;

+    }

+

     @Override

     public void shutdown() {

+        smsStopTrackingIncomingMessage();

     }

 }

diff --git a/Common/src/com/googlecode/android_scripting/facade/tele/PhoneFacade.java b/Common/src/com/googlecode/android_scripting/facade/tele/PhoneFacade.java
index 9fb3f43..a4043bd 100755
--- a/Common/src/com/googlecode/android_scripting/facade/tele/PhoneFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/tele/PhoneFacade.java
@@ -120,7 +120,7 @@
             public Object call() throws Exception {
                 mCallStateChangeListener = new CallStateChangeListener(mEventFacade);
                 mDataConnectionChangeListener = new DataConnectionChangeListener(mEventFacade);
-                mDataConnectionStateChangeListener = new DataConnectionStateChangeListener(mEventFacade);
+                mDataConnectionStateChangeListener = new DataConnectionStateChangeListener(mEventFacade, mTelephonyManager);
                 mServiceStateChangeListener = new ServiceStateChangeListener(mEventFacade);
                 return null;
             }
@@ -633,5 +633,6 @@
         phoneStopTrackingCallStateChange();
         phoneStopTrackingPowerLevelChange();
         phoneStopTrackingServiceStateChange();
+        phoneStopTrackingDataConnectionStateChange();
     }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/tele/TelephonyStateListeners.java b/Common/src/com/googlecode/android_scripting/facade/tele/TelephonyStateListeners.java
index 734c678..99c419e 100644
--- a/Common/src/com/googlecode/android_scripting/facade/tele/TelephonyStateListeners.java
+++ b/Common/src/com/googlecode/android_scripting/facade/tele/TelephonyStateListeners.java
@@ -13,6 +13,57 @@
  */
 public class TelephonyStateListeners {
 
+    private static String getNetworkTypeString(int type) {
+        switch(type) {
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+                return "GPRS";
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+                return "EDGE";
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+                return "UMTS";
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+                return "HSDPA";
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+                return "HSUPA";
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                return "HSPA";
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+                return "CDMA";
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+                return "1xRTT";
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                return "EVDO_0";
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                return "EVDO_A";
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                return "EVDO_B";
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+                return "EHRPD";
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                return "LTE";
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+                return "HSPAP";
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                return "GSM";
+        }
+        return "UNKNOWN";
+    }
+
+    private static String getNetworkStateString(int state) {
+        switch(state) {
+            case ServiceState.STATE_EMERGENCY_ONLY:
+                return "EMERGENCY_ONLY";
+            case ServiceState.STATE_IN_SERVICE:
+                return "IN_SERVICE";
+            case ServiceState.STATE_OUT_OF_SERVICE:
+                return "OUT_OF_SERVICE";
+            case ServiceState.STATE_POWER_OFF:
+                return "POWER_OFF";
+            default:
+                return "UNKNOWN";
+        }
+   }
+
     public static class CallStateChangeListener extends PhoneStateListener {
 
         private final EventFacade mEventFacade;
@@ -63,7 +114,7 @@
                     subEvent = "Ringing";
                     break;
             }
-            mEventFacade.postEvent("onCallStateChanged"+subEvent, mCallStateEvent.clone());
+            mEventFacade.postEvent("onCallStateChanged"+subEvent, mCallStateEvent);
             mCallStateEvent.clear();
         }
 
@@ -111,6 +162,7 @@
                 subEvent = "Idle";
             }
             mEventFacade.postEvent("onPreciseStateChanged"+subEvent, EventMsg);
+            EventMsg.clear();
         }
     }
 
@@ -148,23 +200,27 @@
                 subEvent = "Unknown";
             }
             mEventFacade.postEvent("onModemPowerLevelChanged"+subEvent, event);
+            event.clear();
         }
     }
 
     public static class DataConnectionStateChangeListener extends PhoneStateListener {
 
         private final EventFacade mEventFacade;
+        private final TelephonyManager mTelephonyManager;
         public static final int sListeningStates =
                 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
 
-        public DataConnectionStateChangeListener(EventFacade ef) {
+        public DataConnectionStateChangeListener(EventFacade ef, TelephonyManager tm) {
             super();
             mEventFacade = ef;
+            mTelephonyManager = tm;
         }
 
-        public DataConnectionStateChangeListener(EventFacade ef, int subId) {
+        public DataConnectionStateChangeListener(EventFacade ef, TelephonyManager tm, int subId) {
             super(subId);
             mEventFacade = ef;
+            mTelephonyManager = tm;
         }
 
         @Override
@@ -178,6 +234,8 @@
                 subEvent = "Connecting";
             } else if (state == TelephonyManager.DATA_CONNECTED) {
                 subEvent = "Connected";
+                event.putString("DataNetworkType", getNetworkTypeString(
+                                 mTelephonyManager.getDataNetworkType()));
             } else if (state == TelephonyManager.DATA_SUSPENDED) {
                 subEvent = "Suspended";
             } else if (state == TelephonyManager.DATA_UNKNOWN) {
@@ -187,6 +245,7 @@
                 event.putInt("UnknownStateCode", state);
             }
             mEventFacade.postEvent("onDataConnectionStateChanged"+subEvent, event);
+            event.clear();
         }
     }
 
@@ -209,7 +268,7 @@
         public void onServiceStateChanged(ServiceState serviceState) {
             Bundle event = new Bundle();
             String subEvent = null;
-            switch(serviceState.getVoiceRegState()) {
+            switch(serviceState.getState()) {
                 case ServiceState.STATE_EMERGENCY_ONLY:
                     subEvent = "EmergencyOnly";
                 break;
@@ -218,18 +277,42 @@
                 break;
                 case ServiceState.STATE_OUT_OF_SERVICE:
                     subEvent = "OutOfService";
+                    if(serviceState.isEmergencyOnly())
+                        subEvent = "EmergencyOnly";
                 break;
                 case ServiceState.STATE_POWER_OFF:
                     subEvent = "PowerOff";
                 break;
             }
+            event.putString("VoiceRegState", getNetworkStateString(
+                             serviceState.getVoiceRegState()));
+            event.putString("VoiceNetworkType", getNetworkTypeString(
+                             serviceState.getVoiceNetworkType()));
+            event.putString("DataRegState", getNetworkStateString(
+                             serviceState.getDataRegState()));
+            event.putString("DataNetworkType", getNetworkTypeString(
+                             serviceState.getDataNetworkType()));
             event.putString("OperatorName", serviceState.getOperatorAlphaLong());
             event.putString("OperatorId", serviceState.getOperatorNumeric());
-            event.putBoolean("ManualNwSelection", serviceState.getIsManualSelection());
+            event.putBoolean("isManualNwSelection", serviceState.getIsManualSelection());
             event.putBoolean("Roaming", serviceState.getRoaming());
             event.putBoolean("isEmergencyOnly", serviceState.isEmergencyOnly());
 
-            mEventFacade.postEvent("onServiceStateChanged"+subEvent, event.clone());
+            if(subEvent.equals("InService")) {
+                switch(serviceState.getVoiceNetworkType()) {
+                    case TelephonyManager.NETWORK_TYPE_LTE:
+                        subEvent = subEvent + "LTE" ;
+                        break;
+                    case TelephonyManager.NETWORK_TYPE_UMTS:
+                        subEvent = subEvent + "UMTS";
+                        break;
+                    case TelephonyManager.NETWORK_TYPE_GSM:
+                        subEvent = subEvent + "GSM";
+                        break;
+                }
+            }
+
+            mEventFacade.postEvent("onServiceStateChanged"+subEvent, event);
             event.clear();
         }
     }