Merge "Create Public API which exposes if the device is in a state where simultaneous voice and data are possible."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 395dd88..3f224af 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -145,6 +145,7 @@
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
     <uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL" />
     <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
+    <uses-permission android:name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
     <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
     <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
@@ -596,7 +597,9 @@
                 <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
             </intent-filter>
         </provider>
-        <receiver android:name="com.android.phone.vvm.omtp.sms.OmtpMessageReceiver"
+
+        <receiver
+          android:name="com.android.phone.vvm.VvmSmsReceiver"
             android:exported="false"
             androidprv:systemUserOnly="true">
             <intent-filter>
@@ -656,14 +659,21 @@
             </intent-filter>
         </receiver>
 
-        <service
-            android:name="com.android.phone.vvm.omtp.sms.OmtpProvisioningService"
-            android:exported="false" />
 
         <service
           android:name="com.android.phone.vvm.omtp.scheduling.TaskSchedulerService"
           android:exported="false" />
 
+        <receiver
+          android:name="com.android.phone.vvm.VvmSimStateTracker"
+          android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+                <action android:name="android.telephony.action.CARRIER_CONFIG_CHANGED"/>
+                <action android:name="android.intent.action.SIM_STATE_CHANGED"/>
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver"
             androidprv:systemUserOnly="true">
             <intent-filter>
@@ -673,10 +683,15 @@
             </intent-filter>
         </receiver>
 
-        <activity android:name=".settings.VoicemailChangePinActivity"
-          android:exported="false"
-          android:theme="@style/DialerSettingsLight"
-          android:windowSoftInputMode="stateVisible|adjustResize">
-          </activity>
+        <activity
+            android:name="com.android.phone.settings.VoicemailChangePinActivity"
+            android:exported="false"
+            android:theme="@style/DialerSettingsLight"
+            android:windowSoftInputMode="stateVisible|adjustResize">
+        </activity>
+
+        <service
+            android:name="com.android.phone.vvm.RemoteVvmTaskManager"
+            android:exported="false"/>
     </application>
 </manifest>
