Refactor PhoneFacade.

Separate listeners from PhoneFacade.
Add TelecommManager support to sl4a.
Bug and formatting fixes.
The listeners actually work now.

Change-Id: Ib18064d8282a7a52866937b297dc7cfa51b90531
diff --git a/Common/src/com/googlecode/android_scripting/facade/PhoneFacade.java b/Common/src/com/googlecode/android_scripting/facade/tele/PhoneFacade.java
similarity index 63%
rename from Common/src/com/googlecode/android_scripting/facade/PhoneFacade.java
rename to Common/src/com/googlecode/android_scripting/facade/tele/PhoneFacade.java
index 91394ad..ff4c1db 100755
--- a/Common/src/com/googlecode/android_scripting/facade/PhoneFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/tele/PhoneFacade.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.googlecode.android_scripting.facade;
+package com.googlecode.android_scripting.facade.tele;
 
 import android.app.Service;
 import android.content.ContentResolver;
@@ -22,29 +22,35 @@
 import android.content.Intent;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.Bundle;
+import android.os.RemoteException;
 import android.provider.ContactsContract;
 import android.telephony.CellLocation;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
-import android.telephony.PreciseCallState;
-import android.net.ConnectivityManager;
 import android.provider.Telephony;
-import android.telephony.DataConnectionRealTimeInfo;
 
+import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.TelephonyProperties;
 
 import android.content.ContentValues;
 import android.os.SystemProperties;
 
-import com.googlecode.android_scripting.MainThread;
+import com.googlecode.android_scripting.facade.AndroidFacade;
+import com.googlecode.android_scripting.facade.EventFacade;
+import com.googlecode.android_scripting.facade.FacadeManager;
+import com.googlecode.android_scripting.facade.tele.TelephonyStateListeners
+                                                   .CallStateChangeListener;
+import com.googlecode.android_scripting.facade.tele.TelephonyStateListeners
+                                                   .DataConnectionChangeListener;
+import com.googlecode.android_scripting.facade.tele.TelephonyStateListeners
+                                                   .ServiceStateChangeListener;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
 import com.googlecode.android_scripting.rpc.Rpc;
 import com.googlecode.android_scripting.rpc.RpcDefault;
 import com.googlecode.android_scripting.rpc.RpcParameter;
 import com.googlecode.android_scripting.Log;
+import com.googlecode.android_scripting.MainThread;
 import com.googlecode.android_scripting.rpc.RpcOptional;
 
 import java.io.UnsupportedEncodingException;
