ProcessPendingMessagesAction works based on subscriptions

PorcessPendingMessagesAction queues one message for sending/downloading
associated with a subscription triggering current action at a time. And
the ConnectivityUtil also works based on subscriptions so that pending
messages can be processed regardless of other phones' state in multi-sim
case.

It includes cleanup code as well.

Test: Manual

Change-Id: Id6b4f4a0aa6a3291e7a4d8a5d3f0fbb9db3c5b86
Signed-off-by: Taesu Lee <taesu82.lee@samsung.com>
diff --git a/src/com/android/messaging/datamodel/BugleDatabaseOperations.java b/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
index 281a8dd..c4e5fb8 100644
--- a/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
+++ b/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
@@ -965,7 +965,7 @@
         return participant;
     }
 
-    static int getSelfSubscriptionId(final DatabaseWrapper dbWrapper,
+    public static int getSelfSubscriptionId(final DatabaseWrapper dbWrapper,
             final String selfParticipantId) {
         final ParticipantData selfParticipant = BugleDatabaseOperations.getExistingParticipant(
                 dbWrapper, selfParticipantId);
diff --git a/src/com/android/messaging/datamodel/DataModel.java b/src/com/android/messaging/datamodel/DataModel.java
index 936b51c..b606c72 100644
--- a/src/com/android/messaging/datamodel/DataModel.java
+++ b/src/com/android/messaging/datamodel/DataModel.java
@@ -50,7 +50,6 @@
 import com.android.messaging.datamodel.data.SubscriptionListData;
 import com.android.messaging.datamodel.data.VCardContactItemData;
 import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
-import com.android.messaging.util.ConnectivityUtil;
 
 public abstract class DataModel {
     private String mFocusedConversation;
@@ -152,7 +151,5 @@
 
     public abstract void onApplicationCreated();
 
-    public abstract ConnectivityUtil getConnectivityUtil();
-
     public abstract SyncManager getSyncManager();
 }
diff --git a/src/com/android/messaging/datamodel/DataModelImpl.java b/src/com/android/messaging/datamodel/DataModelImpl.java
index 6ab3f00..b213162 100644
--- a/src/com/android/messaging/datamodel/DataModelImpl.java
+++ b/src/com/android/messaging/datamodel/DataModelImpl.java
@@ -57,22 +57,33 @@
 import com.android.messaging.util.OsUtil;
 import com.android.messaging.util.PhoneUtils;
 
+import java.util.concurrent.ConcurrentHashMap;
+
 public class DataModelImpl extends DataModel {
     private final Context mContext;
     private final ActionService mActionService;
     private final BackgroundWorker mDataModelWorker;
     private final DatabaseHelper mDatabaseHelper;
-    private final ConnectivityUtil mConnectivityUtil;
     private final SyncManager mSyncManager;
 
+    // Cached ConnectivityUtil instance for Pre-L_MR1
+    private static ConnectivityUtil sConnectivityUtilInstanceCachePreLMR1 = null;
+    // Cached ConnectivityUtil subId->instance for L_MR1 and beyond
+    private static final ConcurrentHashMap<Integer, ConnectivityUtil>
+            sConnectivityUtilInstanceCacheLMR1 = new ConcurrentHashMap<>();
+
     public DataModelImpl(final Context context) {
         super();
         mContext = context;
         mActionService = new ActionService();
         mDataModelWorker = new BackgroundWorker();
         mDatabaseHelper = DatabaseHelper.getInstance(context);
-        mConnectivityUtil = new ConnectivityUtil(context);
         mSyncManager = new SyncManager();
+        if (OsUtil.isAtLeastL_MR1()) {
+            createConnectivityUtilForLMR1();
+        } else {
+            sConnectivityUtilInstanceCachePreLMR1 = new ConnectivityUtil(context);
+        }
     }
 
     @Override
@@ -185,11 +196,6 @@
     }
 
     @Override
-    public ConnectivityUtil getConnectivityUtil() {
-        return mConnectivityUtil;
-    }
-
-    @Override
     public SyncManager getSyncManager() {
         return mSyncManager;
     }
@@ -218,7 +224,8 @@
         SyncManager.immediateSync();
 
         if (OsUtil.isAtLeastL_MR1()) {
-            // Start listening for subscription change events for refreshing self participants.
+            // Start listening for subscription change events for refreshing any data associated
+            // with subscriptions.
             PhoneUtils.getDefault().toLMr1().registerOnSubscriptionsChangedListener(
                     new SubscriptionManager.OnSubscriptionsChangedListener() {
                         @Override
@@ -229,8 +236,31 @@
                             // gracefully
                             MmsConfig.loadAsync();
                             ParticipantRefresh.refreshSelfParticipants();
+                            createConnectivityUtilForLMR1();
                         }
                     });
         }
     }
+
+    private void createConnectivityUtilForLMR1() {
+        PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() {
+            @Override
+            public void runForSubscription(int subId) {
+                // Create the ConnectivityUtil instance for given subId if absent.
+                if (subId <= ParticipantData.DEFAULT_SELF_SUB_ID) {
+                    subId = PhoneUtils.getDefault().getDefaultSmsSubscriptionId();
+                }
+                sConnectivityUtilInstanceCacheLMR1.computeIfAbsent(
+                        subId, key -> new ConnectivityUtil(mContext, key));
+            }
+        });
+    }
+
+    public static ConnectivityUtil getConnectivityUtil(final int subId) {
+        if (OsUtil.isAtLeastL_MR1()) {
+            return sConnectivityUtilInstanceCacheLMR1.get(subId);
+        } else {
+            return sConnectivityUtilInstanceCachePreLMR1;
+        }
+    }
 }
diff --git a/src/com/android/messaging/datamodel/action/ActionServiceImpl.java b/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
index 6725dde..d78bdc0 100644
--- a/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
+++ b/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
@@ -28,7 +28,6 @@
 
 import com.android.messaging.Factory;
 import com.android.messaging.datamodel.DataModel;
-import com.android.messaging.util.ConnectivityUtil;
 import com.android.messaging.util.LogUtil;
 import com.android.messaging.util.LoggingTimer;
 import com.google.common.annotations.VisibleForTesting;
@@ -134,7 +133,6 @@
     protected static final String BUNDLE_ACTION = "bundle_action";
 
     private BackgroundWorker mBackgroundWorker;
