Merge "Remove internal dependency for PhoneGlobals/Phone"
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index bfeee7a..bdffb26 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -76,6 +76,11 @@
         mReplaceInvalidCFNumber = replaceInvalidCFNumber;
     }
 
+    void restoreCallForwardInfo(CallForwardInfo cf) {
+        handleCallForwardResult(cf);
+        updateSummaryText();
+    }
+
     @Override
     protected void onBindDialogView(View view) {
         // default the button clicked to be the cancel button.
@@ -100,7 +105,15 @@
             int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
                     CommandsInterface.CF_ACTION_REGISTRATION :
                     CommandsInterface.CF_ACTION_DISABLE;
-            int time = (reason != CommandsInterface.CF_REASON_NO_REPLY) ? 0 : 20;
+            int time = 0;
+            if (reason == CommandsInterface.CF_REASON_NO_REPLY) {
+                PersistableBundle carrierConfig = PhoneGlobals.getInstance()
+                        .getCarrierConfigForSubId(mPhone.getSubId());
+                if (carrierConfig.getBoolean(
+                        CarrierConfigManager.KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true)) {
+                    time = 20;
+                }
+            }
             final String number = getPhoneNumber();
 
             Log.d(LOG_TAG, "callForwardInfo=" + callForwardInfo);
@@ -137,7 +150,7 @@
         }
     }
 
-    void handleCallForwardResult(CallForwardInfo cf) {
+    private void handleCallForwardResult(CallForwardInfo cf) {
         callForwardInfo = cf;
         Log.d(LOG_TAG, "handleGetCFResponse done, callForwardInfo=" + callForwardInfo);
         // In some cases, the network can send call forwarding URIs for voicemail that violate the
diff --git a/src/com/android/phone/GsmUmtsCallForwardOptions.java b/src/com/android/phone/GsmUmtsCallForwardOptions.java
index e562e46..6d80621 100644
--- a/src/com/android/phone/GsmUmtsCallForwardOptions.java
+++ b/src/com/android/phone/GsmUmtsCallForwardOptions.java
@@ -31,6 +31,7 @@
     private static final String KEY_TOGGLE = "toggle";
     private static final String KEY_STATUS = "status";
     private static final String KEY_NUMBER = "number";
+    private static final String KEY_ENABLE = "enable";
 
     private CallForwardEditPreference mButtonCFU;
     private CallForwardEditPreference mButtonCFB;
@@ -112,11 +113,12 @@
                 for (CallForwardEditPreference pref : mPreferences) {
                     Bundle bundle = mIcicle.getParcelable(pref.getKey());
                     pref.setToggled(bundle.getBoolean(KEY_TOGGLE));
+                    pref.setEnabled(bundle.getBoolean(KEY_ENABLE));
                     CallForwardInfo cf = new CallForwardInfo();
                     cf.number = bundle.getString(KEY_NUMBER);
                     cf.status = bundle.getInt(KEY_STATUS);
                     pref.init(this, mPhone, mReplaceInvalidCFNumbers);
-                    pref.handleCallForwardResult(cf);
+                    pref.restoreCallForwardInfo(cf);
                 }
             }
             mFirstResume = false;
@@ -131,6 +133,7 @@
         for (CallForwardEditPreference pref : mPreferences) {
             Bundle bundle = new Bundle();
             bundle.putBoolean(KEY_TOGGLE, pref.isToggled());
+            bundle.putBoolean(KEY_ENABLE, pref.isEnabled());
             if (pref.callForwardInfo != null) {
                 bundle.putString(KEY_NUMBER, pref.callForwardInfo.number);
                 bundle.putInt(KEY_STATUS, pref.callForwardInfo.status);
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index b1e061f..ba36f1b 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -62,6 +62,7 @@
 import com.android.internal.telephony.dataconnection.DataConnectionReasons;
 import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.anas.AlternativeNetworkAccessService;
 import com.android.phone.common.CallLogAsync;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
@@ -333,6 +334,7 @@
             phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
 
             configLoader = CarrierConfigLoader.init(this);
+            AlternativeNetworkAccessService.initInstance(this);
 
             // Create the CallNotifier singleton, which handles
             // asynchronous events from the telephony layer (like
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index fd23fd2..abfa8d7 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -112,6 +112,8 @@
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SmsApplication;
+import com.android.internal.telephony.SmsApplication.SmsApplicationData;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.euicc.EuiccConnector;
@@ -135,6 +137,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -3285,24 +3288,30 @@
         }
     }
 
-    /**
-     * Set the network selection mode to manual with the selected carrier.
+   /**
+     * Ask the radio to connect to the input network and change selection mode to manual.
+     *
+     * @param subId the id of the subscription.
+     * @param operatorInfo the operator information, included the PLMN, long name and short name of
+     * the operator to attach to.
+     * @param persistSelection whether the selection will persist until reboot. If true, only allows
+     * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+     * normal network selection next time.
+     * @return {@code true} on success; {@code true} on any failure.
      */
     @Override
-    public boolean setNetworkSelectionModeManual(int subId, String operatorNumeric,
-            boolean persistSelection) {
+    public boolean setNetworkSelectionModeManual(
+            int subId, OperatorInfo operatorInfo, boolean persistSelection) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeManual");
-
         final long identity = Binder.clearCallingIdentity();
         try {
-            OperatorInfo operator = new OperatorInfo(
-                /* operatorAlphaLong */ "",
-                /* operatorAlphaShort */ "",
-                    operatorNumeric);
-            if (DBG) log("setNetworkSelectionModeManual: subId:" + subId + " operator:" + operator);
-            ManualNetworkSelectionArgument arg = new ManualNetworkSelectionArgument(operator,
+            ManualNetworkSelectionArgument arg = new ManualNetworkSelectionArgument(operatorInfo,
                     persistSelection);
+            if (DBG) {
+                log("setNetworkSelectionModeManual: subId: " + subId
+                        + " operator: " + operatorInfo);
+            }
             return (Boolean) sendRequest(CMD_SET_NETWORK_SELECTION_MODE_MANUAL, arg, subId);
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -5305,4 +5314,73 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    private void ensureUserRunning(int userId) {
+        if (!mUserManager.isUserRunning(userId)) {
+            throw new IllegalStateException("User " + userId + " does not exist or not running");
+        }
+    }
+
+    /**
+     * Returns a list of SMS apps on a given user.
+     *
+     * Only the shell user (UID 2000 or 0) can call it.
+     * Target user must be running.
+     */
+    @Override
+    public String[] getSmsApps(int userId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getSmsApps");
+        ensureUserRunning(userId);
+
+        final Collection<SmsApplicationData> apps =
+                SmsApplication.getApplicationCollectionAsUser(mApp, userId);
+
+        String[] ret = new String[apps.size()];
+        int i = 0;
+        for (SmsApplicationData app : apps) {
+            ret[i++] = app.mPackageName;
+        }
+        return ret;
+    }
+
+    /**
+     * Returns the default SMS app package name on a given user.
+     *
+     * Only the shell user (UID 2000 or 0) can call it.
+     * Target user must be running.
+     */
+    @Override
+    public String getDefaultSmsApp(int userId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDefaultSmsApp");
+        ensureUserRunning(userId);
+
+        final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(mApp,
+                /* updateIfNeeded= */ true, userId);
+        return cn == null ? null : cn.getPackageName();
+    }
+
+    /**
+     * Set a package as the default SMS app on a given user.
+     *
+     * Only the shell user (UID 2000 or 0) can call it.
+     * Target user must be running.
+     */
+    @Override
+    public void setDefaultSmsApp(int userId, String packageName) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDefaultSmsApp");
+        ensureUserRunning(userId);
+
+        boolean found = false;
+        for (String pkg : getSmsApps(userId)) {
+            if (TextUtils.equals(packageName, pkg)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            throw new IllegalArgumentException("Package " + packageName + " is not an SMS app");
+        }
+
+        SmsApplication.setDefaultApplicationAsUser(packageName, mApp, userId);
+    }
 }
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 4acb46b..1a25ae3 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -18,6 +18,7 @@
 
 import android.os.RemoteException;
 import android.os.ShellCommand;
+import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
 
@@ -39,11 +40,16 @@
     private static final int DEFAULT_PHONE_ID = 0;
 
     private static final String IMS_SUBCOMMAND = "ims";
+    private static final String SMS_SUBCOMMAND = "sms";
     private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
     private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
     private static final String IMS_ENABLE = "enable";
     private static final String IMS_DISABLE = "disable";
 
+    private static final String SMS_GET_APPS = "get-apps";
+    private static final String SMS_GET_DEFAULT_APP = "get-default-app";
+    private static final String SMS_SET_DEFAULT_APP = "set-default-app";
+
     // Take advantage of existing methods that already contain permissions checks when possible.
     private final ITelephony mInterface;
 
@@ -61,6 +67,9 @@
             case IMS_SUBCOMMAND: {
                 return handleImsCommand();
             }
+            case SMS_SUBCOMMAND: {
+                return handleSmsCommand();
+            }
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -75,7 +84,10 @@
         pw.println("    Print this help text.");
         pw.println("  ims");
         pw.println("    IMS Commands.");
+        pw.println("  sms");
+        pw.println("    SMS Commands.");
         onHelpIms();
+        onHelpSms();
     }
 
     private void onHelpIms() {
@@ -103,6 +115,17 @@
         pw.println("    slot if none is specified.");
     }
 
+    private void onHelpSms() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("SMS Commands:");
+        pw.println("  sms get-apps [--user USER_ID]");
+        pw.println("    Print all SMS apps on a user.");
+        pw.println("  sms get-default-app [--user USER_ID]");
+        pw.println("    Get the default SMS app.");
+        pw.println("  sms set-default-app [--user USER_ID] PACKAGE_NAME");
+        pw.println("    Set PACKAGE_NAME as the default SMS app.");
+    }
+
     private int handleImsCommand() {
         String arg = getNextArg();
         if (arg == null) {
@@ -296,4 +319,85 @@
         }
         return slotId;
     }
+
+    private int handleSmsCommand() {
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpSms();
+            return 0;
+        }
+
+        try {
+            switch (arg) {
+                case SMS_GET_APPS: {
+                    return handleSmsGetApps();
+                }
+                case SMS_GET_DEFAULT_APP: {
+                    return handleSmsGetDefaultApp();
+                }
+                case SMS_SET_DEFAULT_APP: {
+                    return handleSmsSetDefaultApp();
+                }
+                default:
+                    getErrPrintWriter().println("Unknown command " + arg);
+            }
+        } catch (RemoteException e) {
+            getErrPrintWriter().println("RemoteException: " + e.getMessage());
+        }
+
+        return -1;
+    }
+
+    private int maybeParseUserIdArg() {
+        int userId = UserHandle.USER_SYSTEM;
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "--user": {
+                    try {
+                        userId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        getErrPrintWriter().println("Invalid user ID for --user");
+                        return -1;
+                    }
+                    break;
+                }
+            }
+        }
+        return userId;
+    }
+
+    private int handleSmsGetApps() throws RemoteException {
+        final int userId = maybeParseUserIdArg();
+        if (userId < 0) {
+            return -1;
+        }
+
+        for (String packageName : mInterface.getSmsApps(userId)) {
+            getOutPrintWriter().println(packageName);
+        }
+        return 0;
+    }
+
+    private int handleSmsGetDefaultApp() throws RemoteException {
+        final int userId = maybeParseUserIdArg();
+        if (userId < 0) {
+            return -1;
+        }
+
+        getOutPrintWriter().println(mInterface.getDefaultSmsApp(userId));
+        return 0;
+    }
+
+    private int handleSmsSetDefaultApp() throws RemoteException {
+        final int userId = maybeParseUserIdArg();
+        if (userId < 0) {
+            return -1;
+        }
+
+        String packageName = getNextArgRequired();
+        mInterface.setDefaultSmsApp(userId, packageName);
+        getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
+        return 0;
+    }
 }