diff --git a/src/com/android/phone/Assert.java b/src/com/android/phone/Assert.java
index 143e66f..37ccda8 100644
--- a/src/com/android/phone/Assert.java
+++ b/src/com/android/phone/Assert.java
@@ -49,7 +49,11 @@
     }
 
     public static void fail() {
-        throw new AssertionError("Fail");
+        fail("Fail");
+    }
+
+    public static void fail(String reason) {
+        throw new AssertionError(reason);
     }
 
     /**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 6ba2f37..130e9a8 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -18,8 +18,10 @@
 
 import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
 
+import android.Manifest.permission;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -53,6 +55,7 @@
 import android.telephony.NeighboringCellInfo;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
+import android.telephony.SmsManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
@@ -63,6 +66,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+
 import com.android.ims.ImsManager;
 import com.android.ims.internal.IImsServiceController;
 import com.android.ims.internal.IImsServiceFeatureListener;
@@ -90,8 +94,11 @@
 import com.android.internal.util.HexDump;
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
 import com.android.phone.settings.VoicemailNotificationSettingsUtil;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -2052,11 +2059,33 @@
     }
 
     @Override
-    public VisualVoicemailSmsFilterSettings getSystemVisualVoicemailSmsFilterSettings(
-            String packageName, int subId) {
+    public VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(int subId) {
         enforceReadPrivilegedPermission();
         return VisualVoicemailSmsFilterConfig
-                .getVisualVoicemailSmsFilterSettings(mPhone.getContext(), packageName, subId);
+                .getActiveVisualVoicemailSmsFilterSettings(mPhone.getContext(), subId);
+    }
+
+    @Override
+    public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int subId,
+            String number, int port, String text, PendingIntent sentIntent) {
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+        enforceDefaultDialer(callingPackage);
+        enforceSendSmsPermission();
+        // Make the calls as the phone process.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+            if (port == 0) {
+                smsManager.sendTextMessageWithSelfPermissions(number, null, text,
+                        sentIntent, null, false);
+            } else {
+                byte[] data = text.getBytes(StandardCharsets.UTF_8);
+                smsManager.sendDataMessageWithSelfPermissions(number, null,
+                        (short) port, data, sentIntent, null);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
     /**
      * Sets the voice activation state of a given subId.
@@ -3393,6 +3422,28 @@
     }
 
     /**
+     * Make sure either called from same process as self (phone) or IPC caller has send SMS
+     * permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission
+     */
+    private void enforceSendSmsPermission() {
+        mApp.enforceCallingOrSelfPermission(permission.SEND_SMS, null);
+    }
+
+    /**
+     * Make sure called from the default dialer
+     *
+     * @throws SecurityException if the caller is not the default dialer
+     */
+    private void enforceDefaultDialer(String callingPackage) {
+        TelecomManager telecomManager = mPhone.getContext().getSystemService(TelecomManager.class);
+        if (!callingPackage.equals(telecomManager.getDefaultDialerPackage())) {
+            throw new SecurityException("Caller not default dialer.");
+        }
+    }
+
+    /**
      * Return the application ID for the app type.
      *
      * @param subId the subscription ID that this request applies to.
diff --git a/src/com/android/phone/VisualVoicemailSmsFilterConfig.java b/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
index 2b2e2f5..2ffb477 100644
--- a/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
+++ b/src/com/android/phone/VisualVoicemailSmsFilterConfig.java
@@ -16,12 +16,15 @@
 package com.android.phone;
 
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.preference.PreferenceManager;
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.util.ArraySet;
 
+import com.android.phone.vvm.RemoteVvmTaskManager;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -39,6 +42,7 @@
     private static final String PREFIX_KEY = "_prefix";
     private static final String ORIGINATING_NUMBERS_KEY = "_originating_numbers";
     private static final String DESTINATION_PORT_KEY = "_destination_port";
+    private static final String DEFAULT_PACKAGE = "com.android.phone";
 
     public static void enableVisualVoicemailSmsFilter(Context context, String callingPackage,
             int subId,
@@ -58,6 +62,21 @@
                 .apply();
     }
 
+    public static VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(
+            Context context, int subId) {
+        ComponentName componentName = RemoteVvmTaskManager.getRemotePackage(context);
+        String packageName;
+        if (componentName == null) {
+            packageName = DEFAULT_PACKAGE;
+        } else {
+            packageName = componentName.getPackageName();
+        }
+        return getVisualVoicemailSmsFilterSettings(
+                context,
+                packageName,
+                subId);
+    }
+
     @Nullable
     public static VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(
             Context context,
@@ -75,6 +94,7 @@
                         VisualVoicemailSmsFilterSettings.DEFAULT_DESTINATION_PORT))
                 .build();
     }
+
     private static SharedPreferences getSharedPreferences(Context context) {
         return PreferenceManager
                 .getDefaultSharedPreferences(context.createDeviceProtectedStorageContext());
diff --git a/src/com/android/phone/vvm/RemoteVvmTaskManager.java b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
new file mode 100644
index 0000000..ce6fce6
--- /dev/null
+++ b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 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 com.android.phone.vvm;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.VisualVoicemailService;
+import android.telephony.VisualVoicemailSms;
+
+import com.android.phone.Assert;
+import com.android.phone.vvm.omtp.VvmLog;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * Service to manage tasks issued to the {@link VisualVoicemailService}. This service will bind to
+ * the default dialer on a visual voicemail event if it implements the VisualVoicemailService. The
+ * service will hold all resource for the VisualVoicemailService until {@link
+ * VisualVoicemailService.VisualVoicemailTask#finish()} has been called on all issued tasks.
+ *
+ * If the service is already running it will be reused for new events. The service will stop itself
+ * after all events are handled.
+ */
+public class RemoteVvmTaskManager extends Service {
+
+    private static final String TAG = "RemoteVvmTaskManager";
+
+    private static final String ACTION_START_CELL_SERVICE_CONNECTED =
+            "ACTION_START_CELL_SERVICE_CONNECTED";
+    private static final String ACTION_START_SMS_RECEIVED = "ACTION_START_SMS_RECEIVED";
+    private static final String ACTION_START_SIM_REMOVED = "ACTION_START_SIM_REMOVED";
+
+    // TODO(twyen): track task individually to have time outs.
+    private int mTaskReferenceCount;
+
+    private RemoteServiceConnection mConnection;
+
+    /**
+     * Handles incoming messages from the VisualVoicemailService.
+     */
+    private Messenger mMessenger;
+
+    public static void startCellServiceConnected(Context context,
+            PhoneAccountHandle phoneAccountHandle) {
+        Intent intent = new Intent(ACTION_START_CELL_SERVICE_CONNECTED, null, context,
+                RemoteVvmTaskManager.class);
+        intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+        context.startService(intent);
+    }
+
+    public static void startSmsReceived(Context context, VisualVoicemailSms sms) {
+        Intent intent = new Intent(ACTION_START_SMS_RECEIVED, null, context,
+                RemoteVvmTaskManager.class);
+        intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE,
+                sms.getPhoneAccountHandle());
+        intent.putExtra(VisualVoicemailService.DATA_SMS, sms);
+        context.startService(intent);
+    }
+
+    public static void startSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
+        Intent intent = new Intent(ACTION_START_SIM_REMOVED, null, context,
+                RemoteVvmTaskManager.class);
+        intent.putExtra(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+        context.startService(intent);
+    }
+
+    public static boolean hasRemoteService(Context context) {
+        return getRemotePackage(context) != null;
+    }
+
+    public static ComponentName getRemotePackage(Context context) {
+
+        ResolveInfo info = context.getPackageManager()
+                .resolveService(newBindIntent(context), PackageManager.MATCH_ALL);
+        if (info == null) {
+            return null;
+        }
+        return info.getComponentInfo().getComponentName();
+    }
+
+    @Override
+    public void onCreate() {
+        Assert.isMainThread();
+        mMessenger = new Messenger(new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                Assert.isMainThread();
+                switch (msg.what) {
+                    case VisualVoicemailService.MSG_TASK_ENDED:
+                        mTaskReferenceCount--;
+                        checkReference();
+                        break;
+                    default:
+                        VvmLog.wtf(TAG, "unexpected message " + msg.what);
+                }
+            }
+        });
+    }
+
+    @Override
+    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
+        Assert.isMainThread();
+        mTaskReferenceCount++;
+        switch (intent.getAction()) {
+            case ACTION_START_CELL_SERVICE_CONNECTED:
+                send(VisualVoicemailService.MSG_ON_CELL_SERVICE_CONNECTED, intent.getExtras());
+                break;
+            case ACTION_START_SMS_RECEIVED:
+                send(VisualVoicemailService.MSG_ON_SMS_RECEIVED, intent.getExtras());
+                break;
+            case ACTION_START_SIM_REMOVED:
+                send(VisualVoicemailService.MSG_ON_SIM_REMOVED, intent.getExtras());
+                break;
+            default:
+                Assert.fail("Unexpected action +" + intent.getAction());
+                break;
+        }
+        // Don't rerun service if processed is killed.
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    @Nullable
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    private int getTaskId() {
+        // TODO(twyen): generate unique IDs. Reference counting is used now so it doesn't matter.
+        return 1;
+    }
+
+    /**
+     * Class for interacting with the main interface of the service.
+     */
+    private class RemoteServiceConnection implements ServiceConnection {
+
+        private final Queue<Message> mTaskQueue = new LinkedList<>();
+
+        private boolean mConnected;
+
+        /**
+         * A handler in the VisualVoicemailService
+         */
+        private Messenger mRemoteMessenger;
+
+        public void enqueue(Message message) {
+            mTaskQueue.add(message);
+            if (mConnected) {
+                runQueue();
+            }
+        }
+
+        public boolean isConnected() {
+            return mConnected;
+        }
+
+        public void onServiceConnected(ComponentName className,
+                IBinder service) {
+            mRemoteMessenger = new Messenger(service);
+            mConnected = true;
+            runQueue();
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            mConnection = null;
+            mConnected = false;
+            mRemoteMessenger = null;
+            VvmLog.e(TAG, "Service disconnected, " + mTaskReferenceCount + " tasks dropped.");
+            mTaskReferenceCount = 0;
+            checkReference();
+        }
+
+        private void runQueue() {
+            Assert.isMainThread();
+            Message message = mTaskQueue.poll();
+            while (message != null) {
+                message.replyTo = mMessenger;
+                message.arg1 = getTaskId();
+
+                try {
+                    mRemoteMessenger.send(message);
+                } catch (RemoteException e) {
+                    VvmLog.e(TAG, "Error sending message to remote service", e);
+                }
+                message = mTaskQueue.poll();
+            }
+        }
+    }
+
+    private void send(int what, Bundle extras) {
+        Assert.isMainThread();
+        Message message = Message.obtain();
+        message.what = what;
+        message.setData(new Bundle(extras));
+        if (mConnection == null) {
+            mConnection = new RemoteServiceConnection();
+        }
+        mConnection.enqueue(message);
+
+        if (!mConnection.isConnected()) {
+            Intent intent = newBindIntent(this);
+            intent.setComponent(getRemotePackage(this));
+            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+        }
+    }
+
+    private void checkReference() {
+        if (mTaskReferenceCount == 0) {
+            unbindService(mConnection);
+            mConnection = null;
+        }
+    }
+
+    private static Intent newBindIntent(Context context) {
+        Intent intent = new Intent();
+        intent.setAction(VisualVoicemailService.SERVICE_INTERFACE);
+        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+        intent.setPackage(telecomManager.getDefaultDialerPackage());
+        return intent;
+    }
+}
diff --git a/src/com/android/phone/vvm/VvmSimStateTracker.java b/src/com/android/phone/vvm/VvmSimStateTracker.java
new file mode 100644
index 0000000..069111a
--- /dev/null
+++ b/src/com/android/phone/vvm/VvmSimStateTracker.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2016 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 com.android.phone.vvm;
+
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.phone.PhoneUtils;
+import com.android.phone.vvm.omtp.VvmLog;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tracks the status of all inserted SIMs. Will notify {@link RemoteVvmTaskManager} of when a SIM
+ * connected to the service for the first time after it was inserted or the system booted, and when
+ * the SIM is removed. Losing cell signal or entering airplane mode will not cause the connected
+ * event to be triggered again. Reinserting the SIM will trigger the connected event. Changing the
+ * carrier config will also trigger the connected event. Events will be delayed until the device has
+ * been fully booted (and left FBE mode).
+ */
+public class VvmSimStateTracker extends BroadcastReceiver {
+
+    private static final String TAG = "VvmSimStateTracker";
+
+    /**
+     * Map to keep track of currently inserted SIMs. If the SIM hasn't been connected to the service
+     * before the value will be a {@link ServiceStateListener} that is still waiting for the
+     * connection. A value of {@code null} means the SIM has been connected to the service before.
+     */
+    private static Map<PhoneAccountHandle, ServiceStateListener> sListeners = new ArrayMap<>();
+
+    /**
+     * Accounts that has events before the device is booted. The events should be regenerated after
+     * the device has fully booted.
+     */
+    private static Set<PhoneAccountHandle> sPreBootHandles = new ArraySet<>();
+
+    /**
+     * Waits for the account to become {@link ServiceState#STATE_IN_SERVICE} and notify the
+     * connected event. Will unregister itself once the event has been triggered.
+     */
+    private class ServiceStateListener extends PhoneStateListener {
+
+        private final PhoneAccountHandle mPhoneAccountHandle;
+        private final Context mContext;
+
+        public ServiceStateListener(Context context, PhoneAccountHandle phoneAccountHandle) {
+            mContext = context;
+            mPhoneAccountHandle = phoneAccountHandle;
+        }
+
+        public void listen() {
+            getTelephonyManager(mContext, mPhoneAccountHandle)
+                    .listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
+        }
+
+        public void unlisten() {
+            getTelephonyManager(mContext, mPhoneAccountHandle)
+                    .listen(this, PhoneStateListener.LISTEN_NONE);
+            sListeners.put(mPhoneAccountHandle, null);
+        }
+
+        @Override
+        public void onServiceStateChanged(ServiceState serviceState) {
+            if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
+                VvmLog.i(TAG, "in service");
+                sendConnected(mContext, mPhoneAccountHandle);
+                unlisten();
+            }
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+        final String action = intent.getAction();
+        if (action == null) {
+            VvmLog.w(TAG, "Null action for intent.");
+            return;
+        }
+        VvmLog.i(TAG, action);
+        switch (action) {
+            case Intent.ACTION_BOOT_COMPLETED:
+                onBootCompleted(context);
+                break;
+            case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+                if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
+                        intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
+                    // onSimRemoved will scan all known accounts with isPhoneAccountActive() to find
+                    // which SIM is removed.
+                    // ACTION_SIM_STATE_CHANGED only provides subId which cannot be converted to a
+                    // PhoneAccountHandle when the SIM is absent.
+                    onSimRemoved(context);
+                }
+                break;
+            case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
+                int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+                if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                    VvmLog.i(TAG, "Received SIM change for invalid subscription id.");
+                    return;
+                }
+
+                PhoneAccountHandle phoneAccountHandle =
+                        PhoneAccountHandleConverter.fromSubId(subId);
+
+                if ("null".equals(phoneAccountHandle.getId())) {
+                    VvmLog.e(TAG,
+                            "null phone account handle ID, possible modem crash."
+                                    + " Ignoring carrier config changed event");
+                    return;
+                }
+                onCarrierConfigChanged(context, phoneAccountHandle);
+        }
+    }
+
+    private void onBootCompleted(Context context) {
+        for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
+            TelephonyManager telephonyManager = getTelephonyManager(context, phoneAccountHandle);
+            if (telephonyManager == null) {
+                continue;
+            }
+            if (telephonyManager.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
+                sListeners.put(phoneAccountHandle, null);
+                sendConnected(context, phoneAccountHandle);
+            } else {
+                listenToAccount(context, phoneAccountHandle);
+            }
+        }
+        sPreBootHandles.clear();
+    }
+
+    private void sendConnected(Context context, PhoneAccountHandle phoneAccountHandle) {
+        VvmLog.i(TAG, "Service connected on " + phoneAccountHandle);
+        RemoteVvmTaskManager.startCellServiceConnected(context, phoneAccountHandle);
+    }
+
+    private void onSimRemoved(Context context) {
+        SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
+        if (!isBootCompleted()) {
+            for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
+                if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
+                    sPreBootHandles.remove(phoneAccountHandle);
+                }
+            }
+            return;
+        }
+        Set<PhoneAccountHandle> removeList = new ArraySet<>();
+        for (PhoneAccountHandle phoneAccountHandle : sListeners.keySet()) {
+            if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
+                removeList.add(phoneAccountHandle);
+                ServiceStateListener listener = sListeners.get(phoneAccountHandle);
+                if (listener != null) {
+                    listener.unlisten();
+                }
+                sendSimRemoved(context, phoneAccountHandle);
+            }
+        }
+
+        for (PhoneAccountHandle phoneAccountHandle : removeList) {
+            sListeners.remove(phoneAccountHandle);
+        }
+    }
+
+    private boolean isBootCompleted() {
+        return SystemProperties.getBoolean("sys.boot_completed", false);
+    }
+
+    private void sendSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
+        VvmLog.i(TAG, "Sim removed on " + phoneAccountHandle);
+        RemoteVvmTaskManager.startSimRemoved(context, phoneAccountHandle);
+    }
+
+    private void onCarrierConfigChanged(Context context, PhoneAccountHandle phoneAccountHandle) {
+        if (!isBootCompleted()) {
+            sPreBootHandles.add(phoneAccountHandle);
+            return;
+        }
+        if (getTelephonyManager(context, phoneAccountHandle).getServiceState().getState()
+                == ServiceState.STATE_IN_SERVICE) {
+            sendConnected(context, phoneAccountHandle);
+            sListeners.put(phoneAccountHandle, null);
+        } else {
+            listenToAccount(context, phoneAccountHandle);
+        }
+    }
+
+    private void listenToAccount(Context context, PhoneAccountHandle phoneAccountHandle) {
+        ServiceStateListener listener = new ServiceStateListener(context, phoneAccountHandle);
+        listener.listen();
+        sListeners.put(phoneAccountHandle, listener);
+    }
+
+    @Nullable
+    private static TelephonyManager getTelephonyManager(Context context,
+            PhoneAccountHandle phoneAccountHandle) {
+        return context.getSystemService(TelephonyManager.class)
+                .createForPhoneAccountHandle(phoneAccountHandle);
+    }
+}
diff --git a/src/com/android/phone/vvm/VvmSmsReceiver.java b/src/com/android/phone/vvm/VvmSmsReceiver.java
new file mode 100644
index 0000000..91e7933
--- /dev/null
+++ b/src/com/android/phone/vvm/VvmSmsReceiver.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 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 com.android.phone.vvm;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.VoicemailContract;
+import android.telephony.VisualVoicemailSms;
+
+import com.android.phone.vvm.omtp.VvmLog;
+import com.android.phone.vvm.omtp.sms.OmtpMessageReceiver;
+
+/**
+ * Receives the SMS filtered by {@link com.android.internal.telephony.VisualVoicemailSmsFilter} and
+ * redirect it to the visual voicemail client. The redirection is required to let telephony service
+ * handle tasks with {@link RemoteVvmTaskManager}
+ */
+public class VvmSmsReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "VvmSmsReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        VisualVoicemailSms sms = intent.getExtras()
+                .getParcelable(VoicemailContract.EXTRA_VOICEMAIL_SMS);
+
+        if (sms.getPhoneAccountHandle() == null) {
+            // This should never happen
+            VvmLog.e(TAG, "Received message for null phone account");
+            return;
+        }
+
+        if (RemoteVvmTaskManager.hasRemoteService(context)) {
+            VvmLog.i(TAG, "Sending SMS received event to remote service");
+            RemoteVvmTaskManager.startSmsReceived(context, sms);
+            return;
+        }
+
+        OmtpMessageReceiver
+                .onReceive(context, sms);
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index a506a12..0f93f01 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -32,6 +32,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
+import com.android.phone.vvm.RemoteVvmTaskManager;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
 import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
 