-    private ConnectivityUtil mConnectivityUtil;
 
     /**
      * Allocate an intent with a specific opcode.
@@ -213,14 +211,11 @@
     public void onCreate() {
         super.onCreate();
         mBackgroundWorker = DataModel.get().getBackgroundWorkerForActionService();
-        mConnectivityUtil = DataModel.get().getConnectivityUtil();
-        mConnectivityUtil.registerForSignalStrength();
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        mConnectivityUtil.unregisterForSignalStrength();
     }
 
     /**
diff --git a/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java b/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
index e3d131d..45e2474 100644
--- a/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
+++ b/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
@@ -60,13 +60,12 @@
             final ContentValues values = new ContentValues();
             values.put(DatabaseHelper.MessageColumns.STATUS,
                     MessageData.BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED);
-            downloadFailedCnt += db.update(DatabaseHelper.MESSAGES_TABLE, values,
+            downloadFailedCnt = db.update(DatabaseHelper.MESSAGES_TABLE, values,
                     DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)",
                     new String[]{
                             Integer.toString(MessageData.BUGLE_STATUS_INCOMING_AUTO_DOWNLOADING),
                             Integer.toString(MessageData.BUGLE_STATUS_INCOMING_MANUAL_DOWNLOADING)
                     });
-            values.clear();
 
             values.clear();
             values.put(DatabaseHelper.MessageColumns.STATUS,
diff --git a/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java b/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
index 2567ca9..8bf6d5e 100644
--- a/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
@@ -118,7 +118,6 @@
      */
     @Override
     protected Object executeAction() {
-        LogUtil.i(TAG, "InsertNewMessageAction: inserting new message");
         MessageData message = actionParameters.getParcelable(KEY_MESSAGE);
         if (message == null) {
             LogUtil.i(TAG, "InsertNewMessageAction: Creating MessageData with provided data");
@@ -151,6 +150,8 @@
             return null;
         }
         final int subId = self.getSubId();
+        LogUtil.i(TAG, "InsertNewMessageAction: inserting new message for subId " + subId);
+        actionParameters.putInt(KEY_SUB_ID, subId);
 
         // TODO: Work out whether to send with SMS or MMS (taking into account recipients)?
         final boolean isSms = (message.getProtocol() == MessageData.PROTOCOL_SMS);
diff --git a/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java b/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
index ecbec10..f164148 100644
--- a/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
+++ b/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
@@ -17,76 +17,83 @@
 package com.android.messaging.datamodel.action;
 
 import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
 import android.database.Cursor;
-import android.net.ConnectivityManager;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
 
 import com.android.messaging.Factory;
 import com.android.messaging.datamodel.BugleDatabaseOperations;
 import com.android.messaging.datamodel.DataModel;
+import com.android.messaging.datamodel.DataModelImpl;
 import com.android.messaging.datamodel.DatabaseHelper;
 import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
-import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
 import com.android.messaging.datamodel.DatabaseWrapper;
 import com.android.messaging.datamodel.MessagingContentProvider;
 import com.android.messaging.datamodel.data.MessageData;
 import com.android.messaging.datamodel.data.ParticipantData;
-import com.android.messaging.sms.MmsUtils;
 import com.android.messaging.util.BugleGservices;
 import com.android.messaging.util.BugleGservicesKeys;
 import com.android.messaging.util.BuglePrefs;
 import com.android.messaging.util.BuglePrefsKeys;
+import com.android.messaging.util.ConnectivityUtil;
 import com.android.messaging.util.ConnectivityUtil.ConnectivityListener;
 import com.android.messaging.util.LogUtil;
 import com.android.messaging.util.OsUtil;
 import com.android.messaging.util.PhoneUtils;
 
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 /**
  * Action used to lookup any messages in the pending send/download state and either fail them or
- * retry their action. This action only initiates one retry at a time - further retries should be
- * triggered by successful sending of a message, network status change or exponential backoff timer.
+ * retry their action based on subscriptions. This action only initiates one retry at a time for
+ * both sending/downloading. Further retries should be triggered by successful sending/downloading
+ * of a message, network status change or exponential backoff timer.
  */
 public class ProcessPendingMessagesAction extends Action implements Parcelable {
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
-    private static final int PENDING_INTENT_REQUEST_CODE = 101;
+    // PENDING_INTENT_BASE_REQUEST_CODE + subId(-1 for pre-L_MR1) is used per subscription uniquely.
+    private static final int PENDING_INTENT_BASE_REQUEST_CODE = 103;
+
+    private static final String KEY_SUB_ID = "sub_id";
 
     public static void processFirstPendingMessage() {
-        // Clear any pending alarms or connectivity events
-        unregister();
-        // Clear retry count
-        setRetry(0);
-
-        // Start action
-        final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
-        action.start();
+        PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() {
+            @Override
+            public void runForSubscription(final int subId) {
+                // Clear any pending alarms or connectivity events
+                unregister(subId);
+                // Clear retry count
+                setRetry(0, subId);
+                // Start action
+                final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
+                action.actionParameters.putInt(KEY_SUB_ID, subId);
+                action.start();
+            }
+        });
     }
 
     public static void scheduleProcessPendingMessagesAction(final boolean failed,
             final Action processingAction) {
+        final int subId = processingAction.actionParameters
+                .getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
         LogUtil.i(TAG, "ProcessPendingMessagesAction: Scheduling pending messages"
-                + (failed ? "(message failed)" : ""));
+                + (failed ? "(message failed)" : "") + " for subId " + subId);
         // Can safely clear any pending alarms or connectivity events as either an action
         // is currently running or we will run now or register if pending actions possible.
-        unregister();
+        unregister(subId);
 
         final boolean isDefaultSmsApp = PhoneUtils.getDefault().isDefaultSmsApp();
         boolean scheduleAlarm = false;
         // If message succeeded and if Bugle is default SMS app just carry on with next message
         if (!failed && isDefaultSmsApp) {
             // Clear retry attempt count as something just succeeded
-            setRetry(0);
+            setRetry(0, subId);
 
-            // Lookup and queue next message for immediate processing by background worker
-            //  iff there are no pending messages this will do nothing and return true.
+            // Lookup and queue next message for each sending/downloading for immediate processing
+            // by background worker. If there are no pending messages, this will do nothing and
+            // return true.
             final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
             if (action.queueActions(processingAction)) {
                 if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
@@ -103,66 +110,53 @@
             scheduleAlarm = true;
             LogUtil.w(TAG, "ProcessPendingMessagesAction: Action failed to queue; retrying");
         }
-        if (getHavePendingMessages() || scheduleAlarm) {
+        if (getHavePendingMessages(subId) || scheduleAlarm) {
             // Still have a pending message that needs to be queued for processing
             final ConnectivityListener listener = new ConnectivityListener() {
                 @Override
-                public void onConnectivityStateChanged(final Context context, final Intent intent) {
-                    final int networkType =
-                            MmsUtils.getConnectivityEventNetworkType(context, intent);
-                    if (networkType != ConnectivityManager.TYPE_MOBILE) {
-                        return;
-                    }
-                    final boolean isConnected = !intent.getBooleanExtra(
-                            ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
-                    // TODO: Should we check in more detail?
-                    if (isConnected) {
-                        onConnected();
-                    }
-                }
-
-                @Override
-                public void onPhoneStateChanged(final Context context, final int serviceState) {
+                public void onPhoneStateChanged(final int serviceState) {
                     if (serviceState == ServiceState.STATE_IN_SERVICE) {
-                        onConnected();
+                        LogUtil.i(TAG, "ProcessPendingMessagesAction: Now connected for subId "
+                                + subId + ", starting action");
+
+                        // Clear any pending alarms or connectivity events but leave attempt count
+                        // alone
+                        unregister(subId);
+
+                        // Start action
+                        final ProcessPendingMessagesAction action =
+                                new ProcessPendingMessagesAction();
+                        action.actionParameters.putInt(KEY_SUB_ID, subId);
+                        action.start();
                     }
                 }
-
-                private void onConnected() {
-                    LogUtil.i(TAG, "ProcessPendingMessagesAction: Now connected; starting action");
-
-                    // Clear any pending alarms or connectivity events but leave attempt count alone
-                    unregister();
-
-                    // Start action
-                    final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
-                    action.start();
-                }
             };
             // Read and increment attempt number from shared prefs
-            final int retryAttempt = getNextRetry();
-            register(listener, retryAttempt);
+            final int retryAttempt = getNextRetry(subId);
+            register(listener, retryAttempt, subId);
         } else {
             // No more pending messages (presumably the message that failed has expired) or it
             // may be possible that a send and a download are already in process.
             // Clear retry attempt count.
             // TODO Might be premature if send and download in process...
-            //  but worst case means we try to send a bit more often.
-            setRetry(0);
-
-            if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
-                LogUtil.v(TAG, "ProcessPendingMessagesAction: No more pending messages");
-            }
+            // but worst case means we try to send a bit more often.
+            setRetry(0, subId);
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: No more pending messages");
         }
     }
 
-    private static void register(final ConnectivityListener listener, final int retryAttempt) {
+    private static void register(final ConnectivityListener listener, final int retryAttempt,
+            int subId) {
         int retryNumber = retryAttempt;
 
         // Register to be notified about connectivity changes
-        DataModel.get().getConnectivityUtil().register(listener);
+        ConnectivityUtil connectivityUtil = DataModelImpl.getConnectivityUtil(subId);
+        if (connectivityUtil != null) {
+            connectivityUtil.register(listener);
+        }
 
         final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
+        action.actionParameters.putInt(KEY_SUB_ID, subId);
         final long initialBackoffMs = BugleGservices.get().getLong(
                 BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS,
                 BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS_DEFAULT);
@@ -179,31 +173,34 @@
         while (retryNumber > 0 && nextDelayMs < maxDelayMs);
 
         LogUtil.i(TAG, "ProcessPendingMessagesAction: Registering for retry #" + retryAttempt
-                + " in " + delayMs + " ms");
+                + " in " + delayMs + " ms for subId " + subId);
 
-        action.schedule(PENDING_INTENT_REQUEST_CODE, delayMs);
+        action.schedule(PENDING_INTENT_BASE_REQUEST_CODE + subId, delayMs);
     }
 
-    private static void unregister() {
+    private static void unregister(final int subId) {
         // Clear any pending alarms or connectivity events
-        DataModel.get().getConnectivityUtil().unregister();
+        ConnectivityUtil connectivityUtil = DataModelImpl.getConnectivityUtil(subId);
+        if (connectivityUtil != null) {
+            connectivityUtil.unregister();
+        }
 
         final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
-        action.schedule(PENDING_INTENT_REQUEST_CODE, Long.MAX_VALUE);
+        action.schedule(PENDING_INTENT_BASE_REQUEST_CODE + subId, Long.MAX_VALUE);
 
         if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
             LogUtil.v(TAG, "ProcessPendingMessagesAction: Unregistering for connectivity changed "
-                    + "events and clearing scheduled alarm");
+                    + "events and clearing scheduled alarm for subId " + subId);
         }
     }
 
-    private static void setRetry(final int retryAttempt) {
-        final BuglePrefs prefs = Factory.get().getApplicationPrefs();
+    private static void setRetry(final int retryAttempt, int subId) {
+        final BuglePrefs prefs = Factory.get().getSubscriptionPrefs(subId);
         prefs.putInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, retryAttempt);
     }
 
-    private static int getNextRetry() {
-        final BuglePrefs prefs = Factory.get().getApplicationPrefs();
+    private static int getNextRetry(int subId) {
+        final BuglePrefs prefs = Factory.get().getSubscriptionPrefs(subId);
         final int retryAttempt =
                 prefs.getInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, 0) + 1;
         prefs.putInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, retryAttempt);
@@ -215,21 +212,27 @@
 
     /**
      * Read from the DB and determine if there are any messages we should process
+     *
+     * @param subId the subId
      * @return true if we have pending messages
      */
-    private static boolean getHavePendingMessages() {
+    private static boolean getHavePendingMessages(final int subId) {
         final DatabaseWrapper db = DataModel.get().getDatabase();
         final long now = System.currentTimeMillis();
+        final String selfId = ParticipantData.getParticipantId(db, subId);
+        if (selfId == null) {
+            // This could be happened before refreshing participant.
+            LogUtil.w(TAG, "ProcessPendingMessagesAction: selfId is null for subId " + subId);
+            return false;
+        }
 
-        for (int subId : getActiveSubscriptionIds()) {
-            final String toSendMessageId = findNextMessageToSend(db, now, subId);
-            if (toSendMessageId != null) {
+        final String toSendMessageId = findNextMessageToSend(db, now, selfId);
+        if (toSendMessageId != null) {
+            return true;
+        } else {
+            final String toDownloadMessageId = findNextMessageToDownload(db, now, selfId);
+            if (toDownloadMessageId != null) {
                 return true;
-            } else {
-                final String toDownloadMessageId = findNextMessageToDownload(db, now, subId);
-                if (toDownloadMessageId != null) {
-                    return true;
-                }
             }
         }
         // Messages may be in the process of sending/downloading even when there are no pending
@@ -237,76 +240,65 @@
         return false;
     }
 
-    private static int[] getActiveSubscriptionIds() {
-        if (!OsUtil.isAtLeastL_MR1()) {
-            return new int[] { ParticipantData.DEFAULT_SELF_SUB_ID };
-        }
-        List<SubscriptionInfo> subscriptions = PhoneUtils.getDefault().toLMr1()
-            .getActiveSubscriptionInfoList();
-
-        int numSubs = subscriptions.size();
-        int[] result = new int[numSubs];
-        for (int i = 0; i < numSubs; i++) {
-            result[i] = subscriptions.get(i).getSubscriptionId();
-        }
-        return result;
-    }
-
     /**
      * Queue any pending actions
+     *
      * @param actionState
      * @return true if action queued (or no actions to queue) else false
      */
     private boolean queueActions(final Action processingAction) {
         final DatabaseWrapper db = DataModel.get().getDatabase();
         final long now = System.currentTimeMillis();
-        boolean succeeded = false;
+        boolean succeeded = true;
+        final int subId = processingAction.actionParameters
+                .getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
 
-        // Will queue no more than one message per subscription to send plus one message to download
+        LogUtil.i(TAG, "ProcessPendingMessagesAction: Start queueing for subId " + subId);
+
+        final String selfId = ParticipantData.getParticipantId(db, subId);
+        if (selfId == null) {
+            // This could be happened before refreshing participant.
+            LogUtil.w(TAG, "ProcessPendingMessagesAction: selfId is null");
+            return false;
+        }
+
+        // Will queue no more than one message to send plus one message to download
         // This keeps outgoing messages "in order" but allow downloads to happen even if sending
-        //  gets blocked until messages time out.  Manual resend bumps messages to head of queue.
-        for (int subId : getActiveSubscriptionIds()) {
-            final String toSendMessageId = findNextMessageToSend(db, now, subId);
-            final String toDownloadMessageId = findNextMessageToDownload(db, now, subId);
-            if (toSendMessageId != null) {
-                LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toSendMessageId
-                        + " for sending");
-                // This could queue nothing
-                if (!SendMessageAction.queueForSendInBackground(toSendMessageId,
-                            processingAction)) {
-                    LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
-                            + toSendMessageId + " for sending");
-                } else {
-                    succeeded = true;
-                }
+        // gets blocked until messages time out. Manual resend bumps messages to head of queue.
+        final String toSendMessageId = findNextMessageToSend(db, now, selfId);
+        final String toDownloadMessageId = findNextMessageToDownload(db, now, selfId);
+        if (toSendMessageId != null) {
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toSendMessageId
+                    + " for sending");
+            // This could queue nothing
+            if (!SendMessageAction.queueForSendInBackground(toSendMessageId, processingAction)) {
+                LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
+                        + toSendMessageId + " for sending");
+                succeeded = false;
             }
-            if (toDownloadMessageId != null) {
-                LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message "
+        }
+        if (toDownloadMessageId != null) {
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toDownloadMessageId
+                    + " for download");
+            // This could queue nothing
+            if (!DownloadMmsAction.queueMmsForDownloadInBackground(toDownloadMessageId,
+                    processingAction)) {
+                LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
                         + toDownloadMessageId + " for download");
-                // This could queue nothing
-                if (!DownloadMmsAction.queueMmsForDownloadInBackground(toDownloadMessageId,
-                            processingAction)) {
-                    LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
-                            + toDownloadMessageId + " for download");
-                } else {
-                    succeeded = true;
-                }
-
+                succeeded = false;
             }
-            if (toSendMessageId == null && toDownloadMessageId == null) {
-                if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
-                    LogUtil.d(TAG, "ProcessPendingMessagesAction: No messages to send or download");
-                }
-                succeeded = true;
-            }
+        }
+        if (toSendMessageId == null && toDownloadMessageId == null) {
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: No messages to send or download");
         }
         return succeeded;
     }
 
     @Override
     protected Object executeAction() {
+        final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
         // If triggered by alarm will not have unregistered yet
-        unregister();
+        unregister(subId);
 
         if (PhoneUtils.getDefault().isDefaultSmsApp()) {
             queueActions(this);
@@ -320,83 +312,57 @@
         return null;
     }
 
-    private static String prefixColumnWithTable(final  String tableName, final String column) {
-        return tableName + "." + column;
-    }
-
-    private static String[] prefixProjectionWithTable(final String tableName,
-            final String[] projection) {
-        String[] result = new String[projection.length];
-        for (int i = 0; i < projection.length; i++) {
-            result[i] = prefixColumnWithTable(tableName, projection[i]);
-        }
-        return result;
-    }
-
     private static String findNextMessageToSend(final DatabaseWrapper db, final long now,
-            final int subId) {
+            final String selfId) {
         String toSendMessageId = null;
-        db.beginTransaction();
-        Cursor sending = null;
         Cursor cursor = null;
         int sendingCnt = 0;
         int pendingCnt = 0;
         int failedCnt = 0;
+        db.beginTransaction();
         try {
-            String[] projection = prefixProjectionWithTable(DatabaseHelper.MESSAGES_TABLE,
-                    MessageData.getProjection());
-            String subIdClause =
-                    prefixColumnWithTable(DatabaseHelper.MESSAGES_TABLE,
-                            MessageColumns.SELF_PARTICIPANT_ID)
-                    + " = "
-                    + prefixColumnWithTable(DatabaseHelper.PARTICIPANTS_TABLE,
-                            ParticipantColumns._ID)
-                    + " AND " + ParticipantColumns.SUB_ID + " =?";
-
             // First check to see if we have any messages already sending
-            sending = db.query(DatabaseHelper.MESSAGES_TABLE + ","
-                            + DatabaseHelper.PARTICIPANTS_TABLE,
-                            projection,
-                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)"
-                            + " AND " + subIdClause,
-                    new String[]{Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_SENDING),
-                           Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_RESENDING),
-                           Integer.toString(subId)},
-                    null,
-                    null,
-                    DatabaseHelper.MessageColumns.RECEIVED_TIMESTAMP + " ASC");
-            final boolean messageCurrentlySending = sending.moveToNext();
-            sendingCnt = sending.getCount();
-            // Look for messages we could send
-            final ContentValues values = new ContentValues();
-            values.put(DatabaseHelper.MessageColumns.STATUS,
-                    MessageData.BUGLE_STATUS_OUTGOING_FAILED);
-            cursor = db.query(DatabaseHelper.MESSAGES_TABLE + ","
-                            + DatabaseHelper.PARTICIPANTS_TABLE,
-                    projection,
-                    DatabaseHelper.MessageColumns.STATUS + " IN ("
-                            + MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND + ","
-                            + MessageData.BUGLE_STATUS_OUTGOING_AWAITING_RETRY + ")"
-                            + " AND " + subIdClause,
-                    new String[]{Integer.toString(subId)},
+            sendingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE,
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
+                    new String[] {
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_SENDING),
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_RESENDING),
+                        selfId}
+                    );
+
+            // Look for messages we cound send
+            cursor = db.query(DatabaseHelper.MESSAGES_TABLE,
+                    MessageData.getProjection(),
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
+                    new String[] {
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND),
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_AWAITING_RETRY),
+                        selfId
+                    },
                     null,
                     null,
                     DatabaseHelper.MessageColumns.RECEIVED_TIMESTAMP + " ASC");
             pendingCnt = cursor.getCount();
 
