Merge "Fix wrong service name"
diff --git a/src/com/android/ons/ONSNetworkScanCtlr.java b/src/com/android/ons/ONSNetworkScanCtlr.java
index 243b217..181a56c 100644
--- a/src/com/android/ons/ONSNetworkScanCtlr.java
+++ b/src/com/android/ons/ONSNetworkScanCtlr.java
@@ -29,13 +29,12 @@
 import android.telephony.NetworkScan;
 import android.telephony.NetworkScanRequest;
 import android.telephony.RadioAccessSpecifier;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyScanManager;
 import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/com/android/ons/ONSProfileSelector.java b/src/com/android/ons/ONSProfileSelector.java
index 6a156d8..d92e2bd 100644
--- a/src/com/android/ons/ONSProfileSelector.java
+++ b/src/com/android/ons/ONSProfileSelector.java
@@ -20,10 +20,9 @@
 import static android.telephony.AvailableNetworkInfo.PRIORITY_LOW;
 
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
+import android.compat.Compatibility;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -33,7 +32,6 @@
 import android.telephony.AvailableNetworkInfo;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoLte;
-import android.telephony.Rlog;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -44,6 +42,7 @@
 import com.android.internal.telephony.ISetOpportunisticDataCallback;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -74,6 +73,9 @@
     /* message to indicate Subscription switch completion */
     private static final int MSG_SUB_SWITCH_COMPLETE = 3;
 
+    /* message to stop profile selection process */
+    private static final int MSG_STOP_PROFILE_SELECTION = 4;
+
     private boolean mIsEnabled = false;
 
     @VisibleForTesting
@@ -91,10 +93,13 @@
     protected SubscriptionManager mSubscriptionManager;
     @VisibleForTesting
     protected List<SubscriptionInfo> mOppSubscriptionInfos;
+    @VisibleForTesting
+    protected List<SubscriptionInfo> mStandaloneOppSubInfos;
     private ONSProfileSelectionCallback mProfileSelectionCallback;
     private int mSequenceId;
     private int mSubId;
-    private int mCurrentDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    @VisibleForTesting
+    protected int mCurrentDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private ArrayList<AvailableNetworkInfo> mAvailableNetworkInfos;
     private IUpdateAvailableNetworksCallback mNetworkScanCallback;
 
@@ -137,8 +142,22 @@
                             handleNetworkScanResult(mAvailableNetworkInfos.get(0).getSubId());
                         } else {
                             if (mNetworkScanCallback != null) {
-                                sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
-                                    TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                                if (mIsEnabled) {
+                                    sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
+                                            TelephonyManager
+                                                    .UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                                } else {
+                                    if (Compatibility.isChangeEnabled(
+                                            TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                                        sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
+                                                TelephonyManager
+                                                        .UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED);
+                                    } else {
+                                        sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
+                                                TelephonyManager
+                                                        .UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE);
+                                    }
+                                }
                                 mNetworkScanCallback = null;
                             }
                         }
