Merge "Send outbound visual voicemail SMS as non-persistable" into nyc-mr1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 511febd..a867260 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -60,6 +60,14 @@
     <protected-broadcast android:name= "android.intent.action.CARRIER_SIGNAL_PCO_VALUE" />
     <protected-broadcast android:name= "android.intent.action.VOICEMAIL_SMS_RECEIVED" />
     <protected-broadcast android:name= "com.android.intent.isim_refresh" />
+    <protected-broadcast android:name= "com.android.ims.IMS_SERVICE_UP" />
+    <protected-broadcast android:name= "com.android.ims.IMS_SERVICE_DOWN" />
+    <protected-broadcast android:name= "com.android.ims.IMS_INCOMING_CALL" />
+    <protected-broadcast android:name= "com.android.ims.internal.uce.UCE_SERVICE_UP" />
+    <protected-broadcast android:name= "com.android.ims.internal.uce.UCE_SERVICE_DOWN" />
+    <protected-broadcast android:name= "com.android.imsconnection.DISCONNECTED" />
+    <protected-broadcast android:name= "com.android.intent.action.IMS_FEATURE_CHANGED" />
+    <protected-broadcast android:name= "com.android.intent.action.IMS_CONFIG_CHANGED" />
 
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 6f7e64d..41628c9 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -392,7 +392,7 @@
     <string name="simContacts_emptyLoading" msgid="2203331234764498011">"Ընթերցում է SIM քարտից..."</string>
     <string name="simContacts_empty" msgid="5270660846489561932">"Ձեր SIM քարտում կոնտակտներ չկան:"</string>
     <string name="simContacts_title" msgid="1861472842524839921">"Ընտրեք կոնտակտները ներմուծման համար"</string>
-    <string name="simContacts_airplaneMode" msgid="5254946758982621072">"Կոնտակտները SIM քարտից ներմուծելու համար անջատեք ինքնաթիռային ռեժիմը:"</string>
+    <string name="simContacts_airplaneMode" msgid="5254946758982621072">"Կոնտակտները SIM քարտից ներմուծելու համար անջատեք Ինքնաթիռի ռեժիմը:"</string>
     <string name="enable_pin" msgid="5422767284133234860">"Միացնել/անջատել SIM PIN-ը"</string>
     <string name="change_pin" msgid="9174186126330785343">"Փոխել SIM PIN-ը"</string>
     <string name="enter_pin_text" msgid="8532615714751931951">"SIM PIN՝"</string>
@@ -443,8 +443,8 @@
     <string name="notification_voicemail_no_vm_number" msgid="760963466895609716">"Ձայնային փոստի համարն անհայտ է"</string>
     <string name="notification_network_selection_title" msgid="4224455487793492772">"Ծառայություններ չկան"</string>
     <string name="notification_network_selection_text" msgid="2607085729661923269">"Ընտրված ցանցը (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) անհասանելի է"</string>
-    <string name="incall_error_power_off" msgid="2947938060513306698">"Զանգ կատարելու համար անջատեք ինքնաթիռային ռեժիմը:"</string>
-    <string name="incall_error_power_off_wfc" msgid="8711428920632416575">"Զանգ կատարելու համար անջատեք ինքնաթիռային ռեժիմը կամ միացեք անլար ցանցին:"</string>
+    <string name="incall_error_power_off" msgid="2947938060513306698">"Զանգ կատարելու համար անջատեք Ինքնաթիռի ռեժիմը:"</string>
+    <string name="incall_error_power_off_wfc" msgid="8711428920632416575">"Զանգ կատարելու համար անջատեք Ինքնաթիռի ռեժիմը կամ միացեք անլար ցանցին:"</string>
     <string name="incall_error_ecm_emergency_only" msgid="738708660612388692">"Սովորական զանգ կատարելու համար դուրս եկեք արտակարգ իրավիճակների հետզանգի ռեժիմից:"</string>
     <string name="incall_error_emergency_only" msgid="4678640422710818317">"Ցանցում գրանցված չէ:"</string>
     <string name="incall_error_out_of_service" msgid="4100065333878929223">"Բջջային ցանցն անհասանելի է:"</string>
@@ -464,7 +464,7 @@
     <string name="emergency_enable_radio_dialog_title" msgid="4627849966634578257">"Արտակարգ իրավիճակների զանգ"</string>
     <string name="emergency_enable_radio_dialog_message" msgid="207613549344420291">"Ռադիոն միացվում է..."</string>
     <string name="emergency_enable_radio_dialog_retry" msgid="5960061579996526883">"Ծառայությունը մատչելի չէ: Նորից փորձեք…"</string>