+            final ContentValues values = new ContentValues();
+            values.put(DatabaseHelper.MessageColumns.STATUS,
+                    MessageData.BUGLE_STATUS_OUTGOING_FAILED);
+
             while (cursor.moveToNext()) {
                 final MessageData message = new MessageData();
                 message.bind(cursor);
                 if (message.getInResendWindow(now)) {
                     // If no messages currently sending
-                    if (!messageCurrentlySending) {
+                    if (sendingCnt == 0) {
                         // Resend this message
                         toSendMessageId = message.getMessageId();
                         // Before queuing the message for resending, check if the message's self is
                         // active. If not, switch back to the system's default subscription.
                         if (OsUtil.isAtLeastL_MR1()) {
                             final ParticipantData messageSelf = BugleDatabaseOperations
-                                    .getExistingParticipant(db, message.getSelfId());
+                                    .getExistingParticipant(db, selfId);
                             if (messageSelf == null || !messageSelf.isActiveSubscription()) {
                                 final ParticipantData defaultSelf = BugleDatabaseOperations
                                         .getOrCreateSelf(db, PhoneUtils.getDefault()
@@ -429,9 +395,6 @@
             if (cursor != null) {
                 cursor.close();
             }
-            if (sending != null) {
-                sending.close();
-            }
         }
 
         if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
@@ -445,48 +408,33 @@
     }
 
     private static String findNextMessageToDownload(final DatabaseWrapper db, final long now,
-            final int subId) {
+            final String selfId) {
         String toDownloadMessageId = null;
-        db.beginTransaction();
         Cursor cursor = null;
         int downloadingCnt = 0;
         int pendingCnt = 0;
+        db.beginTransaction();
         try {
-            String[] projection = prefixProjectionWithTable(DatabaseHelper.MESSAGES_TABLE,
-                    MessageData.getProjection());
-            String subIdClause =
-                    prefixColumnWithTable(DatabaseHelper.MESSAGES_TABLE,
-                            MessageColumns.SELF_PARTICIPANT_ID)
-                    + " = "
-                    + prefixColumnWithTable(DatabaseHelper.PARTICIPANTS_TABLE,
-                            ParticipantColumns._ID)
-                    + " AND " + ParticipantColumns.SUB_ID + " =?";
-
             // First check if we have any messages already downloading
-            downloadingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE
-                    + "," + DatabaseHelper.PARTICIPANTS_TABLE,
-                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)"
-                            + " AND " + subIdClause,
+            downloadingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE,
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =?",
                     new String[] {
                         Integer.toString(MessageData.BUGLE_STATUS_INCOMING_AUTO_DOWNLOADING),
                         Integer.toString(MessageData.BUGLE_STATUS_INCOMING_MANUAL_DOWNLOADING),
-                        Integer.toString(subId)
+                        selfId
                     });
 
             // TODO: This query is not actually needed if downloadingCnt == 0.
-            cursor = db.query(DatabaseHelper.MESSAGES_TABLE + ","
-                    + DatabaseHelper.PARTICIPANTS_TABLE,
-                    projection,
-                    DatabaseHelper.MessageColumns.STATUS + " =? OR "
-                            + DatabaseHelper.MessageColumns.STATUS + " =?"
-                            + " AND " + subIdClause,
+            cursor = db.query(DatabaseHelper.MESSAGES_TABLE,
+                    MessageData.getProjection(),
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
                     new String[]{
-                            Integer.toString(
-                                    MessageData.BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD),
-                            Integer.toString(
-                                    MessageData.BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD),
-                            Integer.toString(
-                                    subId)
+                        Integer.toString(MessageData.BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD),
+                        Integer.toString(
+                                MessageData.BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD),
+                        selfId
                     },
                     null,
                     null,
diff --git a/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java b/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
index 5ffb35d..cb7a35d 100644
--- a/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
@@ -44,6 +44,7 @@
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
 
     private static final String KEY_MESSAGE_VALUES = "message_values";
+    private static final String KEY_SUB_ID = "sub_id";
 
     /**
      * Create a message received from a particular number in a particular conversation
@@ -158,6 +159,7 @@
                     + " in conversation " + message.getConversationId()
                     + ", uri = " + messageUri);
 
+            actionParameters.putInt(KEY_SUB_ID, subId);
             ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
         } else {
             if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
diff --git a/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java b/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
index e899b0c..69c34da 100644
--- a/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
+++ b/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
@@ -36,7 +36,9 @@
  */
 public class RedownloadMmsAction extends Action implements Parcelable {
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
-    private static final int REQUEST_CODE_PENDING_INTENT = 102;
+    private static final int REQUEST_CODE_PENDING_INTENT = 101;
+
+    private static final String KEY_SUB_ID = "sub_id";
 
     /**
      * Download an MMS message
@@ -90,7 +92,8 @@
             BugleDatabaseOperations.updateMessageRow(db, message.getMessageId(), values);
 
             MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
-
+            actionParameters.putInt(KEY_SUB_ID,
+                    BugleDatabaseOperations.getSelfSubscriptionId(db, message.getSelfId()));
             // Whether we succeeded or failed we will check and maybe schedule some more work
             ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
         } else {
diff --git a/src/com/android/messaging/datamodel/action/ResendMessageAction.java b/src/com/android/messaging/datamodel/action/ResendMessageAction.java
index 2201965..0943f72 100644
--- a/src/com/android/messaging/datamodel/action/ResendMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ResendMessageAction.java
@@ -34,6 +34,8 @@
 public class ResendMessageAction extends Action implements Parcelable {
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
 
+    private static final String KEY_SUB_ID = "sub_id";
+
     /**
      * Manual send of existing message (no listener)
      */
@@ -87,6 +89,9 @@
 
             MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
 
+            actionParameters.putInt(KEY_SUB_ID,
+                    BugleDatabaseOperations.getSelfSubscriptionId(db, message.getSelfId()));
+
             // Whether we succeeded or failed we will check and maybe schedule some more work
             ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
 
diff --git a/src/com/android/messaging/datamodel/data/ParticipantData.java b/src/com/android/messaging/datamodel/data/ParticipantData.java
index 51e6ee2..95c74e2 100644
--- a/src/com/android/messaging/datamodel/data/ParticipantData.java
+++ b/src/com/android/messaging/datamodel/data/ParticipantData.java
@@ -22,10 +22,12 @@
 import android.graphics.Color;
 import android.os.Parcel;
 import android.os.Parcelable;
-import androidx.appcompat.mms.MmsManager;
 import android.telephony.SubscriptionInfo;
 import android.text.TextUtils;
 
+import androidx.appcompat.mms.MmsManager;
+import androidx.collection.ArrayMap;
+
 import com.android.ex.chips.RecipientEntry;
 import com.android.messaging.Factory;
 import com.android.messaging.R;
@@ -41,6 +43,10 @@
  * A class that encapsulates all of the data for a specific participant in a conversation.
  */
 public class ParticipantData implements Parcelable {
+
+    private static final ArrayMap<Integer, String> sSubIdtoParticipantIdCache =
+            new ArrayMap<Integer, String>();
+
     // We always use -1 as default/invalid sub id although system may give us anything negative
     public static final int DEFAULT_SELF_SUB_ID = MmsManager.DEFAULT_SUB_ID;
 
@@ -282,6 +288,34 @@
         return pd;
     }
 
+    public static String getParticipantId(final DatabaseWrapper db, final int subId) {
+        String id;
+        synchronized (sSubIdtoParticipantIdCache) {
+            id = sSubIdtoParticipantIdCache.get(subId);
+        }
+
+        if (id != null) {
+            return id;
+        }
+
+        try (final Cursor cursor =
+                    db.query(DatabaseHelper.PARTICIPANTS_TABLE,
+                            new String[] {ParticipantColumns._ID},
+                            ParticipantColumns.SUB_ID + " =?",
+                            new String[] {Integer.toString(subId)}, null, null, null)) {
+
+            if (cursor.moveToFirst()) {
+                // We found an existing participant in the database
+                id = cursor.getString(0);
+                synchronized (sSubIdtoParticipantIdCache) {
+                    // Add it to the cache for next time
+                    sSubIdtoParticipantIdCache.put(subId, id);
+                }
+            }
+        }
+        return id;
+    }
+
     private void maybeSetupUnknownSender() {
         if (isUnknownSender()) {
             // Because your locale may change, we setup the display string for the unknown sender
diff --git a/src/com/android/messaging/sms/MmsUtils.java b/src/com/android/messaging/sms/MmsUtils.java
index df0db34..01b1e7e 100644
--- a/src/com/android/messaging/sms/MmsUtils.java
+++ b/src/com/android/messaging/sms/MmsUtils.java
@@ -27,8 +27,6 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
 import android.media.MediaMetadataRetriever;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -2662,26 +2660,6 @@
     }
 
     /**
-     * The absence of a connection type.
-     */
-    public static final int TYPE_NONE = -1;
-
-    public static int getConnectivityEventNetworkType(final Context context, final Intent intent) {
-        final ConnectivityManager connMgr = (ConnectivityManager)
-                context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        if (OsUtil.isAtLeastJB_MR1()) {
-            return intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, TYPE_NONE);
-        } else {
-            final NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
-                    ConnectivityManager.EXTRA_NETWORK_INFO);
-            if (info != null) {
-                return info.getType();
-            }
-        }
-        return TYPE_NONE;
-    }
-
-    /**
      * Dump the raw MMS data into a file
      *
      * @param rawPdu The raw pdu data
diff --git a/src/com/android/messaging/util/BuglePrefsKeys.java b/src/com/android/messaging/util/BuglePrefsKeys.java
index ae409bc..a30b5a3 100644
--- a/src/com/android/messaging/util/BuglePrefsKeys.java
+++ b/src/com/android/messaging/util/BuglePrefsKeys.java
@@ -66,6 +66,6 @@
      * The attempt number when retrying ProcessPendingMessagesAction
      */
     public static final String PROCESS_PENDING_MESSAGES_RETRY_COUNT
-            = "process_pending_retry";
+            = BuglePrefs.SHARED_PREFERENCES_PER_SUBSCRIPTION_PREFIX + "process_pending_retry";
 
 }
diff --git a/src/com/android/messaging/util/ConnectivityUtil.java b/src/com/android/messaging/util/ConnectivityUtil.java
index 49f6e0a..0229607 100644
--- a/src/com/android/messaging/util/ConnectivityUtil.java
+++ b/src/com/android/messaging/util/ConnectivityUtil.java
@@ -16,40 +16,33 @@
 
 package com.android.messaging.util;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 
+import com.android.messaging.datamodel.data.ParticipantData;
+
 public class ConnectivityUtil {
     // Assume not connected until informed differently
     private volatile int mCurrentServiceState = ServiceState.STATE_POWER_OFF;
 
     private final TelephonyManager mTelephonyManager;
-    private final Context mContext;
-    private final ConnectivityBroadcastReceiver mReceiver;
-    private final ConnectivityManager mConnMgr;
 
     private ConnectivityListener mListener;
-    private final IntentFilter mIntentFilter;
 
     public interface ConnectivityListener {
-        public void onConnectivityStateChanged(final Context context, final Intent intent);
-        public void onPhoneStateChanged(final Context context, int serviceState);
+        public void onPhoneStateChanged(int serviceState);
     }
 
     public ConnectivityUtil(final Context context) {
-        mContext = context;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mReceiver = new ConnectivityBroadcastReceiver();
-        mIntentFilter = new IntentFilter();
-        mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+    }
+
+    public ConnectivityUtil(final Context context, final int subId) {
+        mTelephonyManager =
+                ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+                        .createForSubscriptionId(subId);
     }
 
     public int getCurrentServiceState() {
@@ -75,14 +68,7 @@
     private void onPhoneStateChanged(final int serviceState) {
         final ConnectivityListener listener = mListener;
         if (listener != null) {
-            listener.onPhoneStateChanged(mContext, serviceState);
-        }
-    }
-
-    private void onConnectivityChanged(final Context context, final Intent intent) {
-        final ConnectivityListener listener = mListener;
-        if (listener != null) {
-            listener.onConnectivityStateChanged(context, intent);
+            listener.onPhoneStateChanged(serviceState);
         }
     }
 
@@ -95,9 +81,6 @@
                 mTelephonyManager.listen(mPhoneStateListener,
                         PhoneStateListener.LISTEN_SERVICE_STATE);
             }
-            if (mConnMgr != null) {
-                mContext.registerReceiver(mReceiver, mIntentFilter);
-            }
         }
         mListener = listener;
     }
@@ -108,140 +91,7 @@
                 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
                 mCurrentServiceState = ServiceState.STATE_POWER_OFF;
             }
-            if (mConnMgr != null) {
-                mContext.unregisterReceiver(mReceiver);
-            }
         }
         mListener = null;
     }
-
-    /**
-     * Connectivity change broadcast receiver. This gets the network connectivity updates.
-     * In case we don't get the active connectivity when we first acquire the network,
-     * this receiver will notify us when it is connected, so to unblock the waiting thread
-     * which is sending the message.
-     */
-    public class ConnectivityBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(final Context context, final Intent intent) {
-            if (!intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-                return;
-            }
-
-            onConnectivityChanged(context, intent);
-        }
-    }
-
-    private int mSignalLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-    // We use a separate instance than mPhoneStateListener because the lifetimes are different.
-    private final PhoneStateListener mSignalStrengthListener = new PhoneStateListener() {
-        @Override
-        public void onSignalStrengthsChanged(final SignalStrength signalStrength) {
-            mSignalLevel = getLevel(signalStrength);
-        }
-    };
-
-    public void registerForSignalStrength() {
-        mTelephonyManager.listen(
-                mSignalStrengthListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
-    }
-
-    public void unregisterForSignalStrength() {
-        mTelephonyManager.listen(mSignalStrengthListener, PhoneStateListener.LISTEN_NONE);
-    }
-
-    /**
-     * @param subId This is ignored because TelephonyManager does not support it.
-     * @return Signal strength as level 0..4
-     */
-    public int getSignalLevel(final int subId) {
-        return mSignalLevel;
-    }
-
-    private static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
-    private static final int SIGNAL_STRENGTH_POOR = 1;
-    private static final int SIGNAL_STRENGTH_MODERATE = 2;
-    private static final int SIGNAL_STRENGTH_GOOD = 3;
-    private static final int SIGNAL_STRENGTH_GREAT = 4;
-
-    private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
-    private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
-    private static final int GSM_SIGNAL_STRENGTH_MODERATE = 8;
-
-    private static int getLevel(final SignalStrength signalStrength) {
-        if (signalStrength.isGsm()) {
-            // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthGsm.java
-
-            // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-            // asu = 0 (-113dB or less) is very weak
-            // signal, its better to show 0 bars to the user in such cases.
-            // asu = 99 is a special case, where the signal strength is unknown.
-            final int asu = signalStrength.getGsmSignalStrength();
-            if (asu <= 2 || asu == 99) return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-            else if (asu >= GSM_SIGNAL_STRENGTH_GREAT) return SIGNAL_STRENGTH_GREAT;
-            else if (asu >= GSM_SIGNAL_STRENGTH_GOOD) return SIGNAL_STRENGTH_GOOD;
-            else if (asu >= GSM_SIGNAL_STRENGTH_MODERATE) return SIGNAL_STRENGTH_MODERATE;
-            else return SIGNAL_STRENGTH_POOR;
-        } else {
-            // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthCdma.java
-
-            final int cdmaLevel = getCdmaLevel(signalStrength);
-            final int evdoLevel = getEvdoLevel(signalStrength);
-            if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                /* We don't know evdo, use cdma */
-                return getCdmaLevel(signalStrength);
-            } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                /* We don't know cdma, use evdo */
-                return getEvdoLevel(signalStrength);
-            } else {
-                /* We know both, use the lowest level */
-                return cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
-            }
-        }
-    }
-
-    /**
-     * Get cdma as level 0..4
-     */
-    private static int getCdmaLevel(final SignalStrength signalStrength) {
-        final int cdmaDbm = signalStrength.getCdmaDbm();
-        final int cdmaEcio = signalStrength.getCdmaEcio();
-        int levelDbm;
-        int levelEcio;
-        if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
-        else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
-        else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
-        else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
-        else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        // Ec/Io are in dB*10
-        if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
-        else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
-        else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
-        else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
-        else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        final int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
-        return level;
-    }
-    /**
-     * Get Evdo as level 0..4
-     */
-    private static int getEvdoLevel(final SignalStrength signalStrength) {
-        final int evdoDbm = signalStrength.getEvdoDbm();
-        final int evdoSnr = signalStrength.getEvdoSnr();
-        int levelEvdoDbm;
-        int levelEvdoSnr;
-        if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
-        else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
-        else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
-        else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
-        else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
-        else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
-        else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
-        else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
-        else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        final int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
-        return level;
-    }
 }