@@ -152,8 +171,15 @@
                             sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
                                 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
                         } else {
-                            sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
-                                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+                            if (Compatibility.isChangeEnabled(
+                                    TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                                sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
+                                        TelephonyManager
+                                                .UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL);
+                            } else {
+                                sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
+                                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+                            }
                         }
                         mProfileSelectionCallback.onProfileSelectionDone();
                         synchronized (mLock) {
@@ -352,8 +378,14 @@
             sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
                 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
         } else {
-            sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
-                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+            if (Compatibility.isChangeEnabled(
+                    TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL);
+            } else {
+                sendUpdateNetworksCallbackHelper(mNetworkScanCallback,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+            }
         }
         mProfileSelectionCallback.onProfileSelectionDone();
         mNetworkScanCallback = null;
@@ -451,8 +483,14 @@
                 (IUpdateAvailableNetworksCallback) objects[1];
         if (mOppSubscriptionInfos == null) {
             logDebug("null subscription infos");
-            sendUpdateNetworksCallbackHelper(callbackStub,
-                    TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+            if (Compatibility.isChangeEnabled(
+                    TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
+            } else {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+            }
             return;
         }
 
@@ -499,8 +537,15 @@
                         sendUpdateNetworksCallbackHelper(callbackStub,
                             TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
                     } else {
-                        sendUpdateNetworksCallbackHelper(callbackStub,
-                            TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+                        if (Compatibility.isChangeEnabled(
+                                TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                            sendUpdateNetworksCallbackHelper(callbackStub,
+                                    TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL);
+                        } else {
+                            sendUpdateNetworksCallbackHelper(callbackStub,
+                                    TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+                        }
+
                     }
                     mProfileSelectionCallback.onProfileSelectionDone();
                     mAvailableNetworkInfos = null;
@@ -511,8 +556,14 @@
                 mNetworkScanCtlr.startFastNetworkScan(filteredAvailableNetworks);
             }
         } else if (mOppSubscriptionInfos.size() == 0) {
-            sendUpdateNetworksCallbackHelper(callbackStub,
-                    TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+            if (Compatibility.isChangeEnabled(
+                    TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
+            } else {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+            }
             /* check if no profile */
             logDebug("stopping scan");
             mNetworkScanCtlr.stopNetworkScan();
@@ -535,7 +586,8 @@
         return false;
     }
 
-    private int retrieveBestSubscription(List<CellInfo> results) {
+    @VisibleForTesting
+    protected int retrieveBestSubscription(List<CellInfo> results) {
         /* sort the results according to signal strength level */
         Collections.sort(results, new Comparator<CellInfo>() {
             @Override
@@ -594,16 +646,28 @@
     private void disableOpportunisticModem(IUpdateAvailableNetworksCallback callbackStub) {
         int subId = getActiveOpportunisticSubId();
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            sendUpdateNetworksCallbackHelper(callbackStub,
-                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+            if (Compatibility.isChangeEnabled(
+                    TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
+            } else {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+            }
             return;
         }
         if (enableModem(subId, false)) {
             sendUpdateNetworksCallbackHelper(callbackStub,
                 TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
         } else {
-            sendUpdateNetworksCallbackHelper(callbackStub,
-                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+            if (Compatibility.isChangeEnabled(
+                    TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL);
+            } else {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+            }
         }
     }
 
@@ -612,6 +676,12 @@
             return false;
         }
 
+        // If disabling modem for opportunistic sub, make sure to switch data back to default sub.
+        if (!enable) {
+            if (mSubscriptionManager.getPreferredDataSubscriptionId() == subId) {
+                selectProfileForData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
+            }
+        }
         int phoneId = SubscriptionManager.getPhoneId(subId);
         /*  Todo: b/135067156
          *  Reenable this code once 135067156 is fixed
@@ -623,6 +693,12 @@
         return mSubscriptionBoundTelephonyManager.enableModemForSlot(phoneId, enable);
     }
 
+    private void stopProfileSelectionProcess(IUpdateAvailableNetworksCallback callbackStub) {
+        stopProfileScanningPrecedure();
+        logDebug("stopProfileSelection");
+        disableOpportunisticModem(callbackStub);
+    }
+
     private void stopProfileScanningPrecedure() {
         synchronized (mLock) {
             if (mNetworkScanCallback != null) {
@@ -656,6 +732,24 @@
         return false;
     }
 
+    public boolean containStandaloneOppSubs(ArrayList<AvailableNetworkInfo> availableNetworks) {
+        if (mStandaloneOppSubInfos == null) {
+            logDebug("received null subscription infos");
+            return false;
+        }
+        if (mStandaloneOppSubInfos.size() > 0) {
+            logDebug("Standalone opportunistic subInfos size " + mStandaloneOppSubInfos.size());
+            ArrayList<AvailableNetworkInfo> filteredAvailableNetworks =
+                    getFilteredAvailableNetworks(
+                            (ArrayList<AvailableNetworkInfo>) availableNetworks,
+                            mStandaloneOppSubInfos);
+            if (filteredAvailableNetworks.size() > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public boolean isOpportunisticSubActive() {
         if (mOppSubscriptionInfos == null) {
             logDebug("received null subscription infos");
@@ -677,6 +771,10 @@
             IUpdateAvailableNetworksCallback callbackStub) {
         logDebug("startProfileSelection availableNetworks: " + availableNetworks);
         if (availableNetworks == null || availableNetworks.size() == 0) {
+            if (callbackStub != null) {
+                sendUpdateNetworksCallbackHelper(callbackStub,
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+            }
             return;
         }
         Object[] objects = new Object[]{availableNetworks, callbackStub};
@@ -705,23 +803,46 @@
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub == null) {
                 log("Could not get Subscription Service handle");
-                sendSetOpptCallbackHelper(callbackStub,
-                    TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
+                if (Compatibility.isChangeEnabled(
+                        TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                    sendSetOpptCallbackHelper(callbackStub,
+                            TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
+                } else {
+                    sendSetOpptCallbackHelper(callbackStub,
+                            TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
+                }
                 return;
             }
             try {
                 iSub.setPreferredDataSubscriptionId(subId, needValidation, callbackStub);
             } catch (RemoteException ex) {
                 log("Could not connect to Subscription Service");
-                sendSetOpptCallbackHelper(callbackStub,
-                        TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
+                if (Compatibility.isChangeEnabled(
+                        TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                    sendSetOpptCallbackHelper(callbackStub,
+                            TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
+                } else {
+                    sendSetOpptCallbackHelper(callbackStub,
+                            TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
+                }
                 return;
             }
             mCurrentDataSubId = subId;
         } else {
             log("Inactive sub passed for preferred data " + subId);
-            sendSetOpptCallbackHelper(callbackStub,
-                    TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+            if (Compatibility.isChangeEnabled(
+                    TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                if (isOpprotunisticSub(subId)) {
+                    sendSetOpptCallbackHelper(callbackStub,
+                            TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+                } else {
+                    sendSetOpptCallbackHelper(callbackStub,
+                            TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE);
+                }
+            } else {
+                sendSetOpptCallbackHelper(callbackStub,
+                        TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+            }
         }
     }
 
@@ -733,18 +854,23 @@
      * stop profile selection procedure
      */
     public void stopProfileSelection(IUpdateAvailableNetworksCallback callbackStub) {
-        stopProfileScanningPrecedure();
         logDebug("stopProfileSelection");
-        disableOpportunisticModem(callbackStub);
+        Message message = Message.obtain(mHandler, MSG_STOP_PROFILE_SELECTION, callbackStub);
+        message.sendToTarget();
     }
 
     @VisibleForTesting
     protected void updateOpportunisticSubscriptions() {
         synchronized (mLock) {
             mOppSubscriptionInfos = mSubscriptionManager
-                .getOpportunisticSubscriptions().stream()
-                .filter(subInfo -> subInfo.isGroupDisabled() != true)
-                .collect(Collectors.toList());
+                    .getOpportunisticSubscriptions().stream()
+                    .filter(subInfo -> subInfo.isGroupDisabled() != true)
+                    .collect(Collectors.toList());
+            if (mOppSubscriptionInfos != null) {
+                mStandaloneOppSubInfos = mOppSubscriptionInfos.stream()
+                        .filter(subInfo -> subInfo.getGroupUuid() == null)
+                        .collect(Collectors.toList());
+            }
         }
     }
 
@@ -799,6 +925,12 @@
                             checkProfileUpdate((Object[]) msg.obj);
                         }
                         break;
+                    case MSG_STOP_PROFILE_SELECTION:
+                        logDebug("Msg received to stop profile selection");
+                        synchronized (mLock) {
+                            stopProfileSelectionProcess((IUpdateAvailableNetworksCallback) msg.obj);
+                        }
+                        break;
                     case MSG_SUB_SWITCH_COMPLETE:
                         logDebug("Msg received for sub switch");
                         synchronized (mLock) {
diff --git a/src/com/android/ons/OpportunisticNetworkService.java b/src/com/android/ons/OpportunisticNetworkService.java
index 6db5fa8..62e292b 100644
--- a/src/com/android/ons/OpportunisticNetworkService.java
+++ b/src/com/android/ons/OpportunisticNetworkService.java
@@ -17,6 +17,7 @@
 package com.android.ons;
 
 import android.app.Service;
+import android.compat.Compatibility;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -25,12 +26,11 @@
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.Message;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.AvailableNetworkInfo;
-import android.telephony.Rlog;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -41,6 +41,7 @@
 import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -58,7 +59,7 @@
 
     private final Object mLock = new Object();
     @VisibleForTesting protected boolean mIsEnabled;
-    private ONSProfileSelector mProfileSelector;
+    @VisibleForTesting protected ONSProfileSelector mProfileSelector;
     private SharedPreferences mSharedPref;
     @VisibleForTesting protected HashMap<String, ONSConfigInput> mONSConfigInputHashMap;
 
@@ -127,6 +128,11 @@
         }
         List<SubscriptionInfo> subscriptionInfos =
             mSubscriptionManager.getActiveSubscriptionInfoList(false);
+        if (subscriptionInfos == null) {
+          return;
+        }
+
+        logDebug("handleSimStateChange: subscriptionInfos - " + subscriptionInfos);
         for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
             if (subscriptionInfo.getSubscriptionId() == carrierAppConfigInput.getPrimarySub()) {
                 return;
@@ -224,10 +230,10 @@
                 ISetOpportunisticDataCallback callbackStub, String callingPackage) {
             logDebug("setPreferredDataSubscriptionId subId:" + subId + "callingPackage: " + callingPackage);
             if (!enforceModifyPhoneStatePermission(mContext)) {
-                TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+                TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext,
                         mSubscriptionManager.getDefaultSubscriptionId(), "setPreferredDataSubscriptionId");
                 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                    TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId,
+                    TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext, subId,
                             "setPreferredDataSubscriptionId");
                 }
             } else {
@@ -259,11 +265,13 @@
          * subscription id
          *
          */
-        public int getPreferredDataSubscriptionId(String callingPackage) {
+        @Override
+        public int getPreferredDataSubscriptionId(String callingPackage,
+                String callingFeatureId) {
             TelephonyPermissions
                     .checkCallingOrSelfReadPhoneState(mContext,
                             mSubscriptionManager.getDefaultSubscriptionId(),
-                            callingPackage, "getPreferredDataSubscriptionId");
+                            callingPackage, callingFeatureId, "getPreferredDataSubscriptionId");
             final long identity = Binder.clearCallingIdentity();
             try {
                 return mProfileSelector.getPreferredDataSubscriptionId();
@@ -298,7 +306,7 @@
                         (ArrayList<AvailableNetworkInfo>) availableNetworks, callbackStub);
             } else {
                 /* check if the app has primary carrier permission */
-                TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+                TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext,
                         mSubscriptionManager.getDefaultSubscriptionId(), "updateAvailableNetworks");
                 handleCarrierAppAvailableNetworks(
                         (ArrayList<AvailableNetworkInfo>) availableNetworks, callbackStub,
@@ -380,15 +388,29 @@
             /* carrier apps should report only subscription */
             if (availableNetworks.size() > 1) {
                 log("Carrier app should not pass more than one subscription");
-                sendUpdateNetworksCallbackHelper(callbackStub,
-                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                if (Compatibility.isChangeEnabled(
+                        TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                    sendUpdateNetworksCallbackHelper(callbackStub,
+                            TelephonyManager
+                                    .UPDATE_AVAILABLE_NETWORKS_MULTIPLE_NETWORKS_NOT_SUPPORTED);
+                } else {
+                    sendUpdateNetworksCallbackHelper(callbackStub,
+                            TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                }
                 return;
             }
 
             if (!mProfileSelector.hasOpprotunisticSub(availableNetworks)) {
                 log("No opportunistic subscriptions received");
-                sendUpdateNetworksCallbackHelper(callbackStub,
-                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                if (Compatibility.isChangeEnabled(
+                        TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                    sendUpdateNetworksCallbackHelper(callbackStub,
+                            TelephonyManager
+                                    .UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
+                } else {
+                    sendUpdateNetworksCallbackHelper(callbackStub,
+                            TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                }
                 return;
             }
 
@@ -396,7 +418,7 @@
                 if (Binder.withCleanCallingIdentity(
                             () -> mSubscriptionManager.isActiveSubId(
                                     availableNetworkInfo.getSubId()))) {
-                    TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+                    TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext,
                         availableNetworkInfo.getSubId(), "updateAvailableNetworks");
                 } else {
                     /* check if the app has opportunistic carrier permission */
@@ -413,14 +435,33 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 ONSConfigInput onsConfigInput = new ONSConfigInput(availableNetworks, callbackStub);
-                onsConfigInput.setPrimarySub(
-                        mSubscriptionManager.getDefaultVoiceSubscriptionInfo().getSubscriptionId());
-                onsConfigInput.setPreferredDataSub(availableNetworks.get(0).getSubId());
-                mONSConfigInputHashMap.put(CARRIER_APP_CONFIG_NAME, onsConfigInput);
+                SubscriptionInfo subscriptionInfo = mSubscriptionManager.getDefaultVoiceSubscriptionInfo();
+                if (subscriptionInfo != null) {
+                    onsConfigInput.setPrimarySub(subscriptionInfo.getSubscriptionId());
+                    onsConfigInput.setPreferredDataSub(availableNetworks.get(0).getSubId());
+                    mONSConfigInputHashMap.put(CARRIER_APP_CONFIG_NAME, onsConfigInput);
+                }
+                /* standalone opportunistic subscription should be handled in priority. */
+                if (mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME) != null) {
+                    if (mProfileSelector.containStandaloneOppSubs(mONSConfigInputHashMap.get(
+                            SYSTEM_APP_CONFIG_NAME).getAvailableNetworkInfos())) {
+                        log("standalone opportunistic subscription is using.");
+                        return;
+                    }
+                }
 
                 if (mIsEnabled) {
                     /*  if carrier is reporting availability, then it takes higher priority. */
                     mProfileSelector.startProfileSelection(availableNetworks, callbackStub);
+                } else {
+                    if (Compatibility.isChangeEnabled(
+                            TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                        sendUpdateNetworksCallbackHelper(callbackStub,
+                                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED);
+                    } else {
+                        sendUpdateNetworksCallbackHelper(callbackStub,
+                                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+                    }
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -480,16 +521,36 @@
                 /* all subscriptions should be opportunistic subscriptions */
                 if (!mProfileSelector.hasOpprotunisticSub(availableNetworks)) {
                     log("No opportunistic subscriptions received");
-                    sendUpdateNetworksCallbackHelper(callbackStub,
-                            TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                    if (Compatibility.isChangeEnabled(
+                            TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                        sendUpdateNetworksCallbackHelper(callbackStub,
+                                TelephonyManager
+                                        .UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
+                    } else {
+                        sendUpdateNetworksCallbackHelper(callbackStub,
+                                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                    }
                     return;
                 }
                 mONSConfigInputHashMap.put(SYSTEM_APP_CONFIG_NAME,
                         new ONSConfigInput(availableNetworks, callbackStub));
 
-                /* reporting availability. proceed if carrier app has not requested any */
-                if (mIsEnabled && mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME) == null) {
-                    mProfileSelector.startProfileSelection(availableNetworks, callbackStub);
+                /* reporting availability. proceed if carrier app has not requested any, but
+                   standalone opportunistic subscription should be handled in priority. */
+                if (mIsEnabled) {
+                    if (mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME) == null
+                            || mProfileSelector.containStandaloneOppSubs(availableNetworks)) {
+                        mProfileSelector.startProfileSelection(availableNetworks, callbackStub);
+                    }
+                } else {
+                    if (Compatibility.isChangeEnabled(
+                            TelephonyManager.CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                        sendUpdateNetworksCallbackHelper(callbackStub,
+                                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED);
+                    } else {
+                        sendUpdateNetworksCallbackHelper(callbackStub,
+                                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_ABORTED);
+                    }
                 }
             } else {
                 if (!mIsEnabled) {
@@ -498,10 +559,20 @@
                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
                     return;
                 }
-                /* reporting unavailability */
+                /* if system is reporting unavailability, then decide whether to start
+                   carrier app request or not. */
                 mONSConfigInputHashMap.put(SYSTEM_APP_CONFIG_NAME, null);
                 if (mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME) == null) {
                     mProfileSelector.stopProfileSelection(callbackStub);
+                } else {
+                    sendUpdateNetworksCallbackHelper(callbackStub,
+                            TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
+                    log("Try to start carrier app request");
+                    mProfileSelector.startProfileSelection(
+                            mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME)
+                                    .getAvailableNetworkInfos(),
+                            mONSConfigInputHashMap.get(
+                                    CARRIER_APP_CONFIG_NAME).getAvailableNetworkCallback());
                 }
             }
         } finally {
diff --git a/tests/src/com/android/ons/ONSBaseTest.java b/tests/src/com/android/ons/ONSBaseTest.java
index d3f62b1..d31fa9b 100644
--- a/tests/src/com/android/ons/ONSBaseTest.java
+++ b/tests/src/com/android/ons/ONSBaseTest.java
@@ -16,13 +16,14 @@
 package com.android.ons;
 
 import android.content.Context;
-import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.telephony.Rlog;
+
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
diff --git a/tests/src/com/android/ons/ONSProfileSelectorTest.java b/tests/src/com/android/ons/ONSProfileSelectorTest.java
index a3b5a72..4816ec6 100644
--- a/tests/src/com/android/ons/ONSProfileSelectorTest.java
+++ b/tests/src/com/android/ons/ONSProfileSelectorTest.java
@@ -82,6 +82,14 @@
             updateOpportunisticSubscriptions();
         }
 
+        public int getCurrentPreferredData() {
+            return mCurrentDataSubId;
+        }
+
+        public void setCurrentPreferredData(int subId) {
+            mCurrentDataSubId = subId;
+        }
+
         protected void init(Context c,
             MyONSProfileSelector.ONSProfileSelectionCallback aNSProfileSelectionCallback) {
             super.init(c, aNSProfileSelectionCallback);
@@ -161,7 +169,8 @@
         // Testing startProfileSelection without any oppotunistic data.
         // should not get any callback invocation.
         waitUntilReady(100);
-        assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS, mResult);
+        assertEquals(
+                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE, mResult);
         assertFalse(mCallbackInvoked);
     }
 
@@ -238,6 +247,81 @@
     }
 
     @Test
+    public void testStartProfileSelectionWithDifferentPrioritySubInfo() {
+        int PRIORITY_HIGH = 1;
+        int PRIORITY_MED = 2;
+
+        List<SubscriptionInfo> subscriptionInfoList = new ArrayList<SubscriptionInfo>();
+        SubscriptionInfo subscriptionInfo = new SubscriptionInfo(5, "", 1, "TMO", "TMO", 1, 1,
+                "123", 1, null, "310", "210", "", false, null, "1");
+        subscriptionInfoList.add(subscriptionInfo);
+        SubscriptionInfo subscriptionInfo_2 = new SubscriptionInfo(8, "", 1, "Vzw", "Vzw", 1, 1,
+                "123", 1, null, "311", "480", "", false, null, "1");
+        subscriptionInfoList.add(subscriptionInfo_2);
+
+        List<CellInfo> results2 = new ArrayList<CellInfo>();
+        CellIdentityLte cellIdentityLte = new CellIdentityLte(310, 210, 1, 1, 1);
+        CellInfoLte cellInfoLte = new CellInfoLte();
+        cellInfoLte.setCellIdentity(cellIdentityLte);
+        results2.add((CellInfo) cellInfoLte);
+        CellIdentityLte cellIdentityLte_2 = new CellIdentityLte(311, 480, 1, 1, 1);
+        CellInfoLte cellInfoLte_2 = new CellInfoLte();
+        cellInfoLte_2.setCellIdentity(cellIdentityLte_2);
+        results2.add((CellInfo) cellInfoLte_2);
+
+        ArrayList<String> mccMncs = new ArrayList<>();
+        mccMncs.add("310210");
+        AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(5, PRIORITY_MED,
+                mccMncs, new ArrayList<Integer>());
+        ArrayList<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<>();
+        availableNetworkInfos.add(availableNetworkInfo);
+        ArrayList<String> mccMncs_2 = new ArrayList<>();
+        mccMncs_2.add("311480");
+        AvailableNetworkInfo availableNetworkInfo_2 = new AvailableNetworkInfo(8, PRIORITY_HIGH,
+                mccMncs_2, new ArrayList<Integer>());
+        availableNetworkInfos.add(availableNetworkInfo_2);
+
+        IUpdateAvailableNetworksCallback mCallback = new IUpdateAvailableNetworksCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                mResult = result;
+            }
+        };
+
+        mResult = -1;
+        mReady = false;
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                doReturn(subscriptionInfoList).when(mSubscriptionManager)
+                        .getOpportunisticSubscriptions();
+                doReturn(true).when(mSubscriptionManager).isActiveSubId(anyInt());
+                doReturn(true).when(mSubscriptionBoundTelephonyManager).enableModemForSlot(
+                        anyInt(), anyBoolean());
+                mONSProfileSelector = new MyONSProfileSelector(mContext,
+                        new MyONSProfileSelector.ONSProfileSelectionCallback() {
+                            public void onProfileSelectionDone() {
+                                setReady(true);
+                            }
+                        });
+                mONSProfileSelector.updateOppSubs();
+                mONSProfileSelector.startProfileSelection(availableNetworkInfos, mCallback);
+                mLooper = Looper.myLooper();
+                setReady(true);
+                Looper.loop();
+            }
+        }).start();
+        waitUntilReady();
+        waitForMs(500);
+        // get high priority subId
+        int retrieveSubId = mONSProfileSelector.retrieveBestSubscription(results2);
+        mONSProfileSelector.mNetworkAvailableCallBackCpy.onNetworkAvailability(results2);
+        assertEquals(8, retrieveSubId);
+        assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS, mResult);
+    }
+
+    @Test
     public void testStartProfileSelectionWithActivePrimarySimOnESim() {
         List<SubscriptionInfo> opportunisticSubscriptionInfoList = new ArrayList<SubscriptionInfo>();
         List<SubscriptionInfo> activeSubscriptionInfoList = new ArrayList<SubscriptionInfo>();
@@ -543,4 +627,101 @@
         assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS, mResult);
         assertTrue(mReady);
     }
+
+    @Test
+    public void testStopProfileSelectionWithPreferredDataOnSame() {
+        List<SubscriptionInfo> subscriptionInfoList = new ArrayList<SubscriptionInfo>();
+        SubscriptionInfo subscriptionInfo = new SubscriptionInfo(5, "", 1, "TMO", "TMO", 1, 1,
+                "123", 1, null, "310", "210", "", true, null, "1", true, null, 0, 0);
+        subscriptionInfoList.add(subscriptionInfo);
+
+        IUpdateAvailableNetworksCallback mCallback = new IUpdateAvailableNetworksCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                mResult = result;
+            }
+        };
+
+        mResult = -1;
+        mReady = false;
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                doReturn(subscriptionInfoList).when(mSubscriptionManager)
+                        .getOpportunisticSubscriptions();
+                doReturn(true).when(mSubscriptionManager).isActiveSubId(anyInt());
+                doReturn(true).when(mSubscriptionBoundTelephonyManager).enableModemForSlot(
+                        anyInt(), anyBoolean());
+                doReturn(5).when(mSubscriptionManager).getPreferredDataSubscriptionId();
+                doReturn(subscriptionInfoList).when(mSubscriptionManager)
+                        .getActiveSubscriptionInfoList(anyBoolean());
+
+                mONSProfileSelector = new MyONSProfileSelector(mContext,
+                        new MyONSProfileSelector.ONSProfileSelectionCallback() {
+                            public void onProfileSelectionDone() {
+                                setReady(true);
+                            }
+                        });
+                mONSProfileSelector.updateOppSubs();
+                mONSProfileSelector.setCurrentPreferredData(5);
+                mONSProfileSelector.stopProfileSelection(null);
+                mLooper = Looper.myLooper();
+                setReady(true);
+                Looper.loop();
+            }
+        }).start();
+        waitUntilReady();
+        waitForMs(500);
+        assertEquals(mONSProfileSelector.getCurrentPreferredData(), SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+    }
+
+    @Test
+    public void testStopProfileSelectionWithPreferredDataOnDifferent() {
+        List<SubscriptionInfo> subscriptionInfoList = new ArrayList<SubscriptionInfo>();
+        SubscriptionInfo subscriptionInfo = new SubscriptionInfo(5, "", 1, "TMO", "TMO", 1, 1,
+                "123", 1, null, "310", "210", "", true, null, "1", true, null, 0, 0);
+        subscriptionInfoList.add(subscriptionInfo);
+
+        IUpdateAvailableNetworksCallback mCallback = new IUpdateAvailableNetworksCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                mResult = result;
+            }
+        };
+
+        mResult = -1;
+        mReady = false;
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                doReturn(subscriptionInfoList).when(mSubscriptionManager)
+                        .getOpportunisticSubscriptions();
+                doReturn(true).when(mSubscriptionManager).isActiveSubId(anyInt());
+                doReturn(true).when(mSubscriptionBoundTelephonyManager).enableModemForSlot(
+                        anyInt(), anyBoolean());
+                doReturn(4).when(mSubscriptionManager).getPreferredDataSubscriptionId();
+                doReturn(subscriptionInfoList).when(mSubscriptionManager)
+                        .getActiveSubscriptionInfoList(anyBoolean());
+
+                mONSProfileSelector = new MyONSProfileSelector(mContext,
+                        new MyONSProfileSelector.ONSProfileSelectionCallback() {
+                            public void onProfileSelectionDone() {
+                                setReady(true);
+                            }
+                        });
+                mONSProfileSelector.updateOppSubs();
+                mONSProfileSelector.setCurrentPreferredData(5);
+                mONSProfileSelector.stopProfileSelection(null);
+                mLooper = Looper.myLooper();
+                setReady(true);
+                Looper.loop();
+            }
+        }).start();
+        waitUntilReady();
+        waitForMs(500);
+        assertEquals(mONSProfileSelector.getCurrentPreferredData(), 5);
+    }
+
 }
diff --git a/tests/src/com/android/ons/OpportunisticNetworkServiceTest.java b/tests/src/com/android/ons/OpportunisticNetworkServiceTest.java
index ae28862..05a7f17 100644
--- a/tests/src/com/android/ons/OpportunisticNetworkServiceTest.java
+++ b/tests/src/com/android/ons/OpportunisticNetworkServiceTest.java
@@ -50,6 +50,7 @@
 public class OpportunisticNetworkServiceTest extends ONSBaseTest {
     private static final String TAG = "ONSTest";
     private String pkgForDebug;
+    private String pkgForFeature;
     private int mResult;
     private IOns iOpportunisticNetworkService;
     private Looper mLooper;
@@ -59,11 +60,14 @@
 
     @Mock
     private HashMap<String, ONSConfigInput> mockONSConfigInputHashMap;
+    @Mock
+    private ONSProfileSelector mockProfileSelector;
 
     @Before
     public void setUp() throws Exception {
         super.setUp("ONSTest");
         pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        pkgForFeature = null;
         Intent intent = new Intent(mContext, OpportunisticNetworkService.class);
         new Thread(new Runnable() {
             @Override
@@ -217,7 +221,8 @@
         } catch (RemoteException ex) {
             Log.e(TAG, "RemoteException", ex);
         }
-        assertEquals(TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION, mResult);
+        assertEquals(
+                TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE, mResult);
     }
 
     @Test
@@ -225,7 +230,8 @@
         assertNotNull(iOpportunisticNetworkService);
         mResult = -1;
         try {
-            mResult = iOpportunisticNetworkService.getPreferredDataSubscriptionId(pkgForDebug);
+            mResult = iOpportunisticNetworkService.getPreferredDataSubscriptionId(pkgForDebug,
+                    pkgForFeature);
             Log.d(TAG, "testGetPreferredDataSubscriptionId: " + mResult);
             assertNotNull(mResult);
         } catch (RemoteException ex) {
@@ -257,7 +263,8 @@
         } catch (RemoteException ex) {
             Log.e(TAG, "RemoteException", ex);
         }
-        assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS, mResult);
+        assertEquals(
+                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE, mResult);
     }
 
     @Test
@@ -281,6 +288,76 @@
         assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS, mResult);
     }
 