@@ -61,21 +67,17 @@
  */
 public class PhoneFacade extends RpcReceiver {
 
+    private final Service mService;
     private final AndroidFacade mAndroidFacade;
     private final EventFacade mEventFacade;
     private final TelephonyManager mTelephonyManager;
-    private final Bundle mPhoneState;
-    private final Service mService;
-    private final Bundle mServiceState;
-    private PhoneStateListener mPhoneStateListener;
-    private final ConnectivityManager mConnect;
-    private final Bundle mModemPowerLevel;
-    private final Bundle mPreciseCallState;
 
-    private final int POWER_STATE_LOW = 1;
-    private final int POWER_STATE_MEDIUM = 2;
-    private final int POWER_STATE_HIGH = 3;
-    private final int POWER_STATE_UNKNOWN = Integer.MAX_VALUE;
+    private CallStateChangeListener mCallStateChangeListener;
+    private DataConnectionChangeListener mDataConnectionChangeListener;
+    private ServiceStateChangeListener mServiceStateChangeListener;
+
+    private ITelephony mITelephony;
+    private PhoneStateListener mPhoneStateListener;
 
     private static final String[] sProjection = new String[] {
             Telephony.Carriers._ID, // 0
@@ -99,8 +101,7 @@
             Telephony.Carriers.BEARER, // 18
             Telephony.Carriers.ROAMING_PROTOCOL, // 19
             Telephony.Carriers.MVNO_TYPE, // 20
-            Telephony.Carriers.MVNO_MATCH_DATA
-            // 21
+            Telephony.Carriers.MVNO_MATCH_DATA // 21
     };
 
     public PhoneFacade(FacadeManager manager) {
@@ -108,160 +109,63 @@
         mService = manager.getService();
         mTelephonyManager =
                 (TelephonyManager) mService.getSystemService(Context.TELEPHONY_SERVICE);
-        mConnect =
-                (ConnectivityManager) mService.getSystemService(Context.CONNECTIVITY_SERVICE);
         mAndroidFacade = manager.getReceiver(AndroidFacade.class);
         mEventFacade = manager.getReceiver(EventFacade.class);
-        mPhoneState = new Bundle();
-        mServiceState = new Bundle();
-        mModemPowerLevel = new Bundle();
-        mModemPowerLevel.putLong("time", 0);
-        mModemPowerLevel.putInt("power_level", POWER_STATE_UNKNOWN);
-        mPreciseCallState = new Bundle();
-        mPreciseCallState.putString("CallState", "");
-
-        mPhoneStateListener = MainThread.run(mService,
-                new Callable<PhoneStateListener>() {
+        MainThread.run(manager.getService(), new Callable<Object>() {
             @Override
-            public PhoneStateListener call() throws Exception {
-                return new PhoneStateListener() {
-                    @Override
-                    public void onCallStateChanged(int state,
-                            String incomingNumber) {
-                        mPhoneState.putString("incomingNumber",
-                                incomingNumber);
-                        switch (state) {
-                            case TelephonyManager.CALL_STATE_IDLE:
-                                mPhoneState.putString("state", "idle");
-                                break;
-                            case TelephonyManager.CALL_STATE_OFFHOOK:
-                                mPhoneState.putString("state", "offhook");
-                                break;
-                            case TelephonyManager.CALL_STATE_RINGING:
-                                mPhoneState.putString("state", "ringing");
-                                break;
-                        }
-                        mEventFacade.postEvent("PhoneCallStateChanged", mPhoneState.clone());
-                        mPhoneState.clear();
-                    }
-
-                    public void onServiceStateChanged(ServiceState serviceState) {
-                        switch(serviceState.getVoiceRegState()) {
-                            case ServiceState.STATE_EMERGENCY_ONLY:
-                                mServiceState.putString("serviceState", "emergency");
-                            break;
-                            case ServiceState.STATE_IN_SERVICE:
-                                mServiceState.putString("serviceState", "inService");
-                            break;
-                            case ServiceState.STATE_OUT_OF_SERVICE:
-                                mServiceState.putString("serviceState","noService");
-                            break;
-                            case ServiceState.STATE_POWER_OFF:
-                                mServiceState.putString("serviceState", "powerOff");
-                            break;
-                        }
-                        mServiceState.putString("operatorName",
-                                serviceState.getOperatorAlphaLong());
-                        mServiceState.putString("operatorCode",
-                                serviceState.getOperatorNumeric());
-                        mEventFacade.postEvent("PhoneServiceStateChanged", mPhoneState.clone());
-                        mPhoneState.clear();
-                    }
-
-                    @Override
-                    public void onDataConnectionRealTimeInfoChanged(
-                            DataConnectionRealTimeInfo dcRtInfo) {
-                        mModemPowerLevel.putString("Type", "modemPowerLvl");
-                        mModemPowerLevel.putLong("time", dcRtInfo.getTime());
-
-                        int state = dcRtInfo.getDcPowerState();
-                        if (POWER_STATE_LOW == state) {
-                            mModemPowerLevel.putString("power_level", "LOW");
-                        } else if (POWER_STATE_MEDIUM == state) {
-                            mModemPowerLevel.putString("power_level", "MEDIUM");
-                        } else if (POWER_STATE_HIGH == state) {
-                            mModemPowerLevel.putString("power_level", "HIGH");
-                        } else {
-                            mModemPowerLevel.putString("power_level", "UNKNOWN");
-                        }
-
-                        mEventFacade.postEvent("PhoneModemPowerLevelChanged",
-                                mModemPowerLevel.clone());
-                        mModemPowerLevel.clear();
-                    }
-
-                    @Override
-                    public void
-                    onPreciseCallStateChanged(PreciseCallState callState) {
-                        int foreGroundCallState = callState.getForegroundCallState();
-
-                        if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_ACTIVE) {
-                            mPreciseCallState.putString("CallState", "ACTIVE");
-                        } else if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_HOLDING) {
-                            mPreciseCallState.putString("CallState", "HOLDING)");
-                        } else if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_DIALING) {
-                            mPreciseCallState.putString("CallState", "DIALING");
-                        } else if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_ALERTING) {
-                            mPreciseCallState.putString("CallState", "ALERTING");
-                        } else if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_INCOMING) {
-                            mPreciseCallState.putString("CallState", "INCOMING)");
-                        } else if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_WAITING) {
-                            mPreciseCallState.putString("CallState", "WAITING");
-                        } else if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED) {
-                            mPreciseCallState.putString("CallState", "DISCONNECTED");
-                        } else if (foreGroundCallState ==
-                                PreciseCallState.PRECISE_CALL_STATE_DISCONNECTING) {
-                            mPreciseCallState.putString("CallState", "DISCONNECTING");
-                        } else {
-                            if (callState.getRingingCallState() ==
-                                    PreciseCallState.PRECISE_CALL_STATE_INCOMING) {
-                                mPreciseCallState.putString("CallState", "INCOMING");
-                            } else {
-                                mPreciseCallState.putString("CallState", "IDLE");
-                            }
-                        }
-                        mEventFacade.postEvent("PhonePreciseCallStateChanged",
-                                mPreciseCallState.clone());
-                        mPreciseCallState.clear();
-                    }
-
-                };
+            public Object call() throws Exception {
+                mCallStateChangeListener = new CallStateChangeListener(mEventFacade);
+                mDataConnectionChangeListener = new DataConnectionChangeListener(mEventFacade);
+                mServiceStateChangeListener = new ServiceStateChangeListener(mEventFacade);
+                return null;
             }
         });
     }
 