diff --git a/tests/src/com/android/messaging/FakeFactory.java b/tests/src/com/android/messaging/FakeFactory.java
index 41ede77..4c7c9de 100644
--- a/tests/src/com/android/messaging/FakeFactory.java
+++ b/tests/src/com/android/messaging/FakeFactory.java
@@ -46,6 +46,7 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class FakeFactory extends Factory {
@@ -121,7 +122,8 @@
 
                         @Override
                         public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-                            return null;
+                            // Return empty list
+                            return new ArrayList<>();
                         }
 
                         @Override
diff --git a/tests/src/com/android/messaging/datamodel/DataModelTest.java b/tests/src/com/android/messaging/datamodel/DataModelTest.java
index 71723a4..0a1bccd 100644
--- a/tests/src/com/android/messaging/datamodel/DataModelTest.java
+++ b/tests/src/com/android/messaging/datamodel/DataModelTest.java
@@ -35,10 +35,10 @@
 
     @Override
     protected void setUp() throws Exception {
-      super.setUp();
-      dataModel = new DataModelImpl(getTestContext());
-      FakeFactory.register(mContext)
-              .withDataModel(dataModel);
+        super.setUp();
+        FakeFactory factory = FakeFactory.register(mContext);
+        dataModel = new DataModelImpl(getTestContext());
+        factory.withDataModel(dataModel);
     }
 
     @SmallTest