diff --git a/src/com/android/phone/anas/ANASNetworkScanCtlr.java b/src/com/android/phone/anas/ANASNetworkScanCtlr.java
new file mode 100644
index 0000000..6c75706
--- /dev/null
+++ b/src/com/android/phone/anas/ANASNetworkScanCtlr.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoLte;
+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 com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Network Scan controller class which will scan for the specific bands as requested and
+ * provide results to caller when ready.
+ */
+public class ANASNetworkScanCtlr {
+    private static final String LOG_TAG = "ANASNetworkScanCtlr";
+    private static final boolean DBG = true;
+    private static final int SEARCH_PERIODICITY_SLOW = (int) TimeUnit.MINUTES.toSeconds(5);
+    private static final int SEARCH_PERIODICITY_FAST = (int) TimeUnit.MINUTES.toSeconds(1);
+    private static final int MAX_SEARCH_TIME = (int) TimeUnit.MINUTES.toSeconds(1);
+    private final Object mLock = new Object();
+
+    /* message  to handle scan responses from modem */
+    private static final int MSG_SCAN_RESULTS_AVAILABLE = 1;
+    private static final int MSG_SCAN_COMPLETE = 2;
+    private static final int MSG_SCAN_ERROR = 3;
+
+    /* scan object to keep track of current scan request */
+    private NetworkScan mCurrentScan;
+    private boolean mIsScanActive;
+    private NetworkScanRequest mCurrentScanRequest;
+    private List<String> mMccMncs;
+    private TelephonyManager mTelephonyManager;
+    @VisibleForTesting
+    protected NetworkAvailableCallBack mNetworkAvailableCallBack;
+
+    private Handler mHandler =  new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SCAN_RESULTS_AVAILABLE:
+                    logDebug("Msg received for scan results");
+                    /* Todo: need to aggregate the results */
+                    analyzeScanResults((List<CellInfo>) msg.obj);
+                    break;
+                case MSG_SCAN_COMPLETE:
+                    logDebug("Msg received for scan complete");
+                    restartScan();
+                    break;
+                case MSG_SCAN_ERROR:
+                    logDebug("Msg received for scan error");
+                    invalidateScanOnError((int) msg.obj);
+                    break;
+                default:
+                    log("invalid message");
+                    break;
+            }
+        }
+    };
+
+    @VisibleForTesting
+    public TelephonyScanManager.NetworkScanCallback mNetworkScanCallback =
+            new TelephonyScanManager.NetworkScanCallback() {
+
+        @Override
+        public void onResults(List<CellInfo> results) {
+            logDebug("Total results :" + results.size());
+            for (CellInfo cellInfo : results) {
+                logDebug("cell info: " + cellInfo);
+            }
+
+            Message message = Message.obtain(mHandler, MSG_SCAN_RESULTS_AVAILABLE, results);
+            message.sendToTarget();
+        }
+
+        @Override
+        public void onComplete() {
+            logDebug("Scan completed!");
+            Message message = Message.obtain(mHandler, MSG_SCAN_COMPLETE, NetworkScan.SUCCESS);
+            message.sendToTarget();
+        }
+
+        @Override
+        public void onError(@NetworkScan.ScanErrorCode int error) {
+            logDebug("Scan error " + error);
+            Message message = Message.obtain(mHandler, MSG_SCAN_ERROR, error);
+            message.sendToTarget();
+        }
+    };
+
+    /**
+     * call back for network availability
+     */
+    public interface NetworkAvailableCallBack {
+
+        /**
+         * Returns the scan results to the user, this callback will be called multiple times.
+         */
+        void onNetworkAvailability(List<CellInfo> results);
+
+        /**
+         * on error
+         * @param error
+         */
+        void onError(int error);
+    }
+
+
+    /**
+     * analyze scan results
+     * @param results contains all available cells matching the scan request at current location.
+     */
+    public void analyzeScanResults(List<CellInfo> results) {
+        /* Inform registrants about availability of network */
+        if (results != null) {
+            List<CellInfo> filteredResults = new ArrayList<CellInfo>();
+            synchronized (mLock) {
+                for (CellInfo cellInfo : results) {
+                    if (mMccMncs.contains(getMccMnc(cellInfo))) {
+                        filteredResults.add(cellInfo);
+                    }
+                }
+            }
+
+            if ((filteredResults.size() >= 1) && (mNetworkAvailableCallBack != null)) {
+                /* Todo: change to aggregate results on success. */
+                mNetworkAvailableCallBack.onNetworkAvailability(filteredResults);
+            }
+        }
+    }
+
+    private void invalidateScanOnError(int error) {
+        logDebug("scan invalidated on error");
+        if (mNetworkAvailableCallBack != null) {
+            mNetworkAvailableCallBack.onError(error);
+        }
+
+        synchronized (mLock) {
+            mIsScanActive = false;
+            mCurrentScan = null;
+        }
+    }
+
+    public ANASNetworkScanCtlr(Context c, TelephonyManager telephonyManager,
+            NetworkAvailableCallBack networkAvailableCallBack) {
+        init(c, telephonyManager, networkAvailableCallBack);
+    }
+
+    /**
+     * initialize Network Scan controller
+     * @param c context
+     * @param telephonyManager Telephony manager instance
+     * @param networkAvailableCallBack callback to be called when network selection is done
+     */
+    public void init(Context c, TelephonyManager telephonyManager,
+            NetworkAvailableCallBack networkAvailableCallBack) {
+        log("init called");
+        mTelephonyManager = telephonyManager;
+        mNetworkAvailableCallBack = networkAvailableCallBack;
+    }
+
+    /* get mcc mnc from cell info if the cell is for LTE */
+    private String getMccMnc(CellInfo cellInfo) {
+        if (cellInfo instanceof CellInfoLte) {
+            return ((CellInfoLte) cellInfo).getCellIdentity().getMccString()
+                    + ((CellInfoLte) cellInfo).getCellIdentity().getMncString();
+        }
+
+        return null;
+    }
+
+    private NetworkScanRequest createNetworkScanRequest(List<SubscriptionInfo> subscriptionInfos,
+            int periodicity) {
+        RadioAccessSpecifier[] ras = new RadioAccessSpecifier[1];
+        int[] bands = new int[1];
+
+        /* hardcoding band for now, Todo b/113753823 */
+        bands[0] = AccessNetworkConstants.EutranBand.BAND_48;
+
+        ArrayList<String> mccMncs = new ArrayList<String>();
+        /* retrieve mcc mncs for a subscription id */
+        for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+            mccMncs.add(subscriptionInfo.getMccString() + subscriptionInfo.getMncString());
+        }
+
+        /* create network scan request */
+        ras[0] = new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, bands,
+                null);
+        NetworkScanRequest networkScanRequest = new NetworkScanRequest(
+                NetworkScanRequest.SCAN_TYPE_PERIODIC, ras, periodicity, MAX_SEARCH_TIME, false,
+                NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC, mccMncs);
+        synchronized (mLock) {
+            mMccMncs = mccMncs;
+        }
+        return networkScanRequest;
+    }
+
+    /**
+     * start high interval network scan
+     * @param subscriptionInfos list of subscriptions for which the scanning needs to be started.
+     * @return true if successfully accepted request.
+     */
+    public boolean startSlowNetworkScan(List<SubscriptionInfo> subscriptionInfos) {
+        NetworkScanRequest networkScanRequest = createNetworkScanRequest(subscriptionInfos,
+                SEARCH_PERIODICITY_SLOW);
+        return startNetworkScan(networkScanRequest);
+    }
+
+    /**
+     * start less interval network scan
+     * @param subscriptionInfos list of subscriptions for which the scanning needs to be started.
+     * @return true if successfully accepted request.
+     */
+    public boolean startFastNetworkScan(List<SubscriptionInfo> subscriptionInfos) {
+        NetworkScanRequest networkScanRequest = createNetworkScanRequest(subscriptionInfos,
+                SEARCH_PERIODICITY_FAST);
+        return startNetworkScan(networkScanRequest);
+    }
+
+
+    private boolean startNetworkScan(NetworkScanRequest networkScanRequest) {
+        NetworkScan networkScan;
+        synchronized (mLock) {
+            /* if the request is same as existing one, then make sure to not proceed */
+            if (mIsScanActive && mCurrentScanRequest.equals(networkScanRequest)) {
+                return true;
+            }
+
+            /* Need to stop current scan if we already have one */
+            stopNetworkScan();
+
+            /* start new scan */
+            networkScan = mTelephonyManager.requestNetworkScan(networkScanRequest,
+                    mNetworkScanCallback);
+
+            mCurrentScan = networkScan;
+            mIsScanActive = true;
+            mCurrentScanRequest = networkScanRequest;
+        }
+
+        logDebug("startNetworkScan " + networkScanRequest);
+        return true;
+    }
+
+    private void restartScan() {
+        NetworkScan networkScan;
+        synchronized (mLock) {
+            if (mCurrentScanRequest != null) {
+                networkScan = mTelephonyManager.requestNetworkScan(mCurrentScanRequest,
+                        mNetworkScanCallback);
+                mIsScanActive = true;
+            }
+        }
+    }
+
+    /**
+     * stop network scan
+     */
+    public void stopNetworkScan() {
+        logDebug("stopNetworkScan");
+        synchronized (mLock) {
+            if (mIsScanActive && mCurrentScan != null) {
+                try {
+                    mCurrentScan.stopScan();
+                } catch (IllegalArgumentException iae) {
+                    logDebug("Scan failed with exception " + iae);
+                }
+                mIsScanActive = false;
+                mCurrentScan = null;
+                mCurrentScanRequest = null;
+            }
+        }
+    }
+
+    private static void log(String msg) {
+        Rlog.d(LOG_TAG, msg);
+    }
+
+    private static void logDebug(String msg) {
+        if (DBG) {
+            Rlog.d(LOG_TAG, msg);
+        }
+    }
+}
diff --git a/src/com/android/phone/anas/ANASProfileSelector.java b/src/com/android/phone/anas/ANASProfileSelector.java
new file mode 100644
index 0000000..d347cba
--- /dev/null
+++ b/src/com/android/phone/anas/ANASProfileSelector.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.Rlog;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Profile selector class which will select the right profile based upon
+ * geographic information input and network scan results.
+ */
+public class ANASProfileSelector {
+    private static final String LOG_TAG = "ANASProfileSelector";
+    private static final boolean DBG = true;
+    private final Object mLock = new Object();
+
+    private static final int INVALID_SEQUENCE_ID = -1;
+    private static final int START_SEQUENCE_ID = 1;
+
+    /* message to indicate profile update */
+    private static final int MSG_PROFILE_UPDATE = 1;
+
+    /* message to indicate start of profile selection process */
+    private static final int MSG_START_PROFILE_SELECTION = 2;
+    private boolean mIsEnabled = false;
+
+    @VisibleForTesting
+    protected Context mContext;
+
+    @VisibleForTesting
+    protected TelephonyManager mTelephonyManager;
+
+    @VisibleForTesting
+    protected ANASNetworkScanCtlr mNetworkScanCtlr;
+
+    private SubscriptionManager mSubscriptionManager;
+    private ANASProfileSelectionCallback mProfileSelectionCallback;
+    private int mSequenceId;
+
+    /* monitor the subscription for registration */
+    private ANASServiceStateMonitor mRegMonitor;
+    public static final String ACTION_SUB_SWITCH =
+            "android.intent.action.SUBSCRIPTION_SWITCH_REPLY";
+
+    /* service monitor callback will get called for service state change on a particular subId. */
+    private ANASServiceStateMonitor.ANASServiceMonitorCallback mServiceMonitorCallback =
+            new ANASServiceStateMonitor.ANASServiceMonitorCallback() {
+                @Override
+                public void onServiceMonitorUpdate(int subId, int state) {
+                    switch (state) {
+                        case ANASServiceStateMonitor.EVALUATED_STATE_BAD:
+                            switchPreferredData(subId);
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            };
+
+    private SubscriptionManager.OnOpportunisticSubscriptionsChangedListener mProfileChangeListener =
+            new SubscriptionManager.OnOpportunisticSubscriptionsChangedListener() {
+                @Override
+                public void onOpportunisticSubscriptionsChanged() {
+                    mHandler.sendEmptyMessage(MSG_PROFILE_UPDATE);
+                }
+            };
+
+    @VisibleForTesting
+    protected Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_PROFILE_UPDATE:
+                case MSG_START_PROFILE_SELECTION:
+                    logDebug("Msg received for profile update");
+                    checkProfileUpdate();
+                    break;
+                default:
+                    log("invalid message");
+                    break;
+            }
+        }
+    };
+
+    /**
+     * Broadcast receiver to receive intents
+     */
+    @VisibleForTesting
+    protected final BroadcastReceiver mProfileSelectorBroadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    int sequenceId;
+                    int subId;
+                    String action = intent.getAction();
+                    if (!mIsEnabled || action == null) {
+                        return;
+                    }
+
+                    switch (action) {
+                        case ACTION_SUB_SWITCH:
+                            sequenceId = intent.getIntExtra("sequenceId",  INVALID_SEQUENCE_ID);
+                            subId = intent.getIntExtra("subId",
+                                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                            if (sequenceId != mSequenceId) {
+                                return;
+                            }
+
+                            onSubSwitchComplete(subId);
+                            break;
+                    }
+                }
+            };
+
+    /**
+     * Network scan callback handler
+     */
+    @VisibleForTesting
+    protected ANASNetworkScanCtlr.NetworkAvailableCallBack mNetworkAvailableCallBack =
+            new ANASNetworkScanCtlr.NetworkAvailableCallBack() {
+                @Override
+                public void onNetworkAvailability(List<CellInfo> results) {
+                    /* sort the results according to signal strength level */
+                    Collections.sort(results, new Comparator<CellInfo>() {
+                        @Override
+                        public int compare(CellInfo cellInfo1, CellInfo cellInfo2) {
+                            return getSignalLevel(cellInfo1) - getSignalLevel(cellInfo2);
+                        }
+                    });
+
+                    /* get subscription id for the best network scan result */
+                    int subId = getSubId(getMcc(results.get(0)), getMnc(results.get(0)));
+                    if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                        /* could not find any matching subscriptions */
+                        return;
+                    }
+
+                    /* if subscription is already active, proceed to data switch */
+                    if (mSubscriptionManager.isActiveSubId(subId)) {
+                        /* if subscription already is data subscription,
+                         complete the profile selection process */
+                        /* Todo: change to getPreferredDataSubscriptionId once ready */
+                        if (mSubscriptionManager.getDefaultDataSubscriptionId() == subId) {
+                            mProfileSelectionCallback.onProfileSelectionDone(subId,
+                                    mSubscriptionManager.getDefaultSubscriptionId());
+                        } else {
+                            switchPreferredData(subId);
+                        }
+                    } else {
+                        switchToSubscription(subId);
+                    }
+                }
+
+                @Override
+                public void onError(int error) {
+                    log("Network scan failed with error " + error);
+                }
+            };
+
+    /**
+     * interface call back to confirm profile selection
+     */
+    public interface ANASProfileSelectionCallback {
+
+        /**
+         * interface call back to confirm profile selection
+         */
+        void onProfileSelectionDone(int dataSubId, int voiceSubId);
+    }
+
+    /**
+     * ANASProfileSelector constructor
+     * @param c context
+     * @param profileSelectionCallback callback to be called once selection is done
+     */
+    public ANASProfileSelector(Context c, ANASProfileSelectionCallback profileSelectionCallback) {
+        init(c, profileSelectionCallback);
+        log("ANASProfileSelector init complete");
+    }
+
+    private int getSignalLevel(CellInfo cellInfo) {
+        if (cellInfo != null) {
+            return cellInfo.getCellSignalStrength().getLevel();
+        } else {
+            return SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        }
+    }
+
+    private String getMcc(CellInfo cellInfo) {
+        String mcc = "";
+        if (cellInfo instanceof CellInfoGsm) {
+            mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMccString();
+        } else if (cellInfo instanceof CellInfoLte) {
+            mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString();
+        } else if (cellInfo instanceof CellInfoWcdma) {
+            mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMccString();
+        }
+
+        return mcc;
+    }
+
+    private String getMnc(CellInfo cellInfo) {
+        String mnc = "";
+        if (cellInfo instanceof CellInfoGsm) {
+            mnc = ((CellInfoGsm) cellInfo).getCellIdentity().getMncString();
+        } else if (cellInfo instanceof CellInfoLte) {
+            mnc = ((CellInfoLte) cellInfo).getCellIdentity().getMncString();
+        } else if (cellInfo instanceof CellInfoWcdma) {
+            mnc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMncString();
+        }
+
+        return mnc;
+    }
+
+    private int getSubId(String mcc, String mnc) {
+        List<SubscriptionInfo> subscriptionInfos =
+                mSubscriptionManager.getOpportunisticSubscriptions(1);
+        for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+            if (TextUtils.equals(subscriptionInfo.getMccString(), mcc)
+                    && TextUtils.equals(subscriptionInfo.getMncString(), mnc)) {
+                return subscriptionInfo.getSubscriptionId();
+            }
+        }
+
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    private void switchToSubscription(int subId) {
+        Intent callbackIntent = new Intent(ACTION_SUB_SWITCH);
+        callbackIntent.setClass(mContext, ANASProfileSelector.class);
+        callbackIntent.putExtra("sequenceId", getAndUpdateToken());
+        callbackIntent.putExtra("subId", subId);
+
+        PendingIntent replyIntent = PendingIntent.getService(mContext,
+                1, callbackIntent,
+                Intent.FILL_IN_ACTION);
+        mSubscriptionManager.switchToSubscription(subId, replyIntent);
+    }
+
+    private void switchPreferredData(int subId) {
+        mSubscriptionManager.setPreferredData(mSubscriptionManager.getSlotIndex(subId));
+        onDataSwitchComplete(subId);
+    }
+
+    private void onSubSwitchComplete(int subId) {
+        mRegMonitor.startListeningForNetworkConditionChange(subId);
+    }
+
+    private void onDataSwitchComplete(int subId) {
+        mProfileSelectionCallback.onProfileSelectionDone(subId,
+                mSubscriptionManager.getDefaultSubscriptionId());
+    }
+
+    private int getAndUpdateToken() {
+        synchronized (mLock) {
+            return mSequenceId++;
+        }
+    }
+
+    private void checkProfileUpdate() {
+        List<SubscriptionInfo> subscriptionInfos =
+                mSubscriptionManager.getOpportunisticSubscriptions(1);
+        if (subscriptionInfos == null) {
+            logDebug("received null subscription infos");
+            return;
+        }
+
+        if (subscriptionInfos.size() > 0) {
+            logDebug("opportunistic subscriptions size " + subscriptionInfos.size());
+
+            /* start scan immediately */
+            mNetworkScanCtlr.startFastNetworkScan(subscriptionInfos);
+        } else if (subscriptionInfos.size() == 0) {
+            /* check if no profile */
+            log("checkProfileUpdate 0 out");
+            mNetworkScanCtlr.stopNetworkScan();
+        }
+    }
+
+    /**
+     * start profile selection procedure
+     */
+    public void startProfileSelection() {
+        synchronized (mLock) {
+            if (!mIsEnabled) {
+                mIsEnabled = true;
+                mHandler.sendEmptyMessage(MSG_START_PROFILE_SELECTION);
+            }
+        }
+    }
+
+    /**
+     * select primary profile for data
+     */
+    public void selectPrimaryProfileForData() {
+        mSubscriptionManager.setPreferredData(mSubscriptionManager.getDefaultSubscriptionId());
+    }
+
+    /**
+     * stop profile selection procedure
+     */
+    public void stopProfileSelection() {
+        mNetworkScanCtlr.stopNetworkScan();
+        synchronized (mLock) {
+            mIsEnabled = false;
+        }
+    }
+
+    protected void init(Context c, ANASProfileSelectionCallback profileSelectionCallback) {
+        mContext = c;
+        mNetworkScanCtlr = new ANASNetworkScanCtlr(mContext, mTelephonyManager,
+                mNetworkAvailableCallBack);
+        mSequenceId = START_SEQUENCE_ID;
+        mProfileSelectionCallback = profileSelectionCallback;
+        mTelephonyManager = (TelephonyManager)
+                mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        mSubscriptionManager = (SubscriptionManager)
+                mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        mRegMonitor = new ANASServiceStateMonitor(mContext, mServiceMonitorCallback);
+
+        /* register for profile update events */
+        mSubscriptionManager.addOnOpportunisticSubscriptionsChangedListener(
+                AsyncTask.SERIAL_EXECUTOR, mProfileChangeListener);
+
+        /* register for subscription switch intent */
+        mContext.registerReceiver(mProfileSelectorBroadcastReceiver,
+                new IntentFilter(ACTION_SUB_SWITCH));
+    }
+
+    private void log(String msg) {
+        Rlog.d(LOG_TAG, msg);
+    }
+
+    private void logDebug(String msg) {
+        if (DBG) {
+            Rlog.d(LOG_TAG, msg);
+        }
+    }
+}
diff --git a/src/com/android/phone/anas/ANASServiceStateEvaluator.java b/src/com/android/phone/anas/ANASServiceStateEvaluator.java
new file mode 100644
index 0000000..23549a4
--- /dev/null
+++ b/src/com/android/phone/anas/ANASServiceStateEvaluator.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * ANASServiceStateEvaluator class which will evaluate the data service state of a subId
+ * compared to another.
+ */
+public class ANASServiceStateEvaluator {
+    private Context mContext;
+    private final Object mLock = new Object();
+    private int mOppDataSubId;
+    private int mPrimarySubId;
+    /* new opportunistic data service state */
+    private int mOppDataNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+    private int mPrimaryNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+    private boolean mIsWaitingForTimeout = false;
+
+    @VisibleForTesting
+    protected ANASServiceEvaluatorCallback mServiceEvaluatorCallback;
+
+    @VisibleForTesting
+    protected ANASServiceStateMonitor mOppDataSubMonitor;
+
+    @VisibleForTesting
+    protected ANASServiceStateMonitor mPrimarySubMonitor;
+
+    @VisibleForTesting
+    protected AlarmManager mAlarmManager;
+
+    private static final int WAIT_FOR_DATA_SERVICE_PERIOD = (int) TimeUnit.SECONDS.toMillis(10);
+    private static final String LOG_TAG = "ANASServiceStateEvaluator";
+    private static final boolean DBG = true;
+
+    /* message to indicate no data for WAIT_FOR_DATA_SERVICE_PERIOD */
+    private static final int MSG_WAIT_FOR_DATA_SERVICE_TIMOUT = 1;
+
+    /**
+     * call back to confirm service state evaluation
+     */
+    public interface ANASServiceEvaluatorCallback {
+
+        /**
+         * call back to confirm bad service
+         */
+        void onBadDataService();
+    }
+
+    private Handler mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_WAIT_FOR_DATA_SERVICE_TIMOUT:
+                        mIsWaitingForTimeout = false;
+                        logDebug("Msg received to get data");
+                        evaluateUpdatedState();
+                        break;
+
+                    default:
+                        log("invalid message");
+                        break;
+                }
+            }
+        };
+
+    public final AlarmManager.OnAlarmListener mDataServiceWaitTimer =
+            (AlarmManager.OnAlarmListener) () -> {
+                logDebug("Alarm fired");
+                mHandler.sendEmptyMessage(MSG_WAIT_FOR_DATA_SERVICE_TIMOUT);
+            };
+
+    /**
+     * set alarm to wait for data service
+     */
+    private void setDataServiceWaitAlarm() {
+        mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis()
+                + WAIT_FOR_DATA_SERVICE_PERIOD, LOG_TAG, mDataServiceWaitTimer, null);
+    }
+
+    /**
+     * stop the alarm
+     */
+    private void stopDataServiceWaitAlarm() {
+        mAlarmManager.cancel(mDataServiceWaitTimer);
+    }
+
+    private boolean evaluateIfBadOpportunisticDataService() {
+        /* if we have not received update on both subId, we can not take decision, yes */
+        log("evaluateIfBadOpportunisticDataService: mPrimaryNewState: "
+                + ANASServiceStateMonitor.getStateString(mPrimaryNewState) + " mOppDataNewState: "
+                + ANASServiceStateMonitor.getStateString(mOppDataNewState));
+
+        if ((mPrimaryNewState == ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN)
+                || (mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN)) {
+            return false;
+        }
+
+        /* Evaluate if primary subscription has good service and if
+           opportunistic data subscription is not, if yes return true.
+         */
+        switch (mPrimaryNewState) {
+            case ANASServiceStateMonitor.EVALUATED_STATE_NO_SERVICE:
+                /* no need to make any change */
+                return false;
+            case ANASServiceStateMonitor.EVALUATED_STATE_BAD:
+                if ((mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_BAD)
+                        || (mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_GOOD)) {
+                    return false;
+                }
+
+                break;
+            case ANASServiceStateMonitor.EVALUATED_STATE_GOOD:
+                if (mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_GOOD) {
+                    return false;
+                }
+                break;
+            default:
+                log("invalid state");
+                break;
+        }
+
+        return true;
+    }
+
+    private void evaluateUpdatedState() {
+        logDebug("evaluateUpdatedState " + mIsWaitingForTimeout);
+        if (!mIsWaitingForTimeout && evaluateIfBadOpportunisticDataService()) {
+            mServiceEvaluatorCallback.onBadDataService();
+        }
+    }
+
+    /* service monitor callback will get called for service state change on a particular subId. */
+    ANASServiceStateMonitor.ANASServiceMonitorCallback mServiceMonitorCallback =
+            new ANASServiceStateMonitor.ANASServiceMonitorCallback() {
+                @Override
+                public void onServiceMonitorUpdate(int subId, int state) {
+                    logDebug("onServiceMonitorUpdate subId: " + subId + " state: "
+                            + ANASServiceStateMonitor.getStateString(state));
+                    synchronized (mLock) {
+                        if (mServiceEvaluatorCallback == null) {
+                            return;
+                        }
+
+                        if (subId == mPrimarySubId) {
+                            mPrimaryNewState = state;
+                        } else if (subId == mOppDataSubId) {
+                            mOppDataNewState = state;
+                        } else {
+                            logDebug("invalid sub id");
+                        }
+
+                        evaluateUpdatedState();
+                    }
+                }
+            };
+
+    public ANASServiceStateEvaluator(Context c,
+            ANASServiceEvaluatorCallback serviceEvaluatorCallback) {
+        init(c, serviceEvaluatorCallback);
+    }
+
+    protected void init(Context c, ANASServiceEvaluatorCallback serviceEvaluatorCallback) {
+        mContext = c;
+        mServiceEvaluatorCallback = serviceEvaluatorCallback;
+        mOppDataSubMonitor = new ANASServiceStateMonitor(mContext, mServiceMonitorCallback);
+        mPrimarySubMonitor = new ANASServiceStateMonitor(mContext, mServiceMonitorCallback);
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+    }
+
+    /**
+     * start service state evaluation for dataSubId compared to voiceSubId.
+     * This API evaluates the service state conditions of dataSubId and decides whether
+     * data service is bad compared to voiceSubId
+     * @param dataSubId current data subscription id
+     * @param voiceSubId voice subscription id
+     */
+    public void startEvaluation(int dataSubId, int voiceSubId) {
+        logDebug("Start evaluation");
+        /* make sure to clean up if there is any evaluation going on. */
+        stopEvaluation();
+        setDataServiceWaitAlarm();
+        synchronized (mLock) {
+            mIsWaitingForTimeout = true;
+            mOppDataSubId = dataSubId;
+            mPrimarySubId = voiceSubId;
+            mOppDataSubMonitor.startListeningForNetworkConditionChange(dataSubId);
+            mPrimarySubMonitor.startListeningForNetworkConditionChange(voiceSubId);
+        }
+    }
+
+    /**
+     * stop service state evaluation
+     */
+    public void stopEvaluation() {
+        logDebug("Stop evaluation");
+        synchronized (mLock) {
+            mOppDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+            mPrimarySubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+            if (mIsWaitingForTimeout) {
+                stopDataServiceWaitAlarm();
+            }
+            mIsWaitingForTimeout = false;
+            mOppDataSubMonitor.stopListeningForNetworkConditionChange();
+            mPrimarySubMonitor.stopListeningForNetworkConditionChange();
+            mOppDataNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+            mPrimaryNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+        }
+    }
+
+
+    private void log(String msg) {
+        Rlog.d(LOG_TAG, msg);
+    }
+
+    private void logDebug(String msg) {
+        if (DBG) {
+            Rlog.d(LOG_TAG, msg);
+        }
+    }
+}
diff --git a/src/com/android/phone/anas/ANASServiceStateMonitor.java b/src/com/android/phone/anas/ANASServiceStateMonitor.java
new file mode 100644
index 0000000..90a1564
--- /dev/null
+++ b/src/com/android/phone/anas/ANASServiceStateMonitor.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * ANASServiceStateMonitor class which will monitor service state of a given subscription.
+ */
+public class ANASServiceStateMonitor {
+    @VisibleForTesting
+    protected Context mContext;
+
+    @VisibleForTesting
+    protected TelephonyManager mTelephonyManager;
+
+    @VisibleForTesting
+    protected ConnectivityManager mConnectivityManager;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EVALUATED_STATE_"},
+            value = {
+                    EVALUATED_STATE_UNKNOWN,
+                    EVALUATED_STATE_NO_SERVICE,
+                    EVALUATED_STATE_BAD,
+                    EVALUATED_STATE_GOOD})
+    public @interface EvaluatedState {}
+
+    /* service states to be used while reporting onServiceMonitorUpdate */
+    public static final int EVALUATED_STATE_UNKNOWN = 0;
+
+    /* network is not available */
+    public static final int EVALUATED_STATE_NO_SERVICE = 1;
+
+    /* network is available but not good */
+    public static final int EVALUATED_STATE_BAD = 2;
+
+    /* network is available and good */
+    public static final int EVALUATED_STATE_GOOD = 3;
+
+    private static final String LOG_TAG = "ANASServiceStateMonitor";
+    private static final boolean DBG = true;
+    private ANASServiceMonitorCallback mServiceMonitorCallback;
+    private PhoneStateListener mPhoneStateListener;
+    private int mSubId;
+    private @EvaluatedState int mSignalStrengthEvaluatedState;
+    private @EvaluatedState int mServiceStateEvaluatedState;
+    private final Object mLock = new Object();
+
+    protected void init(Context c, ANASServiceMonitorCallback serviceMonitorCallback) {
+        mContext = c;
+        mTelephonyManager = TelephonyManager.from(mContext);
+        mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        mSignalStrengthEvaluatedState = EVALUATED_STATE_UNKNOWN;
+        mServiceStateEvaluatedState = EVALUATED_STATE_UNKNOWN;
+        mServiceMonitorCallback = serviceMonitorCallback;
+        logDebug("[ANASServiceStateMonitor] init by Context");
+    }
+
+    /**
+     * get the string name of a state
+     * @param state service state
+     * @return string name of a state
+     */
+    public static String getStateString(@EvaluatedState int state) {
+        switch (state) {
+            case EVALUATED_STATE_NO_SERVICE:
+                return "No Service";
+            case EVALUATED_STATE_BAD:
+                return "Bad Service";
+            case EVALUATED_STATE_GOOD:
+                return "Good Service";
+            default:
+                return "Unknown";
+        }
+    }
+
+    /**
+     * returns whether the fail reason is permanent
+     * @param failCause fail reason
+     * @return true if reason is permanent
+     */
+    @VisibleForTesting
+    public static boolean isFatalFailCause(String failCause) {
+        if (failCause == null || failCause.isEmpty()) {
+            return false;
+        }
+
+        switch (failCause) {
+            case "OPERATOR_BARRED":
+            case "USER_AUTHENTICATION":
+            case "ACTIVATION_REJECT_GGSN":
+            case "SERVICE_OPTION_NOT_SUPPORTED":
+            case "SERVICE_OPTION_NOT_SUBSCRIBED":
+            case "SERVICE_OPTION_OUT_OF_ORDER":
+            case "PROTOCOL_ERRORS":
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private void updateCallbackOnFinalState() {
+        int evaluatedState = EVALUATED_STATE_UNKNOWN;
+
+        logDebug("mServiceStateEvaluatedState: " + getStateString(mServiceStateEvaluatedState)
+                + " mSignalStrengthEvaluatedState: "
+                + getStateString(mSignalStrengthEvaluatedState));
+
+        /* Service state has highest priority in this validation. If no service, no need to
+           check further. */
+        if (mServiceStateEvaluatedState == EVALUATED_STATE_GOOD) {
+            evaluatedState = EVALUATED_STATE_GOOD;
+        } else if (mServiceStateEvaluatedState == EVALUATED_STATE_NO_SERVICE) {
+            evaluatedState = EVALUATED_STATE_NO_SERVICE;
+            mServiceMonitorCallback.onServiceMonitorUpdate(mSubId, EVALUATED_STATE_NO_SERVICE);
+            return;
+        }
+
+        /* use signal strength to determine service quality only, i.e is good or bad. */
+        if (evaluatedState == EVALUATED_STATE_GOOD) {
+            if (mSignalStrengthEvaluatedState == EVALUATED_STATE_BAD) {
+                evaluatedState = EVALUATED_STATE_BAD;
+            }
+        }
+
+        if (evaluatedState != EVALUATED_STATE_UNKNOWN) {
+            mServiceMonitorCallback.onServiceMonitorUpdate(mSubId, evaluatedState);
+        }
+    }
+
+    private void analyzeSignalStrengthChange(SignalStrength signalStrength) {
+        if (mServiceMonitorCallback == null) {
+            return;
+        }
+
+        if (signalStrength.getLevel() <= SignalStrength.SIGNAL_STRENGTH_POOR) {
+            mSignalStrengthEvaluatedState = EVALUATED_STATE_BAD;
+        } else {
+            mSignalStrengthEvaluatedState = EVALUATED_STATE_GOOD;
+        }
+
+        updateCallbackOnFinalState();
+    }
+
+    private void analyzeServiceStateChange(ServiceState serviceState) {
+        logDebug("analyzeServiceStateChange state:"
+                + serviceState.getDataRegState());
+        if (mServiceMonitorCallback == null) {
+            return;
+        }
+
+        if ((serviceState.getDataRegState() == ServiceState.STATE_OUT_OF_SERVICE)
+                || (serviceState.getState() == ServiceState.STATE_EMERGENCY_ONLY)) {
+            mServiceStateEvaluatedState = EVALUATED_STATE_NO_SERVICE;
+        } else if (serviceState.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+            mServiceStateEvaluatedState = EVALUATED_STATE_GOOD;
+        }
+
+        updateCallbackOnFinalState();
+    }
+
+    /**
+     * Implements phone state listener
+     */
+    @VisibleForTesting
+    public class PhoneStateListenerImpl extends PhoneStateListener {
+        PhoneStateListenerImpl(int subId) {
+            super(subId);
+        }
+
+        private boolean shouldIgnore() {
+            if (PhoneStateListenerImpl.this.mSubId != ANASServiceStateMonitor.this.mSubId) {
+                return true;
+            }
+
+            return false;
+        }
+
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            synchronized (mLock) {
+                if (shouldIgnore()) {
+                    return;
+                }
+
+                analyzeSignalStrengthChange(signalStrength);
+            }
+        }
+
+        @Override
+        public void onServiceStateChanged(ServiceState serviceState) {
+            synchronized (mLock) {
+                if (shouldIgnore()) {
+                    return;
+                }
+
+                analyzeServiceStateChange(serviceState);
+            }
+        }
+    };
+
+    /**
+     * get phone state listener instance
+     * @param subId subscription id
+     * @return the listener instance
+     */
+    @VisibleForTesting
+    public PhoneStateListener getPhoneStateListener(int subId) {
+        synchronized (mLock) {
+            if (mPhoneStateListener != null && subId == mSubId) {
+                return mPhoneStateListener;
+            }
+
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+            mSubId = subId;
+            mPhoneStateListener = (PhoneStateListener) new PhoneStateListenerImpl(subId);
+        }
+        return mPhoneStateListener;
+    }
+
+    /**
+     * call back interface
+     */
+    public interface ANASServiceMonitorCallback {
+        /**
+         * call back interface
+         */
+        void onServiceMonitorUpdate(int subId, @EvaluatedState int state);
+    }
+
+    /**
+     * request to start listening for network changes.
+     */
+    public void startListeningForNetworkConditionChange(int subId) {
+
+        logDebug("start network condition listen for " + subId);
+        /* monitor service state, signal strength and data connection state */
+        synchronized (mLock) {
+            int events = PhoneStateListener.LISTEN_SERVICE_STATE
+                    | PhoneStateListener.LISTEN_SIGNAL_STRENGTH;
+            mTelephonyManager.listen(getPhoneStateListener(subId), events);
+        }
+    }
+
+    /**
+     * request to stop listening for network changes.
+     */
+    public void stopListeningForNetworkConditionChange() {
+        logDebug("stop network condition listen for " + mSubId);
+        synchronized (mLock) {
+            if (mPhoneStateListener != null) {
+                mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+            }
+            mSignalStrengthEvaluatedState = EVALUATED_STATE_UNKNOWN;
+            mServiceStateEvaluatedState = EVALUATED_STATE_UNKNOWN;
+        }
+    }
+
+    public ANASServiceStateMonitor(Context c, ANASServiceMonitorCallback serviceMonitorCallback) {
+        init(c, serviceMonitorCallback);
+    }
+
+    private static void log(String msg) {
+        Rlog.d(LOG_TAG, msg);
+    }
+
+    private static void logDebug(String msg) {
+        if (DBG) {
+            Rlog.d(LOG_TAG, msg);
+        }
+    }
+}
diff --git a/src/com/android/phone/anas/AlternativeNetworkAccessService.java b/src/com/android/phone/anas/AlternativeNetworkAccessService.java
new file mode 100644
index 0000000..b972813
--- /dev/null
+++ b/src/com/android/phone/anas/AlternativeNetworkAccessService.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Binder;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IAnas;
+import com.android.internal.telephony.TelephonyPermissions;
+
+/**
+ * AlternativeNetworkAccessService implements ianas.
+ * It scans network and matches the results with opportunistic subscriptions.
+ * Use the same to provide user opportunistic data in areas with corresponding networks
+ */
+public class AlternativeNetworkAccessService extends IAnas.Stub {
+    private Context mContext;
+    private TelephonyManager mTelephonyManager;
+    private SubscriptionManager mSubsriptionManager;
+
+    private final Object mLock = new Object();
+    private boolean mIsEnabled;
+    private ANASProfileSelector mProfileSelector;
+    private ANASServiceStateEvaluator mServiceStateEvaluator;
+    private SharedPreferences mSharedPref;
+
+    /** The singleton instance. */
+    private static AlternativeNetworkAccessService sInstance = null;
+    private static final String TAG = "ANAS";
+    private static final String PREF_NAME = TAG;
+    private static final String PREF_ENABLED = "isEnabled";
+    private static final boolean DBG = true;
+
+    /**
+     * Profile selection callback. Will be called once Profile selector decides on
+     * the opportunistic data profile.
+     */
+    private ANASProfileSelector.ANASProfileSelectionCallback  mProfileSelectionCallback =
+            new ANASProfileSelector.ANASProfileSelectionCallback() {
+
+                @Override
+                public void onProfileSelectionDone(int dataSubId, int voiceSubId) {
+                    logDebug("profile selection done");
+                    mProfileSelector.stopProfileSelection();
+                    mServiceStateEvaluator.startEvaluation(dataSubId, voiceSubId);
+                }
+            };
+
+    /**
+     * Service state evaluator callback. Will be called once service state evaluator thinks
+     * that current opportunistic data is not providing good service.
+     */
+    private ANASServiceStateEvaluator.ANASServiceEvaluatorCallback mServiceEvaluatorCallback =
+            new ANASServiceStateEvaluator.ANASServiceEvaluatorCallback() {
+                @Override
+                public void onBadDataService() {
+                    logDebug("Bad opportunistic data service");
+                    mServiceStateEvaluator.stopEvaluation();
+                    mProfileSelector.selectPrimaryProfileForData();
+                    mProfileSelector.startProfileSelection();
+                }
+            };
+
+    /**
+     * create AlternativeNetworkAccessService instance
+     *
+     * @param c context
+     *
+     */
+    public static void initInstance(Context c) {
+        if (sInstance == null) {
+            sInstance = new AlternativeNetworkAccessService(c);
+        }
+        return;
+    }
+
+    /**
+     * get AlternativeNetworkAccessService instance
+     *
+     */
+    @VisibleForTesting
+    public static AlternativeNetworkAccessService getInstance() {
+        if (sInstance == null) {
+            Log.wtf(TAG, "getInstance null");
+        }
+        return sInstance;
+    }
+
+    /**
+     * Enable or disable Alternative Network Access service.
+     *
+     * This method should be called to enable or disable
+     * AlternativeNetworkAccess service on the device.
+     *
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @param enable enable(True) or disable(False)
+     * @param callingPackage caller's package name
+     * @return returns true if successfully set.
+     */
+    @Override
+    public boolean setEnable(boolean enable, String callingPackage) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mContext, mSubsriptionManager.getDefaultSubscriptionId(), "setEnable");
+        log("setEnable: " + enable);
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            enableAlternativeNetworkAccess(enable);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        return true;
+    }
+
+    /**
+     * is Alternative Network Access service enabled
+     *
+     * This method should be called to determine if the Alternative Network Access service
+     * is enabled
+     *
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @param callingPackage caller's package name
+     */
+    @Override
+    public boolean isEnabled(String callingPackage) {
+        TelephonyPermissions.enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+                mContext, mSubsriptionManager.getDefaultSubscriptionId(), "isEnabled");
+        return mIsEnabled;
+    }
+
+    /**
+     * initialize ANAS and register as service.
+     * Read persistent state to update enable state
+     * Start sub components if already enabled.
+     * @param context context instance
+     */
+    private void initializeAndRegisterAsService(Context context) {
+        mContext = context;
+        mTelephonyManager = TelephonyManager.from(mContext);
+        mServiceStateEvaluator = new ANASServiceStateEvaluator(mContext, mServiceEvaluatorCallback);
+        mProfileSelector = new ANASProfileSelector(mContext, mProfileSelectionCallback);
+        mSharedPref = mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+        mSubsriptionManager = (SubscriptionManager) mContext.getSystemService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+
+        /* register the service */
+        if (ServiceManager.getService("ianas") == null) {
+            ServiceManager.addService("ianas", this);
+        }
+
+        enableAlternativeNetworkAccess(getPersistentEnableState());
+    }
+
+    private AlternativeNetworkAccessService(Context c) {
+        initializeAndRegisterAsService(c);
+        log("init completed");
+    }
+
+    private boolean getPersistentEnableState() {
+        return mSharedPref.getBoolean(PREF_ENABLED, true);
+    }
+
+    private void updateEnableState(boolean enable) {
+        mIsEnabled = enable;
+        mSharedPref.edit().putBoolean(PREF_ENABLED, mIsEnabled).apply();
+    }
+
+    /**
+     * update the enable state
+     * start profile selection if enabled.
+     * @param enable enable(true) or disable(false)
+     */
+    private void enableAlternativeNetworkAccess(boolean enable) {
+        synchronized (mLock) {
+            if (mIsEnabled != enable) {
+                updateEnableState(enable);
+                if (mIsEnabled) {
+                    mProfileSelector.startProfileSelection();
+                } else {
+                    mProfileSelector.stopProfileSelection();
+                }
+            }
+        }
+        logDebug("service is enable state " + mIsEnabled);
+    }
+
+    private void log(String msg) {
+        Rlog.d(TAG, msg);
+    }
+
+    private void logDebug(String msg) {
+        if (DBG) Rlog.d(TAG, msg);
+    }
+}
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 24c3870..5d987f7 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -211,6 +211,7 @@
             // 4) Add the conference to the connection service if it is new.
             if (isNewlyCreated) {
                 Log.d(this, "Adding the conference call");
+                mConference.updateCallRadioTechAfterCreation();
                 mConnectionService.addConference(mConference);
             }
         } else if (conferenceConnections.isEmpty()) {
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index b196d57..5722834 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -640,6 +640,7 @@
 
         setState(mConferenceHost.getState());
         updateStatusHints();
+        putExtras(mConferenceHost.getExtras());
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index e96815c..e9eef46 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -264,6 +264,7 @@
                                 mTelephonyConference, connection);
                         mTelephonyConference.addConnection(connection);
                     }