+    @Test
+    public void testPriorityRuleOfActivatingAvailableNetworks() {
+        ArrayList<String> mccMncs = new ArrayList<>();
+        mccMncs.add("310210");
+        AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(1, 1, mccMncs,
+                new ArrayList<Integer>());
+        ArrayList<AvailableNetworkInfo> availableNetworkInfos =
+                new ArrayList<AvailableNetworkInfo>();
+        availableNetworkInfos.add(availableNetworkInfo);
+        mResult = -1;
+        IUpdateAvailableNetworksCallback mCallback = new IUpdateAvailableNetworksCallback.Stub() {
+            @Override
+            public void onComplete(int result) {
+                mResult = result;
+                Log.d(TAG, "result: " + result);
+            }
+        };
+        ONSConfigInput onsConfigInput = new ONSConfigInput(availableNetworkInfos, mCallback);
+        onsConfigInput.setPrimarySub(1);
+        onsConfigInput.setPreferredDataSub(availableNetworkInfos.get(0).getSubId());
+        doReturn(onsConfigInput).when(mockONSConfigInputHashMap).get(CARRIER_APP_CONFIG_NAME);
+        doReturn(true).when(mockProfileSelector).hasOpprotunisticSub(any());
+        doReturn(false).when(mockProfileSelector).containStandaloneOppSubs(any());
+        mOpportunisticNetworkService.mIsEnabled = true;
+        mOpportunisticNetworkService.mONSConfigInputHashMap = mockONSConfigInputHashMap;
+        mOpportunisticNetworkService.mProfileSelector = mockProfileSelector;
+
+        // Assume carrier app has updated available networks at first.
+        // Then system app updated available networks which is not standalone.
+        try {
+            IOns onsBinder = (IOns) mOpportunisticNetworkService.onBind(null);
+            onsBinder.updateAvailableNetworks(availableNetworkInfos, mCallback, pkgForDebug);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "RemoteException", ex);
+        }
+        verify(mockProfileSelector, never()).startProfileSelection(any(), any());
+
+        // System app updated available networks which contain standalone network.
+        doReturn(true).when(mockProfileSelector).containStandaloneOppSubs(any());
+        try {
+            IOns onsBinder = (IOns) mOpportunisticNetworkService.onBind(null);
+            onsBinder.updateAvailableNetworks(availableNetworkInfos, mCallback, pkgForDebug);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "RemoteException", ex);
+        }
+        verify(mockProfileSelector, times(1)).startProfileSelection(any(), any());
+
+        // System app updated available networks which equal to null.
+        // Case1: start carrier app request, if there is a carrier app request.
+        availableNetworkInfos.clear();
+        try {
+            IOns onsBinder = (IOns) mOpportunisticNetworkService.onBind(null);
+            onsBinder.updateAvailableNetworks(availableNetworkInfos, mCallback, pkgForDebug);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "RemoteException", ex);
+        }
+        verify(mockProfileSelector, times(2)).startProfileSelection(any(), any());
+
+        // System app updated available networks which equal to null.
+        // Case2: stop profile selection, if there is no any carrier app request.
+        doReturn(null).when(mockONSConfigInputHashMap).get(CARRIER_APP_CONFIG_NAME);
+        try {
+            IOns onsBinder = (IOns) mOpportunisticNetworkService.onBind(null);
+            onsBinder.updateAvailableNetworks(availableNetworkInfos, mCallback, pkgForDebug);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "RemoteException", ex);
+        }
+        verify(mockProfileSelector, times(1)).stopProfileSelection(any());
+    }
+
     private IOns getIOns() {
         return IOns.Stub.asInterface(ServiceManager.getService("ions"));
     }