diff --git a/tests/src/com/android/messaging/datamodel/FakeDataModel.java b/tests/src/com/android/messaging/datamodel/FakeDataModel.java
index 5e80eab..1daaa20 100644
--- a/tests/src/com/android/messaging/datamodel/FakeDataModel.java
+++ b/tests/src/com/android/messaging/datamodel/FakeDataModel.java
@@ -48,7 +48,6 @@
 import com.android.messaging.datamodel.data.SubscriptionListData;
 import com.android.messaging.datamodel.data.TestDataFactory;
 import com.android.messaging.datamodel.data.VCardContactItemData;
-import com.android.messaging.util.ConnectivityUtil;
 
 public class FakeDataModel extends DataModel {
     private BackgroundWorker mWorker;
@@ -58,7 +57,6 @@
     private ContactPickerData mContactPickerData;
     private MediaPickerData mMediaPickerData;
     private PeopleAndOptionsData mPeopleAndOptionsData;
-    private ConnectivityUtil mConnectivityUtil;
     private SyncManager mSyncManager;
     private SettingsData mSettingsData;
     private DraftMessageData mDraftMessageData;
@@ -102,11 +100,6 @@
         return this;
     }
 
-    public FakeDataModel withConnectivityUtil(final ConnectivityUtil connectivityUtil) {
-        mConnectivityUtil = connectivityUtil;
-        return this;
-    }
-
     public FakeDataModel withSyncManager(final SyncManager syncManager) {
         mSyncManager = syncManager;
         return this;
@@ -220,11 +213,6 @@
     }
 
     @Override