+                    mTelephonyConference.updateCallRadioTechAfterCreation();
                     mConnectionService.addConference(mTelephonyConference);
                 } else {
                     Log.d(this, "Trigger recalculate later");
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 31fe68f..7d25767 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -36,6 +36,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
 import android.text.TextUtils;
@@ -96,6 +97,7 @@
     private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15;
     private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
     private static final int MSG_HANGUP = 17;
+    private static final int MSG_SET_CALL_RADIO_TECH = 18;
 
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
@@ -240,6 +242,31 @@
                     int cause = (int) msg.obj;
                     hangup(cause);
                     break;
+
+                case MSG_SET_CALL_RADIO_TECH:
+                    int vrat = (int) msg.obj;
+                    // Check whether Wi-Fi call tech is changed, it means call radio tech is:
+                    //  a) changed from IWLAN to other value, or
+                    //  b) changed from other value to IWLAN.
+                    //
+                    // In other word, below conditions are all met:
+                    // 1) {@link #getCallRadioTech} is different from new vrat
+                    // 2) Current call radio technology indicates Wi-Fi call, i.e. {@link #isWifi}
+                    //    is true, or new vrat indicates Wi-Fi call.
+                    boolean isWifiTechChange = getCallRadioTech() != vrat
+                            && (isWifi() || vrat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+
+                    // Step 1) Updates call radio tech firstly, so that afterwards Wi-Fi related
+                    // update actions are taken correctly.
+                    setCallRadioTech(vrat);
+
+                    // Step 2) Handles Wi-Fi call tech change.
+                    if (isWifiTechChange) {
+                        updateConnectionProperties();
+                        updateStatusHints();
+                        refreshDisableAddCall();
+                    }
+                    break;
             }
         }
     };
