Merge "Avoid duplicate carrier config change bcast." into mnc-dev
diff --git a/sip/src/com/android/services/telephony/sip/SipConnection.java b/sip/src/com/android/services/telephony/sip/SipConnection.java
index 5df61b7..e9f8e05 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnection.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnection.java
@@ -132,7 +132,8 @@
     public void onHold() {
         if (VERBOSE) log("onHold");
         try {
-            if (getPhone() != null && getState() == STATE_ACTIVE) {
+            if (getPhone() != null && getState() == STATE_ACTIVE
+                    && getPhone().getRingingCall().getState() != Call.State.WAITING) {
                 getPhone().switchHoldingAndActive();
             }
         } catch (CallStateException e) {
diff --git a/sip/src/com/android/services/telephony/sip/SipConnectionService.java b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
index ae21d73..ab4223a 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnectionService.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
@@ -110,8 +110,8 @@
         if (attemptCall) {
             // The ID used for SIP-based phone account is the SIP profile Uri. Use it to find
             // the actual profile.
-            String profileUri = accountHandle.getId();
-            findProfile(profileUri, new IProfileFinderCallback() {
+            String profileName = accountHandle.getId();
+            findProfile(profileName, new IProfileFinderCallback() {
                 @Override
                 public void onFound(SipProfile profile) {
                     if (profile == null) {
@@ -205,7 +205,7 @@
      * in communicating with the database, so it is done asynchronously with a separate thread and a
      * callback interface.
      */
-    private void findProfile(final String profileUri, final IProfileFinderCallback callback) {
+    private void findProfile(final String profileName, final IProfileFinderCallback callback) {
         if (VERBOSE) log("findProfile");
         new Thread(new Runnable() {
             @Override
@@ -214,7 +214,7 @@
                 List<SipProfile> profileList = mSipProfileDb.retrieveSipProfileList();
                 if (profileList != null) {
                     for (SipProfile profile : profileList) {
-                        if (Objects.equals(profileUri, profile.getUriString())) {
+                        if (Objects.equals(profileName, profile.getProfileName())) {
                             profileToUse = profile;
                             break;
                         }
diff --git a/src/com/android/phone/common/mail/MailTransport.java b/src/com/android/phone/common/mail/MailTransport.java
index c66130c..99f0272 100644
--- a/src/com/android/phone/common/mail/MailTransport.java
+++ b/src/com/android/phone/common/mail/MailTransport.java
@@ -16,6 +16,7 @@
 package com.android.phone.common.mail;
 
 import android.content.Context;
+import android.net.Network;
 
 import com.android.phone.common.mail.store.ImapStore;
 import com.android.phone.common.mail.utils.LogUtils;
@@ -51,6 +52,7 @@
             HttpsURLConnection.getDefaultHostnameVerifier();
 
     private Context mContext;
+    private Network mNetwork;
     private String mHost;
     private int mPort;
     private Socket mSocket;
@@ -58,8 +60,9 @@
     private BufferedOutputStream mOut;
     private int mFlags;
 
-    public MailTransport(Context context, String address, int port, int flags) {
+    public MailTransport(Context context, Network network, String address, int port, int flags) {
         mContext = context;
+        mNetwork = network;
         mHost = address;
         mPort = port;
         mFlags = flags;
@@ -71,7 +74,7 @@
      */
     @Override
     public MailTransport clone() {
-        return new MailTransport(mContext, mHost, mPort, mFlags);
+        return new MailTransport(mContext, mNetwork, mHost, mPort, mFlags);
     }
 
     public boolean canTrySslSecurity() {
@@ -94,7 +97,11 @@
             if (canTrySslSecurity()) {
                 mSocket = HttpsURLConnection.getDefaultSSLSocketFactory().createSocket();
             } else {
-                mSocket = new Socket();
+                if (mNetwork == null) {
+                    mSocket = new Socket();
+                } else {
+                    mSocket = mNetwork.getSocketFactory().createSocket();
+                }
             }
             mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
             // After the socket connects to an SSL server, confirm that the hostname is as expected
diff --git a/src/com/android/phone/common/mail/store/ImapStore.java b/src/com/android/phone/common/mail/store/ImapStore.java
index 1c91e76..605abbb 100644
--- a/src/com/android/phone/common/mail/store/ImapStore.java
+++ b/src/com/android/phone/common/mail/store/ImapStore.java
@@ -17,6 +17,7 @@
 package com.android.phone.common.mail.store;
 
 import android.content.Context;
+import android.net.Network;
 
 import com.android.phone.common.mail.internet.MimeMessage;
 import com.android.phone.common.mail.MailTransport;
@@ -50,11 +51,11 @@
      * Contains all the information necessary to log into an imap server
      */
     public ImapStore(Context context, String username, String password, int port,
-            String serverName, int flags) {
+            String serverName, int flags, Network network) {
         mContext = context;
         mUsername = username;
         mPassword = password;
-        mTransport = new MailTransport(context, serverName, port, flags);
+        mTransport = new MailTransport(context, network, serverName, port, flags);
     }
 
     public Context getContext() {
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index ce738b6..86036ce 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -25,6 +25,8 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneUtils;
 import com.android.phone.R;
 import com.android.phone.SubscriptionInfoHelper;
 import com.android.services.telephony.sip.SipAccountRegistry;
@@ -103,22 +105,56 @@
 
         addPreferencesFromResource(R.xml.phone_account_settings);
 
+        /**
+         * Here we make decisions about what we will and will not display with regards to phone-
+         * account settings.  The basic settings structure is this:
+         * (1) <Make Calls With...>  // Lets user pick a default account for outgoing calls
+         * (2) <Account List>
+         *       <Account>
+         *       ...
+         *       <Account>
+         *     </Account List>
+         * (3) <All Accounts>  // Lets user enable/disable third-party accounts. SIM-based accounts
+         *                     // are always enabled and so aren't relevant here.
+         *
+         * Here are the rules that we follow:
+         * - (1) is only shown if there are multiple enabled accounts, including SIM accounts.
+         *   This can be 2+ SIM accounts, 2+ third party accounts or any combination.
+         * - (2) The account list only lists (a) enabled third party accounts and (b) SIM-based
+         *   accounts. However, for single-SIM devices, if the only account to show is the
+         *   SIM-based account, we don't show the list at all under the assumption that the user
+         *   already knows about the account.
+         * - (3) Is only shown if there exist any third party accounts.  If none exist, then the
+         *   option is hidden since there is nothing that can be done in it.
+         *
+         * By far, the most common case for users will be the single-SIM device without any
+         * third party accounts. IOW, the great majority of users won't see any of these options.
+         */
         mAccountList = (PreferenceCategory) getPreferenceScreen().findPreference(
                 ACCOUNTS_LIST_CATEGORY_KEY);
-        if (shouldShowConnectionServiceList()) {
-            initAccountList();
+        List<PhoneAccountHandle> allNonSimAccounts =
+                getCallingAccounts(false /* includeSims */, true /* includeDisabled */);
+        // Check to see if we should show the entire section at all.
+        if (shouldShowConnectionServiceList(allNonSimAccounts)) {
+            List<PhoneAccountHandle> enabledAccounts =
+                    getCallingAccounts(true /* includeSims */, false /* includeDisabled */);
+            // Initialize the account list with the set of enabled & SIM accounts.
+            initAccountList(enabledAccounts);
 
             mDefaultOutgoingAccount = (AccountSelectionPreference)
                     getPreferenceScreen().findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
             mDefaultOutgoingAccount.setListener(this);
-            if (mTelecomManager.getCallCapablePhoneAccounts().size() > 1) {
+
+            // Only show the 'Make Calls With..." option if there are multiple accounts.
+            if (enabledAccounts.size() > 1) {
                 updateDefaultOutgoingAccountsModel();
             } else {
                 mAccountList.removePreference(mDefaultOutgoingAccount);
             }
 
             Preference allAccounts = getPreferenceScreen().findPreference(ALL_CALLING_ACCOUNTS_KEY);
-            if (getNonSimCallingAccounts(true).isEmpty() && allAccounts != null) {
+            // If there are no third party (nonSim) accounts, then don't show enable/disable dialog.
+            if (allNonSimAccounts.isEmpty() && allAccounts != null) {
                 mAccountList.removePreference(allAccounts);
             }
         } else {
@@ -296,7 +332,7 @@
     private void updateDefaultOutgoingAccountsModel() {
         mDefaultOutgoingAccount.setModel(
                 mTelecomManager,
-                mTelecomManager.getCallCapablePhoneAccounts(),
+                getCallingAccounts(true /* includeSims */, false /* includeDisabled */),
                 mTelecomManager.getUserSelectedOutgoingPhoneAccount(),
                 getString(R.string.phone_accounts_ask_every_time));
     }
@@ -329,20 +365,22 @@
         }
     }
 
-    private void initAccountList() {
+    private void initAccountList(List<PhoneAccountHandle> enabledAccounts) {
+
         boolean isMultiSimDevice = mTelephonyManager.isMultiSimEnabled();
 
         // On a single-SIM device, do not list any accounts if the only account is the SIM-based
         // one. This is because on single-SIM devices, we do not expose SIM settings through the
         // account listing entry so showing it does nothing to help the user. Nor does the lack of
         // action match the "Settings" header above the listing.
-        if (!isMultiSimDevice && getNonSimCallingAccounts(false).isEmpty()) {
+        if (!isMultiSimDevice && getCallingAccounts(
+                false /* includeSims */, false /* includeDisabled */).isEmpty()){
             return;
         }
 
         // Obtain the list of phone accounts.
         List<PhoneAccount> accounts = new ArrayList<>();
-        for (PhoneAccountHandle handle : mTelecomManager.getCallCapablePhoneAccounts()) {
+        for (PhoneAccountHandle handle : enabledAccounts) {
             PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
             if (account != null) {
                 accounts.add(account);
@@ -453,21 +491,29 @@
         return null;
     }
 
-    private boolean shouldShowConnectionServiceList() {
-        return mTelephonyManager.isMultiSimEnabled() ||
-            getNonSimCallingAccounts(true).size() > 0;
+    private boolean shouldShowConnectionServiceList(List<PhoneAccountHandle> allNonSimAccounts) {
+        return mTelephonyManager.isMultiSimEnabled() || allNonSimAccounts.size() > 0;
     }
 
-    private List<PhoneAccountHandle> getNonSimCallingAccounts(boolean includeDisabledAccounts) {
+    private List<PhoneAccountHandle> getCallingAccounts(
+            boolean includeSims, boolean includeDisabledAccounts) {
+        PhoneAccountHandle emergencyAccountHandle = getEmergencyPhoneAccount();
+
         List<PhoneAccountHandle> accountHandles =
                 mTelecomManager.getCallCapablePhoneAccounts(includeDisabledAccounts);
         for (Iterator<PhoneAccountHandle> i = accountHandles.iterator(); i.hasNext();) {
             PhoneAccountHandle handle = i.next();
+            if (handle.equals(emergencyAccountHandle)) {
+                // never include emergency call accounts in this piece of code.
+                i.remove();
+                continue;
+            }
+
             PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
-            if (account == null ||
-                    (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION))) {
-                // If the account is no longer valid OR the account is a built-in SIM account,
-                // remove!
+            if (account == null) {
+                i.remove();
+            } else if (!includeSims &&
+                    account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
                 i.remove();
             }
         }
@@ -477,4 +523,9 @@
     private String nullToEmpty(String str) {
         return str == null ? "" : str;
     }
+
+    private PhoneAccountHandle getEmergencyPhoneAccount() {
+        return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
+                (Phone) null, "" /* prefix */, true /* isEmergency */);
+    }
 }
diff --git a/src/com/android/phone/vvm/omtp/OmtpConstants.java b/src/com/android/phone/vvm/omtp/OmtpConstants.java
index 971d2d6..fa3cb63 100644
--- a/src/com/android/phone/vvm/omtp/OmtpConstants.java
+++ b/src/com/android/phone/vvm/omtp/OmtpConstants.java
@@ -188,4 +188,6 @@
         put(RETURN_CODE, RETURN_CODE_VALUES);
     }};
 
+    /** Indicates the client is Google visual voicemail version 1.0. */
+    public static final String CLIENT_TYPE_GOOGLE_10 = "google.vvm.10";
 }
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 917374c..95fcb24 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -103,7 +103,7 @@
         Log.i(TAG, "Requesting VVM activation for subId: " + subId);
         SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
         OmtpMessageSender messageSender = new OmtpMessageSender(smsManager,
-                (short) applicationPort, destinationNumber, null,
+                (short) applicationPort, destinationNumber, OmtpConstants.CLIENT_TYPE_GOOGLE_10,
                 OmtpConstants.PROTOCOL_VERSION1_1, null);
         messageSender.requestVvmActivation(null);
     }
diff --git a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
index ae32c27..179fec3 100644
--- a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
+++ b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
@@ -21,6 +21,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.Uri;
 import android.provider.VoicemailContract;
 import android.provider.VoicemailContract.Voicemails;
@@ -28,6 +32,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.phone.PhoneUtils;
 import com.android.phone.vvm.omtp.imap.ImapHelper;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSyncAccountManager;
 
@@ -45,12 +50,22 @@
     public static final int SOURCE_DATA = 0;
     public static final int PHONE_ACCOUNT_ID = 1;
 
+    // Timeout used to call ConnectivityManager.requestNetwork
+    private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000;
+
     private ContentResolver mContentResolver;
     private Uri mUri;
+    private NetworkRequest mNetworkRequest;
+    private OmtpVvmNetworkRequestCallback mNetworkCallback;
+    private Context mContext;
+    private Account mAccount;
+    private String mUid;
+    private ConnectivityManager mConnectivityManager;
 
     @Override
     public void onReceive(final Context context, Intent intent) {
         if (VoicemailContract.ACTION_FETCH_VOICEMAIL.equals(intent.getAction())) {
+            mContext = context;
             mContentResolver = context.getContentResolver();
             mUri = intent.getData();
 
@@ -71,7 +86,7 @@
             }
             try {
                 if (cursor.moveToFirst()) {
-                    final String uid = cursor.getString(SOURCE_DATA);
+                    mUid = cursor.getString(SOURCE_DATA);
                     String accountId = cursor.getString(PHONE_ACCOUNT_ID);
                     if (TextUtils.isEmpty(accountId)) {
                         TelephonyManager telephonyManager = (TelephonyManager)
@@ -83,27 +98,71 @@
                             return;
                         }
                     }
-                    final Account account = new Account(accountId,
+                    mAccount = new Account(accountId,
                             OmtpVvmSyncAccountManager.ACCOUNT_TYPE);
 
                     if (!OmtpVvmSyncAccountManager.getInstance(context)
-                            .isAccountRegistered(account)) {
+                            .isAccountRegistered(mAccount)) {
                         Log.w(TAG, "Account not registered - cannot retrieve message.");
                         return;
                     }
 
-                    Executor executor = Executors.newCachedThreadPool();
-                    executor.execute(new Runnable() {
-                        @Override
-                        public void run() {
-                            new ImapHelper(context, account).fetchVoicemailPayload(
-                                    new VoicemailFetchedCallback(context, mUri), uid);
-                        }
-                    });
+                    int subId = PhoneUtils.getSubIdForPhoneAccountHandle(
+                            PhoneUtils.makePstnPhoneAccountHandle(accountId));
+
+                    mNetworkRequest = new NetworkRequest.Builder()
+                            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                            .setNetworkSpecifier(Integer.toString(subId))
+                            .build();
+                    mNetworkCallback = new OmtpVvmNetworkRequestCallback();
+                    getConnectivityManager().requestNetwork(
+                            mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);
                 }
             } finally {
                 cursor.close();
             }
         }
     }
+
+    private class OmtpVvmNetworkRequestCallback extends ConnectivityManager.NetworkCallback {
+        @Override
+        public void onAvailable(final Network network) {
+            super.onAvailable(network);
+
+            Executor executor = Executors.newCachedThreadPool();
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    new ImapHelper(mContext, mAccount, network).fetchVoicemailPayload(
+                            new VoicemailFetchedCallback(mContext, mUri), mUid);
+                    releaseNetwork();
+                }
+            });
+        }
+
+        @Override
+        public void onLost(Network network) {
+            super.onLost(network);
+            releaseNetwork();
+        }
+
+        @Override
+        public void onUnavailable() {
+            super.onUnavailable();
+            releaseNetwork();
+        }
+    }
+
+    private void releaseNetwork() {
+        getConnectivityManager().unregisterNetworkCallback(mNetworkCallback);
+    }
+
+    private ConnectivityManager getConnectivityManager() {
+        if (mConnectivityManager == null) {
+            mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
+                    Context.CONNECTIVITY_SERVICE);
+        }
+        return mConnectivityManager;
+    }
 }
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index 1368286..0d02700 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -18,6 +18,7 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.content.Context;
+import android.net.Network;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.Voicemail;
 
@@ -61,7 +62,7 @@
     private Context mContext;
     private PhoneAccountHandle mPhoneAccount;
 
-    public ImapHelper(Context context, Account account) {
+    public ImapHelper(Context context, Account account, Network network) {
         try {
             mContext = context;
             mPhoneAccount = PhoneUtils.makePstnPhoneAccountHandle(account.name);
@@ -75,7 +76,7 @@
                     accountManager.getUserData(account, OmtpConstants.IMAP_PORT));
             // TODO: determine the security protocol (e.g. ssl, tls, none, etc.)
             mImapStore = new ImapStore(
-                    context, username, password, port, serverName, ImapStore.FLAG_NONE);
+                    context, username, password, port, serverName, ImapStore.FLAG_NONE, network);
         } catch (NumberFormatException e) {
             LogUtils.e(TAG, e, "Could not parse port number");
         }
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index 89f69e0..7a2f5ad 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -126,11 +126,6 @@
                     VoicemailContract.Status.CONFIGURATION_STATE_OK,
                     VoicemailContract.Status.DATA_CHANNEL_STATE_OK,
                     VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
-
-            Bundle bundle = new Bundle();
-            bundle.putBoolean(OmtpVvmSyncAdapter.SYNC_EXTRAS_DOWNLOAD, true);
-            bundle.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
-            ContentResolver.requestSync(account, VoicemailContract.AUTHORITY, bundle);
         }
 
         // Save the IMAP credentials in the corresponding account object so they are
@@ -139,5 +134,10 @@
 
         // Add a phone state listener so that changes to the communication channels can be recorded.
         vvmAccountSyncManager.addPhoneStateListener(account);
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(OmtpVvmSyncAdapter.SYNC_EXTRAS_DOWNLOAD, true);
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+        ContentResolver.requestSync(account, VoicemailContract.AUTHORITY, bundle);
     }
 }
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index 2fbeaab..7fb4df2 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -80,7 +80,7 @@
         @Override
         public void onPerformSync(Account account, Bundle extras, String authority,
                 ContentProviderClient provider, SyncResult syncResult) {
-            ImapHelper imapHelper = new ImapHelper(mContext, account);
+            ImapHelper imapHelper = new ImapHelper(mContext, account, null);
             VoicemailsQueryHelper queryHelper = new VoicemailsQueryHelper(mContext);
 
             if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)) {