@@ -55,6 +56,10 @@
             return;
         }
 
+        if (RemoteVvmTaskManager.hasRemoteService(context)) {
+            return;
+        }
+
         switch (action) {
             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
@@ -91,6 +96,7 @@
                 }
 
                 VvmLog.d(TAG, "Carrier config changed");
+
                 if (UserManager.get(context).isUserUnlocked() && !isCryptKeeperMode()) {
                     processSubId(context, subId);
                 } else {
diff --git a/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
index 9809c64..21cce31 100644
--- a/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
+++ b/src/com/android/phone/vvm/omtp/VvmBootCompletedReceiver.java
@@ -22,6 +22,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.SubscriptionManager;
+import com.android.phone.vvm.RemoteVvmTaskManager;
 import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
 
 /**
@@ -45,6 +46,10 @@
             return;
         }
 
+        if (RemoteVvmTaskManager.hasRemoteService(context)) {
+            return;
+        }
+
         VvmLog.v(TAG, "processing subId list");
         for (PhoneAccountHandle handle : TelecomManager.from(context)
                 .getCallCapablePhoneAccounts()) {
diff --git a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
index 7e2472b..31c173d 100644
--- a/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
+++ b/src/com/android/phone/vvm/omtp/VvmPhoneStateListener.java
@@ -20,9 +20,9 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
-
 import com.android.phone.PhoneUtils;
 import com.android.phone.VoicemailStatus;
+import com.android.phone.vvm.RemoteVvmTaskManager;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSyncService;
 import com.android.phone.vvm.omtp.sync.SyncTask;
@@ -55,6 +55,10 @@
             return;
         }
 
+        if (RemoteVvmTaskManager.hasRemoteService(mContext)) {
+            return;
+        }
+
         int state = serviceState.getState();
         if (state == mPreviousState || (state != ServiceState.STATE_IN_SERVICE
                 && mPreviousState != ServiceState.STATE_IN_SERVICE)) {
@@ -67,6 +71,7 @@
         OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, subId);
 
         if (state == ServiceState.STATE_IN_SERVICE) {
+
             VoicemailStatusQueryHelper voicemailStatusQueryHelper =
                     new VoicemailStatusQueryHelper(mContext);
             if (voicemailStatusQueryHelper.isVoicemailSourceConfigured(mPhoneAccount)) {
diff --git a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
index ba5bd70..6a5f349 100644
--- a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
+++ b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
@@ -17,10 +17,8 @@
 package com.android.phone.vvm.omtp.sms;
 
 import android.content.Context;
-import android.content.Intent;
 import android.os.Bundle;
-import android.provider.VoicemailContract;
-import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailSms;
 
 import com.android.internal.telephony.Phone;
 import com.android.phone.PhoneUtils;
@@ -37,15 +35,14 @@
 
     private static final String TAG = "LegacyModeSmsHandler";
 
-    public static void handle(Context context, Intent intent, PhoneAccountHandle handle) {
+    public static void handle(Context context, VisualVoicemailSms sms) {
         VvmLog.v(TAG, "processing VVM SMS on legacy mode");
-        String eventType = intent.getExtras()
-                .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
-        Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
+        String eventType = sms.getPrefix();
+        Bundle data = sms.getFields();
 
         if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
             SyncMessage message = new SyncMessage(data);
-            VvmLog.v(TAG, "Received SYNC sms for " + handle.getId() +
+            VvmLog.v(TAG, "Received SYNC sms for " + sms.getPhoneAccountHandle() +
                     " with event " + message.getSyncTriggerEvent());
 
             switch (message.getSyncTriggerEvent()) {
@@ -56,7 +53,8 @@
                     // For some carriers new message count could be set to 0 even if there are still
                     // unread messages, to clear the message waiting indicator.
                     VvmLog.v(TAG, "updating MWI");
-                    Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(handle);
+                    Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(
+                            sms.getPhoneAccountHandle());
                     // Setting voicemail message count to non-zero will show the telephony voicemail
                     // notification, and zero will clear it.
                     phone.setVoiceMessageCount(message.getNewMessageCount());
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index 397caf8..d42e70e 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -15,7 +15,6 @@
  */
 package com.android.phone.vvm.omtp.sms;
 
-import android.content.BroadcastReceiver;
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
@@ -25,6 +24,8 @@
 import android.provider.VoicemailContract;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.Voicemail;
+import android.telephony.VisualVoicemailSms;
+
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
 import com.android.phone.vvm.omtp.ActivationTask;
 import com.android.phone.vvm.omtp.OmtpConstants;
@@ -40,46 +41,35 @@
 /**
  * Receive SMS messages and send for processing by the OMTP visual voicemail source.
  */
-public class OmtpMessageReceiver extends BroadcastReceiver {
+public class OmtpMessageReceiver {
 
     private static final String TAG = "OmtpMessageReceiver";
 
-    private Context mContext;
+    public static void onReceive(Context context, VisualVoicemailSms sms) {
 
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        mContext = context;
-        int subId = intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID);
-        PhoneAccountHandle phone = PhoneAccountHandleConverter.fromSubId(subId);
-
-        if (phone == null) {
-            // This should never happen
-            VvmLog.i(TAG, "Received message for null phone account on subId " + subId);
-            return;
-        }
-
+        PhoneAccountHandle phone = sms.getPhoneAccountHandle();
+        int subId = PhoneAccountHandleConverter.toSubId(phone);
         if (!UserManager.get(context).isUserUnlocked()) {
             VvmLog.i(TAG, "Received message on locked device");
             // LegacyModeSmsHandler can handle new message notifications without storage access
-            LegacyModeSmsHandler.handle(context, intent, phone);
+            LegacyModeSmsHandler.handle(context, sms);
             // A full sync will happen after the device is unlocked, so nothing else need to be
             // done.
             return;
         }
 
-        OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, subId);
-        if (!VisualVoicemailSettingsUtil.isEnabled(mContext, phone)) {
+        OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, subId);
+        if (!VisualVoicemailSettingsUtil.isEnabled(context, phone)) {
             if (helper.isLegacyModeEnabled()) {
-                LegacyModeSmsHandler.handle(context, intent, phone);
+                LegacyModeSmsHandler.handle(context, sms);
             } else {
                 VvmLog.i(TAG, "Received vvm message for disabled vvm source.");
             }
             return;
         }
 
-        String eventType = intent.getExtras()
-                .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
-        Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
+        String eventType = sms.getPrefix();
+        Bundle data = sms.getFields();
 
         if (eventType == null || data == null) {
             VvmLog.e(TAG, "Unparsable VVM SMS received, ignoring");
@@ -91,7 +81,7 @@
 
             VvmLog.v(TAG, "Received SYNC sms for " + subId +
                     " with event " + message.getSyncTriggerEvent());
-            processSync(phone, message);
+            processSync(context, phone, message);
         } else if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
             VvmLog.v(TAG, "Received Status sms for " + subId);
             // If the STATUS SMS is initiated by ActivationTask the TaskSchedulerService will reject
@@ -122,7 +112,8 @@
      *
      * @param message The sync message to extract data from.
      */
-    private void processSync(PhoneAccountHandle phone, SyncMessage message) {
+    private static void processSync(Context context, PhoneAccountHandle phone,
+            SyncMessage message) {
         Intent serviceIntent = null;
         switch (message.getSyncTriggerEvent()) {
             case OmtpConstants.NEW_MESSAGE:
@@ -137,18 +128,18 @@
                         .setPhoneAccount(phone)
                         .setSourceData(message.getId())
                         .setDuration(message.getLength())
-                        .setSourcePackage(mContext.getPackageName());
+                        .setSourcePackage(context.getPackageName());
                 Voicemail voicemail = builder.build();
 
-                VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(mContext);
+                VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(context);
                 if (queryHelper.isVoicemailUnique(voicemail)) {
-                    Uri uri = VoicemailContract.Voicemails.insert(mContext, voicemail);
+                    Uri uri = VoicemailContract.Voicemails.insert(context, voicemail);
                     voicemail = builder.setId(ContentUris.parseId(uri)).setUri(uri).build();
-                    SyncOneTask.start(mContext, phone, voicemail);
+                    SyncOneTask.start(context, phone, voicemail);
                 }
                 break;
             case OmtpConstants.MAILBOX_UPDATE:
-                SyncTask.start(mContext, phone, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY);
+                SyncTask.start(context, phone, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY);
                 break;
             case OmtpConstants.GREETINGS_UPDATE:
                 // Not implemented in V1
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java b/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java
deleted file mode 100644
index 154eeeb..0000000
--- a/src/com/android/phone/vvm/omtp/sms/OmtpProvisioningService.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2016 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 com.android.phone.vvm.omtp.sms;
-
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.provider.VoicemailContract;
-import android.telecom.PhoneAccountHandle;
-
-import com.android.phone.PhoneUtils;
-import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
-import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
-
-/**
- * Performs visual voicemail provisioning in background thread. Not exported.
- */
-public class OmtpProvisioningService extends IntentService {
-
-    public OmtpProvisioningService() {
-        super("OmtpProvisioningService");
-    }
-
-    /**
-     * Create an intent to start OmtpProvisioningService from a {@link
-     * VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} intent.
-     */
-    public static Intent getProvisionIntent(Context context, Intent messageIntent) {
-        Intent serviceIntent = new Intent(context, OmtpProvisioningService.class);
-
-        serviceIntent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID,
-                messageIntent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID));
-        serviceIntent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS,
-                messageIntent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS));
-
-        return serviceIntent;
-    }
-
-    @Override
-    public void onHandleIntent(Intent intent) {
-        int subId = intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID);
-        PhoneAccountHandle phone = PhoneAccountHandleConverter.fromSubId(subId);
-
-        Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
-
-        StatusMessage message = new StatusMessage(data);
-        startProvisioning(phone, message, data);
-    }
-
-    private void startProvisioning(PhoneAccountHandle phone, StatusMessage message, Bundle data) {
-        OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(this,
-                PhoneUtils.getSubIdForPhoneAccountHandle(phone));
-
-    }
-}
diff --git a/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java b/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java
index 69e4f5f..adaa2f4 100644
--- a/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java
+++ b/src/com/android/phone/vvm/omtp/sms/StatusSmsFetcher.java
@@ -27,13 +27,16 @@
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
 import android.telephony.SmsManager;