-    public ConnectivityUtil getConnectivityUtil() {
-        return mConnectivityUtil;
-    }
-
-    @Override
     public SyncManager getSyncManager() {
         return mSyncManager;
     }
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java b/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
index 97e0f10..4d1ad5d 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
@@ -35,7 +35,6 @@
 import com.android.messaging.datamodel.action.ActionMonitor.ActionExecutedListener;
 import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader;
 
 import java.util.ArrayList;
@@ -291,8 +290,7 @@
         FakeFactory.registerWithFakeContext(getContext(), mContext)
                 .withDataModel(new FakeDataModel(mContext)
                 .withBackgroundWorkerForActionService(mWorker)
-                .withActionService(new ActionService())
-                .withConnectivityUtil(new StubConnectivityUtil(mContext)));
+                .withActionService(new ActionService()));
 
         mLoader = new StubLoader();
         setContext(Factory.get().getApplicationContext());
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java b/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
index 6f66fa9..5eb0f67 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
@@ -35,7 +35,6 @@
 import com.android.messaging.datamodel.action.ActionMonitor.ActionStateChangedListener;
 import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader;
 
 import java.util.ArrayList;
@@ -145,8 +144,7 @@
         FakeFactory.registerWithFakeContext(getContext(),mContext)
                 .withDataModel(new FakeDataModel(mContext)
                 .withBackgroundWorkerForActionService(mWorker)
-                .withActionService(new ActionService())
-                .withConnectivityUtil(new StubConnectivityUtil(mContext)));
+                .withActionService(new ActionService()));
 
         mStates = new ArrayList<Integer>();
         setContext(Factory.get().getApplicationContext());
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java b/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
index d72a0f9..00aa267 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
@@ -21,8 +21,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 
-import com.android.messaging.util.ConnectivityUtil;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -174,18 +172,4 @@
             }
         }
     }
-
-    public static class StubConnectivityUtil extends ConnectivityUtil {
-        public StubConnectivityUtil(final Context context) {
-            super(context);
-        }
-
-        @Override
-        public void registerForSignalStrength() {
-        }
-
-        @Override
-        public void unregisterForSignalStrength() {
-        }
-    }
 }
diff --git a/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java b/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
index 0405c90..e977aa7 100644
--- a/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
@@ -36,7 +36,6 @@
 import com.android.messaging.datamodel.MessagingContentProvider;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService.StubActionServiceCallLog;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
 import com.android.messaging.datamodel.action.ReadDraftDataAction.ReadDraftDataActionListener;
 import com.android.messaging.datamodel.data.MessageData;
 import com.android.messaging.datamodel.data.MessagePartData;
@@ -473,8 +472,7 @@
 
         mService = new StubActionService();
         final FakeDataModel fakeDataModel = new FakeDataModel(context)
-                .withActionService(mService)
-                .withConnectivityUtil(new StubConnectivityUtil(context));
+                .withActionService(mService);
         FakeFactory.registerWithFakeContext(getTestContext(), context)
                 .withDataModel(fakeDataModel);