-    @Override
-    public void shutdown() {
-        stopTrackingPhoneState();
+    @Rpc(description = "Starts tracking call state change.")
+    public void phoneStartTrackingCallState() {
+        mTelephonyManager.listen(mCallStateChangeListener,
+                                   CallStateChangeListener.sListeningStates);
     }
 
-    @Rpc(description = "Starts tracking phone state.")
-    public void startTrackingPhoneState() {
-        mTelephonyManager.listen(mPhoneStateListener,
-                PhoneStateListener.LISTEN_CALL_STATE
-                        | PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO
-                        | PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                        | PhoneStateListener.LISTEN_SERVICE_STATE);
+    @Rpc(description = "Turn on/off precise listening on fore/background or ringing calls.")
+    public void phoneAdjustPreciseCallStateListenLevel(String type, Boolean listen) {
+        if (type.equals("Foreground")) {
+          mCallStateChangeListener.listenForeground = listen;
+        } else if (type.equals("Ringing")) {
+            mCallStateChangeListener.listenRinging = listen;
+        } else if (type.equals("Background")) {
+            mCallStateChangeListener.listenBackground = listen;
+        }
     }
 
-    @Rpc(description = "Returns the current phone state and incoming number.",
-            returns = "A Map of \"state\" and \"incomingNumber\"")
-    public Bundle readPhoneState() {
-        return mPhoneState;
-    }
-
-    @Rpc(description = "Stops tracking phone state.")
-    public void stopTrackingPhoneState() {
+    @Rpc(description = "Stops tracking call state change.")
+    public void phoneStopTrackingCallStateChange() {
         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
     }
 
+    @Rpc(description = "Starts tracking power level change.")
+    public void phoneStartTrackingPowerLevelChange() {
+        mTelephonyManager.listen(mDataConnectionChangeListener,
+                                 DataConnectionChangeListener.sListeningStates);
+    }
+
+    @Rpc(description = "Stops tracking power level change.")
+    public void phoneStopTrackingPowerLevelChange() {
+        mTelephonyManager.listen(mDataConnectionChangeListener, PhoneStateListener.LISTEN_NONE);
+    }
+
+    @Rpc(description = "Starts tracking service state change.")
+    public void phoneStartTrackingServiceStateChange() {
+        mTelephonyManager.listen(mServiceStateChangeListener,
+                                 ServiceStateChangeListener.sListeningStates);
+    }
+
+    @Rpc(description = "Stops tracking service state change.")
+    public void phoneStopTrackingServicetateChange() {
+        mTelephonyManager.listen(mServiceStateChangeListener, PhoneStateListener.LISTEN_NONE);
+    }
+
     @Rpc(description = "Calls a contact/phone number by URI.")
     public void phoneCall(@RpcParameter(name = "uri")
     final String uriString)
@@ -319,6 +223,12 @@
                 null, null);
     }
 
+    @Rpc(description = "Answers an incoming ringing call.")
+    public void phoneAnswerCall() throws RemoteException {
+        mITelephony.silenceRinger();
+        mITelephony.answerRingingCall();
+    }
+
     @Rpc(description = "Dials a phone number.")
     public void phoneDialNumber(@RpcParameter(name = "phone number")
     final String number)
@@ -388,9 +298,9 @@
             case TelephonyManager.PHONE_TYPE_NONE:
                 return "none";
             case TelephonyManager.PHONE_TYPE_CDMA:
-                return "none";
+                return "cdma";
             case TelephonyManager.PHONE_TYPE_SIP:
-                return "none";
+                return "sip";
             default:
                 return null;
         }