+import android.telephony.VisualVoicemailSms;
 
 import com.android.phone.Assert;
 import com.android.phone.vvm.omtp.OmtpConstants;
 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
 import com.android.phone.vvm.omtp.VvmLog;
 import com.android.phone.vvm.omtp.protocol.VisualVoicemailProtocol;
+import com.android.phone.vvm.omtp.utils.PhoneAccountHandleConverter;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -107,16 +110,17 @@
             return;
         }
 
-        int subId = intent.getExtras().getInt(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID);
-
+        VisualVoicemailSms sms = intent.getExtras()
+                .getParcelable(VoicemailContract.EXTRA_VOICEMAIL_SMS);
+        PhoneAccountHandle phoneAccountHandle = sms.getPhoneAccountHandle();
+        int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
         if (mSubId != subId) {
             return;
         }
-        String eventType = intent.getExtras()
-                .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
+        String eventType = sms.getPrefix();
 
         if (eventType.equals(OmtpConstants.STATUS_SMS_PREFIX)) {
-            mFuture.complete(intent.getBundleExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS));
+            mFuture.complete(sms.getFields());
             return;
         }
 
@@ -132,7 +136,7 @@
             return;
         }
         Bundle translatedBundle = protocol.translateStatusSmsBundle(helper, eventType,
-                intent.getBundleExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS));
+                sms.getFields());
 
         if (translatedBundle != null) {
             VvmLog.i(TAG, "Translated to STATUS SMS");