-    <string name="radio_off_during_emergency_call" msgid="2535800034010306830">"Արտակարգ իրավիճակների զանգի ժամանակ հնարավոր չէ մտնել ինքնաթիռային ռեժիմ:"</string>
+    <string name="radio_off_during_emergency_call" msgid="2535800034010306830">"Արտակարգ իրավիճակների զանգի ժամանակ հնարավոր չէ մտնել ինքնաթիռի ռեժիմ:"</string>
     <string name="dial_emergency_error" msgid="1509085166367420355">"Հնարավոր չէ զանգել: <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> համարը արտակարգ իրավիճակի համար չէ:"</string>
     <string name="dial_emergency_empty_error" msgid="9130194953830414638">"Հնարավոր չէ զանգել: Հավաքեք արտակարգ իրավիճակի որևէ համար:"</string>
     <string name="dialerKeyboardHintText" msgid="9192914825413747792">"Օգտագործեք ստեղնաշարը՝ համարհավաքման համար"</string>
diff --git a/res/xml/vvm_config.xml b/res/xml/vvm_config.xml
index 79edaa6..d55fdb2 100644
--- a/res/xml/vvm_config.xml
+++ b/res/xml/vvm_config.xml
@@ -134,5 +134,6 @@
     <string name="vvm_type_string">vvm_type_vvm3</string>
     <string name="vvm_client_prefix_string">//VZWVVM</string>
     <boolean name="vvm_cellular_data_required_bool" value="true"/>
+    <boolean name="vvm_legacy_mode_enabled_bool" value="true"/>
   </pbundle_as_map>
 </list>
\ No newline at end of file
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 3204a9f..5a40322 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -665,9 +665,11 @@
         // TODO: Check that the calling packages is privileged for subId specifically.
         int privilegeStatus = TelephonyManager.from(mContext).checkCarrierPrivilegesForPackage(
                 callingPackageName);
+        // Requires the calling app to be either a carrier privileged app or
+        // system privileged app with MODIFY_PHONE_STATE permission.
         if (privilegeStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
-            throw new SecurityException(
-                    "Package is not privileged for subId=" + subId + ": " + callingPackageName);
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
+                    "Require carrier privileges or MODIFY_PHONE_STATE permission.");
         }
 
         // This method should block until deleting has completed, so that an error which prevents us
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 4dd7d0b..9f70349 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -64,7 +64,6 @@
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.sip.SipPhone;
 import com.android.phone.CallGatewayManager.RawGatewayInfo;
-import com.android.services.telephony.TelephonyConnectionService;
 
 import java.util.Arrays;
 import java.util.List;
@@ -2442,7 +2441,7 @@
         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     }
 