@@ -421,14 +448,14 @@
         }
 
         /**
-         * Used by {@link com.android.internal.telephony.Connection} to report a change in whether
-         * the call is being made over a wifi network.
+         * Used by {@link com.android.internal.telephony.Connection} to report a change for
+         * the call radio technology.
          *
-         * @param isWifi True if call is made over wifi.
+         * @param vrat the RIL Voice Radio Technology used for current connection.
          */
         @Override
-        public void onWifiChanged(boolean isWifi) {
-            setWifi(isWifi);
+        public void onCallRadioTechChanged(@ServiceState.RilRadioTechnology int vrat) {
+            mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget();
         }
 
         /**
@@ -599,13 +626,6 @@
     private int mOriginalConnectionCapabilities;
 
     /**
-     * Determines if the {@link TelephonyConnection} is using wifi.
-     * This is used when {@link TelephonyConnection#updateConnectionProperties()} is called to
-     * indicate whether a call has the {@link Connection#PROPERTY_WIFI} property.
-     */
-    private boolean mIsWifi;
-
-    /**
      * Determines the audio quality is high for the {@link TelephonyConnection}.
      * This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to
      * indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
@@ -1030,7 +1050,7 @@
 
         newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
                 hasHighDefAudioProperty());
-        newProperties = changeBitmask(newProperties, PROPERTY_WIFI, mIsWifi);
+        newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi());
         newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
                 isExternalConnection());
         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
@@ -1117,12 +1137,13 @@
         // Set video state and capabilities
         setVideoState(mOriginalConnection.getVideoState());
         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
-        setWifi(mOriginalConnection.isWifi());
         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
         setVideoProvider(mOriginalConnection.getVideoProvider());
         setAudioQuality(mOriginalConnection.getAudioQuality());
         setTechnologyTypeExtra();
 
+        setCallRadioTech(mOriginalConnection.getCallRadioTech());
+
         // Post update of extras to the handler; extras are updated via the handler to ensure thread
         // safety. The Extras Bundle is cloned in case the original extras are modified while they
         // are being added to mOriginalConnectionExtras in updateExtras.
@@ -1239,7 +1260,7 @@
 
         if (isCurrentVideoCall) {
             return true;
-        } else if (wasVideoCall && mIsWifi && !isVowifiEnabled) {
+        } else if (wasVideoCall && isWifi() && !isVowifiEnabled) {
             return true;
         }
         return false;
@@ -1274,7 +1295,7 @@
             return false;
         }
 
-        if (mIsWifi && !canWifiCallsBeHdAudio) {
+        if (isWifi() && !canWifiCallsBeHdAudio) {
             return false;
         }
 
@@ -1831,21 +1852,10 @@
     }
 
     /**
-     * Sets whether the call is using wifi. Used when rebuilding the capabilities to set or unset
-     * the {@link Connection#PROPERTY_WIFI} property.
-     */
-    public void setWifi(boolean isWifi) {
-        mIsWifi = isWifi;
-        updateConnectionProperties();
-        updateStatusHints();
-        refreshDisableAddCall();
-    }
-
-    /**
      * Whether the call is using wifi.
      */
     boolean isWifi() {
-        return mIsWifi;
+        return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
     }
 
     /**
@@ -2025,7 +2035,7 @@
     }
 
     private void updateStatusHints() {
-        if (mIsWifi && getPhone() != null) {
+        if (isWifi() && getPhone() != null) {
             int labelId = isValidRingingCall()
                     ? R.string.status_hint_label_incoming_wifi_call
                     : R.string.status_hint_label_wifi_call;