@@ -493,15 +403,10 @@
     }
 
     @Rpc(description = "Sets an APN and make that as preferred APN.")
-    public void setAPN(
-            @RpcParameter(name = "name")
-            final String name,
-            @RpcParameter(name = "apn")
-            final String apn,
-            @RpcParameter(name = "type")
-            @RpcOptional
-            @RpcDefault("")
-            final String type) {
+    public void setAPN(@RpcParameter(name = "name") final String name,
+                       @RpcParameter(name = "apn") final String apn,
+                       @RpcParameter(name = "type") @RpcOptional @RpcDefault("")
+                       final String type) {
         Uri uri;
         Cursor cursor;
 
@@ -520,13 +425,11 @@
         uri = mService.getContentResolver().insert(
                 Telephony.Carriers.CONTENT_URI, new ContentValues());
         if (uri == null) {
-            Log.w("Failed to insert new telephony provider into "
-                    + Telephony.Carriers.CONTENT_URI);
+            Log.w("Failed to insert new provider into " + Telephony.Carriers.CONTENT_URI);
             return;
         }
 
-        cursor = mService.getContentResolver().query(uri, sProjection, null,
-                null, null);
+        cursor = mService.getContentResolver().query(uri, sProjection, null, null, null);
         cursor.moveToFirst();
 
         ContentValues values = new ContentValues();
@@ -573,24 +476,20 @@
 
     @Rpc(description = "Returns the number of APNs defined")
     public int getNumberOfAPNs() {
-        int noOfAPN = 0;
-        String where = "numeric=\""
-                + android.os.SystemProperties.get(
-                        TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "")
-                + "\"";
+        int result = 0;
+        String where = "numeric=\"" + android.os.SystemProperties.get(
+                        TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "") + "\"";
 
         Cursor cursor = mService.getContentResolver().query(
                 Telephony.Carriers.CONTENT_URI,
-                new String[] {
-                        "_id", "name", "apn", "type"
-                }, where, null,
+                new String[] {"_id", "name", "apn", "type"}, where, null,
                 Telephony.Carriers.DEFAULT_SORT_ORDER);
 
         if (cursor != null) {
-            noOfAPN = cursor.getCount();
+            result = cursor.getCount();
         }
         cursor.close();
-        return noOfAPN;
+        return result;
     }
 
     @Rpc(description = "Returns the currently selected APN name")
@@ -599,11 +498,9 @@
         int ID_INDEX = 0;
         final String PREFERRED_APN_URI = "content://telephony/carriers/preferapn";
 
-        Cursor cursor = mService.getContentResolver().query(
-                Uri.parse(PREFERRED_APN_URI), new String[] {
-                    "name"
-                }, null,
-                null, Telephony.Carriers.DEFAULT_SORT_ORDER);
+        Cursor cursor = mService.getContentResolver().query(Uri.parse(PREFERRED_APN_URI),
+                new String[] {"name"}, null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
+
         if (cursor.getCount() > 0) {
             cursor.moveToFirst();
             key = cursor.getString(ID_INDEX);
@@ -611,4 +508,11 @@
         cursor.close();
         return key;
     }
+
+    @Override
+    public void shutdown() {
+        phoneStopTrackingCallStateChange();
+        phoneStopTrackingPowerLevelChange();
+        phoneStopTrackingServicetateChange();
+    }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/tele/TelecommManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/tele/TelecommManagerFacade.java
new file mode 100644
index 0000000..2c53465
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/tele/TelecommManagerFacade.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * 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 com.googlecode.android_scripting.facade.tele;
+
+import java.util.List;
+
+import android.app.Service;
+import android.telecomm.PhoneAccount;
+import android.telecomm.TelecommManager;
+
+import com.googlecode.android_scripting.facade.FacadeManager;
+import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
+import com.googlecode.android_scripting.rpc.Rpc;
+
+/**
+ * Exposes TelecommManager functionality.
+ *
+ */
+public class TelecommManagerFacade extends RpcReceiver {
+    private final TelecommManager mTelecommManager;
+    private final Service mService;
+
+    public TelecommManagerFacade(FacadeManager manager) {
+        super(manager);
+        mService = manager.getService();
+        mTelecommManager = new TelecommManager(mService);
+    }
+
+    @Rpc(description = "If there's a ringing call, accept on behalf of the user.")
+    public List<PhoneAccount> telecommGetEnabledPhoneAccounts() {
+        return mTelecommManager.getEnabledPhoneAccounts();
+    }
+
+    @Rpc(description = "If there's a ringing call, accept on behalf of the user.")
+    public void telecommAcceptRingingCall() {
+        mTelecommManager.acceptRingingCall();
+    }
+
+    @Rpc(description = "End an ongoing call.")
+    public Boolean telecommEndCall() {
+        return mTelecommManager.endCall();
+    }
+
+    @Rpc(description = "Returns whether there is a ringing incoming call.")
+    public Boolean telecommIsRinging() {
+        return mTelecommManager.isRinging();
+    }
+
+    @Rpc(description = "Returns whether there is an ongoing phone call.")
+    public Boolean telecommIsInAPhoneCall() {
+        return mTelecommManager.isInAPhoneCall();
+    }
+
+    @Rpc(description = "Silences the rigner if there's a ringing call.")
+    public void telecommSilenceRinger() {
+        mTelecommManager.silenceRinger();
+    }
+
+    @Override
+    public void shutdown() {
+    }
+
+}
diff --git a/Common/src/com/googlecode/android_scripting/facade/tele/TelephonyStateListeners.java b/Common/src/com/googlecode/android_scripting/facade/tele/TelephonyStateListeners.java
new file mode 100644
index 0000000..834d8cb
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/tele/TelephonyStateListeners.java
@@ -0,0 +1,177 @@
+package com.googlecode.android_scripting.facade.tele;
+
+import com.googlecode.android_scripting.facade.EventFacade;
+import android.os.Bundle;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.PhoneStateListener;
+import android.telephony.PreciseCallState;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+
+/**
+ * Store all subclasses of PhoneStateListener here.
+ */
+public class TelephonyStateListeners {
+
+    public static class CallStateChangeListener extends PhoneStateListener {
+
+        private final EventFacade mEventFacade;
+        public static final int sListeningStates = PhoneStateListener.LISTEN_CALL_STATE |
+                                                   PhoneStateListener.LISTEN_PRECISE_CALL_STATE;
+
+        public boolean listenForeground = true;
+        public boolean listenRinging = false;
+        public boolean listenBackground = false;
+
+        public CallStateChangeListener(EventFacade ef) {
+            super();
+            mEventFacade = ef;
+        }
+
+        public CallStateChangeListener(EventFacade ef, int subId) {
+            super(subId);
+            mEventFacade = ef;
+        }
+
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            Bundle mCallStateEvent = new Bundle();
+            if (incomingNumber.length() > 0) {
+              mCallStateEvent.putString("incomingNumber", incomingNumber);
+            }
+            switch (state) {
+                case TelephonyManager.CALL_STATE_IDLE:
+                    mCallStateEvent.putString("State", "IDLE");
+                    break;
+                case TelephonyManager.CALL_STATE_OFFHOOK:
+                    mCallStateEvent.putString("State", "OFFHOOK");
+                    break;
+                case TelephonyManager.CALL_STATE_RINGING:
+                    mCallStateEvent.putString("State", "RINGING");
+                    break;
+            }
+            mEventFacade.postEvent("onCallStateChanged", mCallStateEvent.clone());
+            mCallStateEvent.clear();
+        }
+
+        @Override
+        public void onPreciseCallStateChanged(PreciseCallState callState) {
+            int foregroundState = callState.getForegroundCallState();
+            int ringingState = callState.getRingingCallState();
+            int backgroundState = callState.getBackgroundCallState();
+            if (listenForeground &&
+                foregroundState != PreciseCallState.PRECISE_CALL_STATE_NOT_VALID) {
+                processCallState(foregroundState, "Foreground", callState);
+            }
+            if (listenRinging &&
+                ringingState != PreciseCallState.PRECISE_CALL_STATE_NOT_VALID) {
+                processCallState(ringingState, "Ringing", callState);
+            }
+            if (listenBackground &&
+                backgroundState != PreciseCallState.PRECISE_CALL_STATE_NOT_VALID) {
+                processCallState(backgroundState, "Background", callState);
+            }
+        }
+
+        private void processCallState(int newState, String which, PreciseCallState callState) {
+            Bundle EventMsg = new Bundle();
+            EventMsg.putString("Type", which);
+            if (newState == PreciseCallState.PRECISE_CALL_STATE_ACTIVE) {
+                EventMsg.putString("State", "ACTIVE");
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_HOLDING) {
+                EventMsg.putString("State", "HOLDING)");
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_DIALING) {
+                EventMsg.putString("State", "DIALING");
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_ALERTING) {
+                EventMsg.putString("State", "ALERTING");
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_INCOMING) {
+                EventMsg.putString("State", "INCOMING");
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_WAITING) {
+                EventMsg.putString("State", "WAITING");
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED) {
+                EventMsg.putString("State", "DISCONNECTED");
+                EventMsg.putInt("Cause", callState.getPreciseDisconnectCause());
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_DISCONNECTING) {
+                EventMsg.putString("State", "DISCONNECTING");
+            } else if (newState == PreciseCallState.PRECISE_CALL_STATE_IDLE) {
+                EventMsg.putString("State", "IDLE");
+            }
+            mEventFacade.postEvent("onPreciseStateChanged", EventMsg);
+        }
+    }
+
+    public static class DataConnectionChangeListener extends PhoneStateListener {
+
+        private final EventFacade mEventFacade;
+        public static final int sListeningStates =
+                PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO;
+
+        public DataConnectionChangeListener(EventFacade ef) {
+            super();
+            mEventFacade = ef;
+        }
+
+        public DataConnectionChangeListener(EventFacade ef, int subId) {
+            super(subId);
+            mEventFacade = ef;
+        }
+
+        @Override
+        public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+            Bundle event = new Bundle();
+            event.putString("Type", "modemPowerLvl");
+            event.putLong("Time", dcRtInfo.getTime());
+
+            int state = dcRtInfo.getDcPowerState();
+            if (state == DataConnectionRealTimeInfo.DC_POWER_STATE_LOW) {
+                event.putString("PowerLevel", "LOW");
+            } else if (state == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH) {
+                event.putString("PowerLevel", "MEDIUM");
+            } else if (state == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM) {
+                event.putString("PowerLevel", "HIGH");
+            } else if (state == DataConnectionRealTimeInfo.DC_POWER_STATE_UNKNOWN) {
+                event.putString("PowerLevel", "UNKNOWN");
+            }
+            mEventFacade.postEvent("onModemPowerLevelChanged", event);
+        }
+    }
+
+    public static class ServiceStateChangeListener extends PhoneStateListener {
+
+        private final EventFacade mEventFacade;
+        public static final int sListeningStates = PhoneStateListener.LISTEN_SERVICE_STATE;
+
+        public ServiceStateChangeListener(EventFacade ef) {
+            super();
+            mEventFacade = ef;
+        }
+
+        public ServiceStateChangeListener(EventFacade ef, int subId) {
+            super(subId);
+            mEventFacade = ef;
+        }
+
+        @Override
+        public void onServiceStateChanged(ServiceState serviceState) {
+            Bundle event = new Bundle();;
+            switch(serviceState.getVoiceRegState()) {
+                case ServiceState.STATE_EMERGENCY_ONLY:
+                    event.putString("State", "EMERGENCY_ONLY");
+                break;
+                case ServiceState.STATE_IN_SERVICE:
+                    event.putString("State", "IN_SERVICE");
+                break;
+                case ServiceState.STATE_OUT_OF_SERVICE:
+                    event.putString("State","OUT_OF_SERVICE");
+                break;
+                case ServiceState.STATE_POWER_OFF:
+                    event.putString("State", "POWER_OFF");
+                break;
+            }
+            event.putString("OperatorName", serviceState.getOperatorAlphaLong());
+            event.putString("OperatorId", serviceState.getOperatorNumeric());
+            mEventFacade.postEvent("onServiceStateChanged", event);
+        }
+    }
+
+}
diff --git a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
index 9b88a0b..144597c 100644
--- a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
+++ b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
@@ -43,8 +43,10 @@
 import android.net.wifi.WifiInfo;
 import android.os.Bundle;
 import android.os.ParcelUuid;
+import android.telecomm.PhoneAccount;
 import android.telephony.CellLocation;
 import android.telephony.NeighboringCellInfo;
+import android.telephony.SmsMessage;
 import android.telephony.gsm.GsmCellLocation;
 import android.util.DisplayMetrics;
 
@@ -154,6 +156,12 @@
         if (data instanceof Point) {
             return buildPoint((Point) data);
         }
+        if (data instanceof SmsMessage) {
+            return buildSmsMessage((SmsMessage) data);
+        }
+        if (data instanceof PhoneAccount) {
+            return buildPhoneAccount((PhoneAccount) data);
+        }
         if (data instanceof DisplayMetrics) {
             return buildDisplayMetrics((DisplayMetrics) data);
         }
@@ -355,6 +363,19 @@
         return point;
     }
 