-    static Phone getPhoneForPhoneAccountHandle(PhoneAccountHandle handle) {
+    public static Phone getPhoneForPhoneAccountHandle(PhoneAccountHandle handle) {
         if (handle != null && handle.getComponentName().equals(getPstnConnectionServiceName())) {
             return getPhoneFromIccId(handle.getId());
         }
diff --git a/src/com/android/phone/settings/VoicemailRingtonePreference.java b/src/com/android/phone/settings/VoicemailRingtonePreference.java
index 4ee4e64..bb82d4f 100644
--- a/src/com/android/phone/settings/VoicemailRingtonePreference.java
+++ b/src/com/android/phone/settings/VoicemailRingtonePreference.java
@@ -17,8 +17,13 @@
  * it is created or updated.
  */
 public class VoicemailRingtonePreference extends RingtonePreference {
+    public interface VoicemailRingtoneNameChangeListener {
+        void onVoicemailRingtoneNameChanged(CharSequence name);
+    }
+
     private static final int MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY = 1;
 
+    private VoicemailRingtoneNameChangeListener mVoicemailRingtoneNameChangeListener;
     private Runnable mVoicemailRingtoneLookupRunnable;
     private Handler mVoicemailRingtoneLookupComplete;
 
@@ -32,6 +37,10 @@
             public void handleMessage(Message msg) {
                 switch (msg.what) {
                     case MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY:
+                        if (mVoicemailRingtoneNameChangeListener != null) {
+                            mVoicemailRingtoneNameChangeListener.onVoicemailRingtoneNameChanged(
+                                    (CharSequence) msg.obj);
+                        }
                         setSummary((CharSequence) msg.obj);
                         break;
                 }
@@ -39,7 +48,7 @@
         };
     }
 
-    public void init(Phone phone) {
+    public void init(Phone phone, CharSequence oldRingtoneName) {
         mPhone = phone;
 
         // Requesting the ringtone will trigger migration if necessary.
@@ -48,6 +57,7 @@
         final Preference preference = this;
         final String preferenceKey =
                 VoicemailNotificationSettingsUtil.getVoicemailRingtoneSharedPrefsKey(mPhone);
+        setSummary(oldRingtoneName);
         mVoicemailRingtoneLookupRunnable = new Runnable() {
             @Override
             public void run() {
@@ -63,6 +73,10 @@
         updateRingtoneName();
     }
 
+    public void setVoicemailRingtoneNameChangeListener(VoicemailRingtoneNameChangeListener l) {
+        mVoicemailRingtoneNameChangeListener = l;
+    }
+
     @Override
     protected Uri onRestoreRingtone() {
         return VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index 09f9e03..b10af6e 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -59,7 +59,8 @@
         implements DialogInterface.OnClickListener,
                 Preference.OnPreferenceChangeListener,
                 EditPhoneNumberPreference.OnDialogClosedListener,
-                EditPhoneNumberPreference.GetDefaultNumberListener {
+                EditPhoneNumberPreference.GetDefaultNumberListener,
+                VoicemailRingtonePreference.VoicemailRingtoneNameChangeListener {
     private static final String LOG_TAG = VoicemailSettingsActivity.class.getSimpleName();
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
@@ -186,6 +187,8 @@
     private CallForwardInfo[] mNewFwdSettings;
     private String mNewVMNumber;
 
+    private CharSequence mOldVmRingtoneName = "";
+
     /**
      * Used to indicate that the voicemail preference should be shown.
      */
@@ -253,7 +256,8 @@
 
         mVoicemailNotificationRingtone = (VoicemailRingtonePreference) findPreference(
                 getResources().getString(R.string.voicemail_notification_ringtone_key));
-        mVoicemailNotificationRingtone.init(mPhone);
+        mVoicemailNotificationRingtone.setVoicemailRingtoneNameChangeListener(this);
+        mVoicemailNotificationRingtone.init(mPhone, mOldVmRingtoneName);
 
         mVoicemailNotificationVibrate = (CheckBoxPreference) findPreference(
                 getResources().getString(R.string.voicemail_notification_vibrate_key));
@@ -541,6 +545,11 @@
         super.onActivityResult(requestCode, resultCode, data);
     }
 
+    @Override
+    public void onVoicemailRingtoneNameChanged(CharSequence name) {
+        mOldVmRingtoneName = name;
+    }
+
     /**
      * Simulates user clicking on a passed preference.
      * Usually needed when the preference is a dialog preference and we want to invoke
diff --git a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
index cd27ade..dd394b4 100644
--- a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
+++ b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
@@ -74,6 +74,12 @@
             "vvm_ssl_port_number_int";
 
     /**
+     * @see #isLegacyModeEnabled()
+     */
+    static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL =
+            "vvm_legacy_mode_enabled_bool";
+
+    /**
      * Ban a capability reported by the server from being used. The array of string should be a
      * subset of the capabilities returned IMAP CAPABILITY command.
      *
@@ -99,7 +105,7 @@
         TelephonyManager telephonyManager =
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mTelephonyConfig = new TelephonyVvmConfigManager(context.getResources())
-                .getConfig(telephonyManager.getNetworkOperator(subId));
+                .getConfig(telephonyManager.getSimOperator(subId));
 
         mVvmType = getVvmType();
         mProtocol = VisualVoicemailProtocolFactory.create(mVvmType);
@@ -263,24 +269,46 @@
         return "//VVM";
     }
 
+    /**
+     * Should legacy mode be used when the OMTP VVM client is disabled?
+     *
+     * <p>Legacy mode is a mode that on the carrier side visual voicemail is still activated, but on
+     * the client side all network operations are disabled. SMSs are still monitored so a new
+     * message SYNC SMS will be translated to show a message waiting indicator, like traditional
+     * voicemails.
+     *
+     * <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
+     * function without the data cost.
+     */
+    public boolean isLegacyModeEnabled() {
+        return (boolean) getValue(KEY_VVM_LEGACY_MODE_ENABLED_BOOL, false);
+    }
+
     public void startActivation() {
         VoicemailStatus.edit(mContext, mSubId)
                 .setType(getVvmType())
                 .apply();
 
-        TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
-        telephonyManager.enableVisualVoicemailSmsFilter(mSubId,
-                new VisualVoicemailSmsFilterSettings.Builder().setClientPrefix(getClientPrefix())
-                        .build());
+        activateSmsFilter();
 
         if (mProtocol != null) {
             mProtocol.startActivation(this);
         }
     }
 
+    public void activateSmsFilter() {
+        TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+        telephonyManager.enableVisualVoicemailSmsFilter(mSubId,
+                new VisualVoicemailSmsFilterSettings.Builder().setClientPrefix(getClientPrefix())
+                        .build());
+    }
+
     public void startDeactivation() {
-        mContext.getSystemService(TelephonyManager.class)
-                .disableVisualVoicemailSmsFilter(mSubId);
+        if (!isLegacyModeEnabled()) {
+            // SMS should still be filtered in legacy mode
+            mContext.getSystemService(TelephonyManager.class)
+                    .disableVisualVoicemailSmsFilter(mSubId);
+        }
         if (mProtocol != null) {
             mProtocol.startDeactivation(this);
         }
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 833daa9..a8de8cd 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -23,6 +23,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -42,7 +43,7 @@
  */
 public class SimChangeReceiver extends BroadcastReceiver {
 
-    private static final String TAG = "SimChangeReceiver";
+    private static final String TAG = "VvmSimChangeReceiver";
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -73,8 +74,9 @@
                     Log.i(TAG, "Received SIM change for invalid subscription id.");
                     return;
                 }
-
+                Log.d(TAG, "Carrier config changed");
                 if (!UserManager.get(context).isUserUnlocked()) {
+                    Log.d(TAG, "User locked, activation request delayed until unlock");
                     OmtpBootCompletedReceiver.addDeferredSubId(context, subId);
                 } else {
                     processSubId(context, subId);
@@ -90,7 +92,7 @@
             PhoneAccountHandle phoneAccount = PhoneAccountHandleConverter.fromSubId(subId);
 
             if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(context, phoneAccount)) {
-                LocalLogHelper.log(TAG, "Sim state or carrier config changed: requesting"
+                Log.i(TAG, "Sim state or carrier config changed: requesting"
                         + " activation for " + phoneAccount.getId());
 
                 // Add a phone state listener so that changes to the communication channels
@@ -99,11 +101,19 @@
                         phoneAccount);
                 carrierConfigHelper.startActivation();
             } else {
+                if (carrierConfigHelper.isLegacyModeEnabled()) {
+                    // SMS still need to be filtered under legacy mode.
+                    carrierConfigHelper.activateSmsFilter();
+                }
                 // It may be that the source was not registered to begin with but we want
                 // to run through the steps to remove the source just in case.
                 OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
                 Log.v(TAG, "Sim change for disabled account.");
             }
+        } else {
+            String mccMnc = context.getSystemService(TelephonyManager.class).getSimOperator(subId);
+            Log.d(TAG,
+                    "visual voicemail not supported for carrier " + mccMnc + " on subId " + subId);
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
new file mode 100644
index 0000000..53422c4
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/sms/LegacyModeSmsHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.phone.vvm.omtp.sms;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneUtils;
+import com.android.phone.vvm.omtp.OmtpConstants;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+
+/**
+ * Class ot handle voicemail SMS under legacy mode
+ *
+ * @see OmtpVvmCarrierConfigHelper#isLegacyModeEnabled()
+ */
+public class LegacyModeSmsHandler {
+
+    private static final String TAG = "LegacyModeSmsHandler";
+
+    public static void handle(Context context, Intent intent, PhoneAccountHandle handle) {
+        Log.v(TAG, "processing VVM SMS on legacy mode");
+        String eventType = intent.getExtras()
+                .getString(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX);
+        Bundle data = intent.getExtras().getBundle(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS);
+
+        if (eventType.equals(OmtpConstants.SYNC_SMS_PREFIX)) {
+            SyncMessage message = new SyncMessage(data);
+            Log.v(TAG, "Received SYNC sms for " + handle.getId() +
+                    " with event " + message.getSyncTriggerEvent());
+
+            switch (message.getSyncTriggerEvent()) {
+                case OmtpConstants.NEW_MESSAGE:
+                case OmtpConstants.MAILBOX_UPDATE:
+                    // The user has called into the voicemail and the new message count could
+                    // change.
+                    // For some carriers new message count could be set to 0 even if there are still
+                    // unread messages, to clear the message waiting indicator.
+                    Log.v(TAG, "updating MWI");
+                    Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(handle);
+                    // Setting voicemail message count to non-zero will show the telephony voicemail
+                    // notification, and zero will clear it.
+                    phone.setVoiceMessageCount(message.getNewMessageCount());
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index d9939c2..6bb053f 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -63,8 +63,13 @@
             return;
         }
 
+        OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(mContext, subId);
         if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mContext, phone)) {
-            Log.i(TAG, "Received vvm message for disabled vvm source.");
+            if (helper.isLegacyModeEnabled()) {
+                LegacyModeSmsHandler.handle(context, intent, phone);
+            } else {
+                Log.i(TAG, "Received vvm message for disabled vvm source.");
+            }
             return;
         }