+    private static Object buildSmsMessage(SmsMessage data) throws JSONException {
+        JSONObject msg = new JSONObject();
+        msg.put("originatingAddress", data.getOriginatingAddress());
+        msg.put("messageBody", data.getMessageBody());
+        return msg;
+    }
+
+    private static Object buildPhoneAccount(PhoneAccount data) throws JSONException {
+        JSONObject msg = new JSONObject();
+        msg.put("id", data.getId());
+        return msg;
+    }
+
     private static Object buildDisplayMetrics(DisplayMetrics data) throws JSONException {
         JSONObject dm = new JSONObject();
         dm.put("widthPixels", data.widthPixels);
diff --git a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
index 11d2501..5d77696 100644
--- a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
+++ b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
@@ -29,6 +29,8 @@
 import com.googlecode.android_scripting.facade.bluetooth.BluetoothLeScanFacade;
 import com.googlecode.android_scripting.facade.bluetooth.BluetoothMapFacade;
 import com.googlecode.android_scripting.facade.bluetooth.BluetoothRfcommFacade;
+import com.googlecode.android_scripting.facade.tele.PhoneFacade;
+import com.googlecode.android_scripting.facade.tele.TelecommManagerFacade;
 import com.googlecode.android_scripting.facade.ui.UiFacade;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
 import com.googlecode.android_scripting.rpc.MethodDescriptor;
@@ -82,8 +84,7 @@
     sFacadeClassList.add(MediaSessionFacade.class);
     sFacadeClassList.add(SensorManagerFacade.class);
     sFacadeClassList.add(SettingsFacade.class);
-    // Could not get SmsFacade to compile. APIs are deprecated
-    // sFacadeClassList.add(SmsFacade.class);
+    sFacadeClassList.add(SmsFacade.class);
     sFacadeClassList.add(SpeechRecognitionFacade.class);
     sFacadeClassList.add(ToneGeneratorFacade.class);
     sFacadeClassList.add(WakeLockFacade.class);
@@ -121,6 +122,7 @@
       sFacadeClassList.add(BluetoothGattFacade.class);
       sFacadeClassList.add(BluetoothLeAdvertiseFacade.class);
       sFacadeClassList.add(DisplayFacade.class);
+      sFacadeClassList.add(TelecommManagerFacade.class);
       sFacadeClassList.add(WifiPasspointManagerFacade.class);
       sFacadeClassList.add(WifiScannerFacade.class);
     }
diff --git a/ScriptingLayerForAndroid/AndroidManifest.xml b/ScriptingLayerForAndroid/AndroidManifest.xml
index a69bf92..666d93a 100644
--- a/ScriptingLayerForAndroid/AndroidManifest.xml
+++ b/ScriptingLayerForAndroid/AndroidManifest.xml
@@ -12,6 +12,7 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.READ_SMS" />
@@ -20,6 +21,7 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
     <uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" />
     <uses-permission android:name="android.permission.RESTART_PACKAGES" />
     <uses-permission android:name="android.permission.GET_TASKS" />