Merge "Force phone number to LTR in forwarding summary." into mnc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9e54312..50741ac 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -667,5 +667,12 @@
android:name="com.android.phone.vvm.omtp.sync.OmtpVvmSyncService"
android:exported="false"
/>
+ <receiver android:name="com.android.phone.vvm.omtp.VvmPackageInstallReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_INSTALL" />
+ <action android:name="android.intent.action.PACKAGE_ADDED" />
+ <data android:scheme="package"/>
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9b3dee9..e8dac11 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1217,6 +1217,9 @@
<!-- Dialog title for the vibration settings for voice mail notifications [CHAR LIMIT=40]-->
<string name="voicemail_notification_vibarte_when_dialog_title" msgid="8995274609647451109">Vibrate</string>
+ <!-- Visual voicemail on/off title [CHAR LIMIT=40] -->
+ <string name="voicemail_visual_voicemail_switch_title">Visual Voicemail</string>
+
<!-- Voicemail ringtone title. The user clicks on this preference to select
which sound to play when a voicemail notification is received.
[CHAR LIMIT=30] -->
@@ -1282,6 +1285,8 @@
<string name="voicemail_notification_ringtone_key">voicemail_notification_ringtone_key</string>
<!-- DO NOT TRANSLATE. Internal key for a voicemail notification preference. -->
<string name="voicemail_notification_vibrate_key">voicemail_notification_vibrate_key</string>
+ <!-- DO NOT TRANSLATE. Internal key for a visual voicemail preference. -->
+ <string name="voicemail_visual_voicemail_key">voicemail_visual_voicemail_key</string>
<!-- DO NOT TRANSLATE. Internal key for tty mode preference. -->
<string name="tty_mode_key">button_tty_mode_key</string>
<!-- DO NOT TRANSLATE. Internal key for a voicemail notification preference. -->
diff --git a/res/xml/voicemail_settings.xml b/res/xml/voicemail_settings.xml
index a7d5fb4..9334566 100644
--- a/res/xml/voicemail_settings.xml
+++ b/res/xml/voicemail_settings.xml
@@ -60,5 +60,8 @@
android:key="@string/voicemail_notification_vibrate_key"
android:title="@string/voicemail_notification_vibrate_when_title"
android:persistent="true" />
+ <SwitchPreference
+ android:key="@string/voicemail_visual_voicemail_key"
+ android:title="@string/voicemail_visual_voicemail_switch_title" />"
</PreferenceScreen>
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 0c34c5f..66cce28 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -65,13 +65,13 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
/**
* CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays.
- * TODO: implement persist cache
* TODO: handle package install/uninstall events
*/
@@ -97,10 +97,6 @@
// Message codes; see mHandler below.
// Request from SubscriptionInfoUpdater when SIM becomes absent or error.
private static final int EVENT_CLEAR_CONFIG = 0;
- // Request from SubscriptionInfoUpdater to update config.
- private static final int EVENT_UPDATE_CONFIG = 1;
- // Request from carrier app to reload config.
- private static final int EVENT_RELOAD_CONFIG = 2;
// Has connected to default app.
private static final int EVENT_CONNECTED_TO_DEFAULT = 3;
// Has connected to carrier app.
@@ -109,6 +105,12 @@
private static final int EVENT_LOADED_FROM_DEFAULT = 5;
// Config has been loaded from carrier app.
private static final int EVENT_LOADED_FROM_CARRIER = 6;
+ // Attempt to fetch from default app or read from XML.
+ private static final int EVENT_FETCH_DEFAULT = 7;
+ // Attempt to fetch from carrier app or read from XML.
+ private static final int EVENT_FETCH_CARRIER = 8;
+ // A package has been installed, uninstalled, or updated.
+ private static final int EVENT_PACKAGE_CHANGED = 9;
// Tags used for saving and restoring XML documents.
private static final String TAG_DOCUMENT = "carrier_config";
@@ -117,22 +119,29 @@
// Handler to process various events.
//
- // For each phoneId, state transition should be: default app bind->connected->loaded, carrier
- // app (if exists) bind-> connected->loaded. At any time, at most one connection is active. If
- // events are not in this order, previous connection will be unbind, so only latest event takes
- // effect.
+ // For each phoneId, the event sequence should be:
+ // fetch default, connected to default, loaded from default,
+ // fetch carrier, connected to carrier, loaded from carrier.
//
- // We broadcast config change when:
- // 1. loaded from carrier app
- // 2. loaded from default app if no carrier app
- // 3. config cleared, possibly due to sim removed
- // 4. bind or IPC error
+ // If there is a saved config file for either the default app or the carrier app, we skip
+ // binding to the app and go straight from fetch to loaded.
+ //
+ // At any time, at most one connection is active. If events are not in this order, previous
+ // connection will be unbound, so only latest event takes effect.
+ //
+ // We broadcast ACTION_CARRIER_CONFIG_CHANGED after:
+ // 1. loading from carrier app (even if read from a file)
+ // 2. loading from default app if there is no carrier app (even if read from a file)
+ // 3. clearing config (e.g. due to sim removal)
+ // 4. encountering bind or IPC error
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
int phoneId = msg.arg1;
log("mHandler: " + msg.what + " phoneId: " + phoneId);
+ String iccid;
CarrierIdentifier carrierId;
+ String carrierPackageName;
CarrierServiceConnection conn;
PersistableBundle config;
switch (msg.what) {
@@ -145,14 +154,32 @@
mServiceConnection[phoneId] = null;
broadcastConfigChangedIntent(phoneId);
break;
- case EVENT_UPDATE_CONFIG:
- // Use persist cache to avoid loading from app.
- // Fall through to next event if cache not hit.
- case EVENT_RELOAD_CONFIG:
- if (!bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE,
- phoneId, EVENT_CONNECTED_TO_DEFAULT)) {
- // Send bcast if bind fails
- broadcastConfigChangedIntent(phoneId);
+
+ case EVENT_PACKAGE_CHANGED:
+ carrierPackageName = (String) msg.obj;
+ deleteConfigForPackage(carrierPackageName);
+ int numPhones = TelephonyManager.from(mContext).getPhoneCount();
+ for (int i = 0; i < numPhones; ++i) {
+ updateConfigForPhoneId(i);
+ }
+ break;
+
+ case EVENT_FETCH_DEFAULT:
+ iccid = getIccIdForPhoneId(phoneId);
+ config = restoreConfigFromXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid);
+ if (config != null) {
+ log("Loaded config from XML. package=" + DEFAULT_CARRIER_CONFIG_PACKAGE
+ + " phoneId=" + phoneId);
+ mConfigFromDefaultApp[phoneId] = config;
+ Message newMsg = obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1);
+ newMsg.getData().putBoolean("loaded_from_xml", true);
+ mHandler.sendMessage(newMsg);
+ } else {
+ if (!bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE,
+ phoneId, EVENT_CONNECTED_TO_DEFAULT)) {
+ // Send bcast if bind fails
+ broadcastConfigChangedIntent(phoneId);
+ }
}
break;
@@ -168,6 +195,8 @@
ICarrierService carrierService = ICarrierService.Stub
.asInterface(conn.service);
config = carrierService.getCarrierConfig(carrierId);
+ iccid = getIccIdForPhoneId(phoneId);
+ saveConfigToXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid, config);
mConfigFromDefaultApp[phoneId] = config;
sendMessage(obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1));
} catch (RemoteException ex) {
@@ -178,23 +207,41 @@
break;
case EVENT_LOADED_FROM_DEFAULT:
- if (mServiceConnection[phoneId] == null) {
+ // If we attempted to bind to the app, but the service connection is null, then
+ // config was cleared while we were waiting and we should not continue.
+ if (!msg.getData().getBoolean("loaded_from_xml", false)
+ && mServiceConnection[phoneId] == null) {
break;
}
- List<String> carrierPackageNames = TelephonyManager.from(mContext)
- .getCarrierPackageNamesForIntentAndPhone(
- new Intent(CarrierService.CONFIG_SERVICE_INTERFACE), phoneId);
- log("Found carrier config app: " + carrierPackageNames);
- if (carrierPackageNames != null && carrierPackageNames.size() > 0) {
- if (!bindToConfigPackage(carrierPackageNames.get(0),
- phoneId, EVENT_CONNECTED_TO_CARRIER)) {
- broadcastConfigChangedIntent(phoneId);
- }
+ carrierPackageName = getCarrierPackageForPhoneId(phoneId);
+ if (carrierPackageName != null) {
+ log("Found carrier config app: " + carrierPackageName);
+ sendMessage(obtainMessage(EVENT_FETCH_CARRIER, phoneId));
} else {
broadcastConfigChangedIntent(phoneId);
}
break;
+ case EVENT_FETCH_CARRIER:
+ carrierPackageName = getCarrierPackageForPhoneId(phoneId);
+ iccid = getIccIdForPhoneId(phoneId);
+ config = restoreConfigFromXml(carrierPackageName, iccid);
+ if (config != null) {
+ log("Loaded config from XML. package=" + carrierPackageName + " phoneId="
+ + phoneId);
+ mConfigFromCarrierApp[phoneId] = config;
+ Message newMsg = obtainMessage(EVENT_LOADED_FROM_CARRIER, phoneId, -1);
+ newMsg.getData().putBoolean("loaded_from_xml", true);
+ sendMessage(newMsg);
+ } else {
+ if (!bindToConfigPackage(carrierPackageName, phoneId,
+ EVENT_CONNECTED_TO_CARRIER)) {
+ // Send bcast if bind fails
+ broadcastConfigChangedIntent(phoneId);
+ }
+ }
+ break;
+
case EVENT_CONNECTED_TO_CARRIER:
carrierId = getCarrierIdForPhoneId(phoneId);
conn = (CarrierServiceConnection) msg.obj;
@@ -208,6 +255,9 @@
ICarrierService carrierService = ICarrierService.Stub
.asInterface(conn.service);
config = carrierService.getCarrierConfig(carrierId);
+ carrierPackageName = getCarrierPackageForPhoneId(phoneId);
+ iccid = getIccIdForPhoneId(phoneId);
+ saveConfigToXml(carrierPackageName, iccid, config);
mConfigFromCarrierApp[phoneId] = config;
sendMessage(obtainMessage(EVENT_LOADED_FROM_CARRIER, phoneId, -1));
} catch (RemoteException ex) {
@@ -218,7 +268,10 @@
break;
case EVENT_LOADED_FROM_CARRIER:
- if (mServiceConnection[phoneId] == null) {
+ // If we attempted to bind to the app, but the service connection is null, then
+ // config was cleared while we were waiting and we should not continue.
+ if (!msg.getData().getBoolean("loaded_from_xml", false)
+ && mServiceConnection[phoneId] == null) {
break;
}
broadcastConfigChangedIntent(phoneId);
@@ -312,18 +365,51 @@
return new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2);
}
+ /** Returns the package name of a priveleged carrier app, or null if there is none. */
+ private String getCarrierPackageForPhoneId(int phoneId) {
+ List<String> carrierPackageNames = TelephonyManager.from(mContext)
+ .getCarrierPackageNamesForIntentAndPhone(
+ new Intent(CarrierService.CONFIG_SERVICE_INTERFACE), phoneId);
+ if (carrierPackageNames != null && carrierPackageNames.size() > 0) {
+ return carrierPackageNames.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ private String getIccIdForPhoneId(int phoneId) {
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+ return null;
+ }
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone == null) {
+ return null;
+ }
+ return phone.getIccSerialNumber();
+ }
+
/**
* Writes a bundle to an XML file.
*
* The bundle will be written to a file named after the package name and ICCID, so that it can
* be restored later with {@link @restoreConfigFromXml}. The XML output will include the bundle
- * and the current version of the specified package. In case of errors, no file will be written.
+ * and the current version of the specified package.
+ *
+ * In case of errors or invalid input, no file will be written.
*
* @param packageName the name of the package from which we fetched this bundle.
* @param iccid the ICCID of the subscription for which this bundle was fetched.
- * @param config the bundle to be written.
+ * @param config the bundle to be written. Null will be treated as an empty bundle.
*/
private void saveConfigToXml(String packageName, String iccid, PersistableBundle config) {
+ if (packageName == null || iccid == null) {
+ loge("Cannot save config with null packageName or iccid.");
+ return;
+ }
+ if (config == null) {
+ config = new PersistableBundle();
+ }
+
final String version = getPackageVersion(packageName);
if (version == null) {
loge("Failed to get package version for: " + packageName);
@@ -361,8 +447,10 @@
* Reads a bundle from an XML file.
*
* This restores a bundle that was written with {@link #saveConfigToXml}. This returns the saved
- * config bundle for the given package and ICCID. If the saved config is for a different package
- * version than the current version, then null will be returned.
+ * config bundle for the given package and ICCID.
+ *
+ * In case of errors, or if the saved config is from a different package version than the
+ * current version, then null will be returned.
*
* @param packageName the name of the package from which we fetched this bundle.
* @param iccid the ICCID of the subscription for which this bundle was fetched.
@@ -375,6 +463,10 @@
loge("Failed to get package version for: " + packageName);
return null;
}
+ if (packageName == null || iccid == null) {
+ loge("Cannot restore config with null packageName or iccid.");
+ return null;
+ }
PersistableBundle restoredBundle = null;
FileInputStream inFile = null;
@@ -414,23 +506,48 @@
return restoredBundle;
}
+ /** Deletes all saved XML files associated with the given package name. */
+ private void deleteConfigForPackage(final String packageName) {
+ File dir = mContext.getFilesDir();
+ File[] packageFiles = dir.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String filename) {
+ return filename.startsWith("carrierconfig-" + packageName + "-");
+ }
+ });
+ for (File f : packageFiles) {
+ log("deleting " + f.getName());
+ f.delete();
+ }
+ }
+
/** Builds a canonical file name for a config file. */
- private String getFilenameForConfig(String packageName, String iccid) {
+ private String getFilenameForConfig(@NonNull String packageName, @NonNull String iccid) {
return "carrierconfig-" + packageName + "-" + iccid + ".xml";
}
- /** Return the current version of a package, or null if the name is not found. */
+ /** Return the current version code of a package, or null if the name is not found. */
private String getPackageVersion(String packageName) {
try {
PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
- return info.versionName;
+ return Integer.toString(info.versionCode);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
- @Override
- public @NonNull PersistableBundle getConfigForSubId(int subId) {
+ /** Read up to date config.
+ *
+ * This reads config bundles for the given phoneId. That means getting the latest bundle from
+ * the default app and a privileged carrier app, if present. This will not bind to an app if we
+ * have a saved config file to use instead.
+ */
+ private void updateConfigForPhoneId(int phoneId) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_FETCH_DEFAULT, phoneId, -1));
+ }
+
+ @Override public
+ @NonNull
+ PersistableBundle getConfigForSubId(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
if (SubscriptionManager.isValidPhoneId(phoneId)) {
@@ -447,11 +564,25 @@
@Override
public void reloadCarrierConfigForSubId(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- if (SubscriptionManager.isValidPhoneId(phoneId)) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELOAD_CONFIG, phoneId, -1));
- } else {
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) {
log("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
+ return;
}
+ String callingPackageName = mContext.getPackageManager().getNameForUid(
+ Binder.getCallingUid());
+ // TODO: Check that the calling packages is privileged for subId specifically.
+ int privilegeStatus = TelephonyManager.from(mContext).checkCarrierPrivilegesForPackage(
+ callingPackageName);
+ if (privilegeStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ throw new SecurityException(
+ "Package is not privileged for subId=" + subId + ": " + callingPackageName);
+ }
+
+ // This method should block until deleting has completed, so that an error which prevents us
+ // from clearing the cache is passed back to the carrier app. With the files successfully
+ // deleted, this can return and we will eventually bind to the carrier app.
+ deleteConfigForPackage(callingPackageName);
+ updateConfigForPhoneId(phoneId);
}
@Override
@@ -469,7 +600,7 @@
break;
case IccCardConstants.INTENT_VALUE_ICC_LOADED:
case IccCardConstants.INTENT_VALUE_ICC_LOCKED:
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_UPDATE_CONFIG, phoneId, -1));
+ updateConfigForPhoneId(phoneId);
break;
}
}
@@ -518,6 +649,18 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
log("Receive action: " + action);
+ switch (action) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ case Intent.ACTION_PACKAGE_CHANGED:
+ case Intent.ACTION_PACKAGE_REMOVED:
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ String packageName = mContext.getPackageManager().getNameForUid(uid);
+ // We don't have a phoneId for arg1.
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName));
+ break;
+
+ }
}
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 8b0b8a9..d066de2 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -159,7 +159,7 @@
static int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
static boolean sVoiceCapable = true;
- // Internal PhoneApp Call state tracker
+ // TODO: Remove, no longer used.
CdmaPhoneCallState cdmaPhoneCallState;
// The currently-active PUK entry activity and progress dialog.
@@ -780,14 +780,8 @@
if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology...");
final Phone phone = PhoneFactory.getPhone(phoneId);
-
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- // Create an instance of CdmaPhoneCallState and initialize it to IDLE
- cdmaPhoneCallState = new CdmaPhoneCallState();
- cdmaPhoneCallState.CdmaPhoneCallStateInit();
- }
- if (!TelephonyCapabilities.supportsOtasp(phone)) {
- //Clean up OTA data in GSM/UMTS. It is valid only for CDMA
+ if (phone == null || !TelephonyCapabilities.supportsOtasp(phone)) {
+ // Clean up OTA for non-CDMA since it is only valid for CDMA.
clearOtaState();
}
@@ -795,7 +789,7 @@
callStateMonitor.updateAfterRadioTechnologyChange();
// Update registration for ICC status after radio technology change
- IccCard sim = phone.getIccCard();
+ IccCard sim = phone == null ? null : phone.getIccCard();
if (sim != null) {
if (DBG) Log.d(LOG_TAG, "Update registration for ICC status...");
@@ -830,6 +824,7 @@
}
Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
+
// The "data disconnected due to roaming" notification is shown
// if (a) you have the "data roaming" feature turned off, and
// (b) you just lost data connectivity because you're roaming.
@@ -916,11 +911,13 @@
*/
// If service just returned, start sending out the queued messages
- ServiceState ss = ServiceState.newFromBundle(intent.getExtras());
-
- if (ss != null) {
- int state = ss.getState();
- notificationMgr.updateNetworkSelection(state);
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ ServiceState ss = ServiceState.newFromBundle(extras);
+ if (ss != null) {
+ int state = ss.getState();
+ notificationMgr.updateNetworkSelection(state);
+ }
}
}
diff --git a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
index e3a9307..5dd3166 100644
--- a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
+++ b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
@@ -20,11 +20,15 @@
import android.preference.PreferenceManager;
import android.telecom.PhoneAccountHandle;
+import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneUtils;
import com.android.phone.vvm.omtp.OmtpConstants;
import com.android.phone.vvm.omtp.sms.StatusMessage;
/**
* Save visual voicemail login values in shared preferences to be retrieved later.
+ * Because a voicemail source is tied 1:1 to a phone account, the phone account handle is used in
+ * the key for each voicemail source and the associated data.
*/
public class VisualVoicemailSettingsUtil {
private static final String VISUAL_VOICEMAIL_SHARED_PREFS_KEY_PREFIX =
@@ -32,6 +36,11 @@
private static final String IS_ENABLED_KEY = "is_enabled";
+ public static void setVisualVoicemailEnabled(Phone phone, boolean isEnabled) {
+ setVisualVoicemailEnabled(phone.getContext(), PhoneUtils.makePstnPhoneAccountHandle(phone),
+ isEnabled);
+ }
+
public static void setVisualVoicemailEnabled(Context context, PhoneAccountHandle phoneAccount,
boolean isEnabled) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
@@ -41,17 +50,22 @@
editor.commit();
}
- public static boolean getVisualVoicemailEnabled(Context context,
+ public static boolean isVisualVoicemailEnabled(Context context,
PhoneAccountHandle phoneAccount) {
if (phoneAccount == null) {
return false;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(getVisualVoicemailSharedPrefsKey(IS_ENABLED_KEY, phoneAccount),
- true);
+ false);
}
- public static void setSourceCredentialsFromStatusMessage(Context context,
+ public static boolean isVisualVoicemailEnabled(Phone phone) {
+ return isVisualVoicemailEnabled(phone.getContext(),
+ PhoneUtils.makePstnPhoneAccountHandle(phone));
+ }
+
+ public static void setVisualVoicemailCredentialsFromStatusMessage(Context context,
PhoneAccountHandle phoneAccount, StatusMessage message) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
@@ -71,7 +85,7 @@
editor.commit();
}
- public static String getCredentialForSource(Context context, String key,
+ public static String getVisualVoicemailCredentials(Context context, String key,
PhoneAccountHandle phoneAccount) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString(getVisualVoicemailSharedPrefsKey(key, phoneAccount), null);
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index f017836..f70ee65 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -28,7 +28,9 @@
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
import android.provider.ContactsContract.CommonDataKinds;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
@@ -40,7 +42,10 @@
import com.android.phone.R;
import com.android.phone.EditPhoneNumberPreference;
import com.android.phone.PhoneGlobals;
+import com.android.phone.PhoneUtils;
import com.android.phone.SubscriptionInfoHelper;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
import java.util.Collection;
import java.util.HashMap;
@@ -187,12 +192,14 @@
private boolean mForeground;
private Phone mPhone;
private SubscriptionInfoHelper mSubscriptionInfoHelper;
+ private OmtpVvmCarrierConfigHelper mOmtpVvmCarrierConfigHelper;
private EditPhoneNumberPreference mSubMenuVoicemailSettings;
private VoicemailProviderListPreference mVoicemailProviders;
private PreferenceScreen mVoicemailSettings;
private VoicemailRingtonePreference mVoicemailNotificationRingtone;
private CheckBoxPreference mVoicemailNotificationVibrate;
+ private SwitchPreference mVoicemailVisualVoicemail;
//*********************************************************************************************
@@ -212,6 +219,8 @@
mSubscriptionInfoHelper.setActionBarTitle(
getActionBar(), getResources(), R.string.voicemail_settings_with_label);
mPhone = mSubscriptionInfoHelper.getPhone();
+ mOmtpVvmCarrierConfigHelper = new OmtpVvmCarrierConfigHelper(
+ mPhone.getContext(), mPhone.getSubId());
}
@Override
@@ -248,6 +257,17 @@
getResources().getString(R.string.voicemail_notification_vibrate_key));
mVoicemailNotificationVibrate.setOnPreferenceChangeListener(this);
+ mVoicemailVisualVoicemail = (SwitchPreference) findPreference(
+ getResources().getString(R.string.voicemail_visual_voicemail_key));
+ if (TelephonyManager.VVM_TYPE_OMTP.equals(mOmtpVvmCarrierConfigHelper.getVvmType()) ||
+ TelephonyManager.VVM_TYPE_CVVM.equals(mOmtpVvmCarrierConfigHelper.getVvmType())) {
+ mVoicemailVisualVoicemail.setOnPreferenceChangeListener(this);
+ mVoicemailVisualVoicemail.setChecked(
+ VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mPhone));
+ } else {
+ prefSet.removePreference(mVoicemailVisualVoicemail);
+ }
+
updateVMPreferenceWidgets(mVoicemailProviders.getValue());
// check the intent that started this activity and pop up the voicemail
@@ -366,6 +386,14 @@
// TODO: Revert to checking reference after migrating voicemail to its own activity.
VoicemailNotificationSettingsUtil.setVibrationEnabled(
mPhone, Boolean.TRUE.equals(objValue));
+ } else if (preference.getKey().equals(mVoicemailVisualVoicemail.getKey())) {
+ if ((Boolean) objValue) {
+ VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mPhone, true);
+ mOmtpVvmCarrierConfigHelper.startActivation();
+ } else {
+ OmtpVvmSourceManager.getInstance(mPhone.getContext()).removeSource(mPhone);
+ mOmtpVvmCarrierConfigHelper.startDeactivation();
+ }
}
// Always let the preference setting proceed.
diff --git a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
index f2df9b1..6300622 100644
--- a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
+++ b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
@@ -16,6 +16,7 @@
package com.android.phone.vvm.omtp;
import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SmsManager;
@@ -34,54 +35,100 @@
*/
public class OmtpVvmCarrierConfigHelper {
private static final String TAG = "OmtpVvmCarrierConfigHelper";
+ private Context mContext;
+ private int mSubId;
+ private PersistableBundle mCarrierConfig;
+ private String mVvmType;
- public static void startActivation(Context context, int subId) {
- OmtpMessageSender messageSender = getMessageSender(context, subId);
+ public OmtpVvmCarrierConfigHelper(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ mCarrierConfig = getCarrierConfig();
+ mVvmType = getVvmType();
+ }
+
+ public String getVvmType() {
+ if (mCarrierConfig == null) {
+ return null;
+ }
+
+ return mCarrierConfig.getString(
+ CarrierConfigManager.STRING_VVM_TYPE, null);
+ }
+
+ public String getCarrierVvmPackageName() {
+ if (mCarrierConfig == null) {
+ return null;
+ }
+
+ return mCarrierConfig.getString(
+ CarrierConfigManager.STRING_CARRIER_VVM_PACKAGE_NAME, null);
+ }
+
+ public boolean isOmtpVvmType() {
+ return (TelephonyManager.VVM_TYPE_OMTP.equals(mVvmType) ||
+ TelephonyManager.VVM_TYPE_CVVM.equals(mVvmType));
+ }
+
+ /**
+ * For checking upon sim insertion whether visual voicemail should be enabled. This method does
+ * so by checking if the carrier's voicemail app is installed.
+ */
+ public boolean isEnabledByDefault() {
+ String packageName = mCarrierConfig.getString(
+ CarrierConfigManager.STRING_CARRIER_VVM_PACKAGE_NAME);
+ if (packageName == null) {
+ return true;
+ }
+ try {
+ mContext.getPackageManager().getPackageInfo(packageName, 0);
+ return false;
+ } catch (NameNotFoundException e) {
+ return true;
+ }
+ }
+
+ public void startActivation() {
+ OmtpMessageSender messageSender = getMessageSender();
if (messageSender != null) {
- Log.i(TAG, "Requesting VVM activation for subId: " + subId);
+ Log.i(TAG, "Requesting VVM activation for subId: " + mSubId);
messageSender.requestVvmActivation(null);
}
}
- public static void startDeactivation(Context context, int subId) {
- OmtpMessageSender messageSender = getMessageSender(context, subId);
+ public void startDeactivation() {
+ OmtpMessageSender messageSender = getMessageSender();
if (messageSender != null) {
- Log.i(TAG, "Requesting VVM deactivation for subId: " + subId);
+ Log.i(TAG, "Requesting VVM deactivation for subId: " + mSubId);
messageSender.requestVvmDeactivation(null);
}
}
- private static OmtpMessageSender getMessageSender(Context context, int subId) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ private PersistableBundle getCarrierConfig() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
Log.w(TAG, "Invalid subscriptionId or subscriptionId not provided in intent.");
return null;
}
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
- context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (carrierConfigManager == null) {
Log.w(TAG, "No carrier config service found.");
return null;
}
- PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
- if (carrierConfig == null) {
+ return carrierConfigManager.getConfigForSubId(mSubId);
+ }
+
+ private OmtpMessageSender getMessageSender() {
+ if (mCarrierConfig == null) {
Log.w(TAG, "Empty carrier config.");
return null;
}
- String vvmType = carrierConfig.getString(
- CarrierConfigManager.STRING_VVM_TYPE, null);
-
- if (!(TelephonyManager.VVM_TYPE_OMTP.equals(vvmType) ||
- TelephonyManager.VVM_TYPE_CVVM.equals(vvmType))) {
- // This is not an OMTP visual voicemail compatible carrier.
- return null;
- }
-
- int applicationPort = carrierConfig.getInt(
+ int applicationPort = mCarrierConfig.getInt(
CarrierConfigManager.INT_VVM_PORT_NUMBER, 0);
- String destinationNumber = carrierConfig.getString(
+ String destinationNumber = mCarrierConfig.getString(
CarrierConfigManager.STRING_VVM_DESTINATION_NUMBER);
if (TextUtils.isEmpty(destinationNumber)) {
Log.w(TAG, "No destination number for this carrier.");
@@ -89,8 +136,8 @@
}
OmtpMessageSender messageSender = null;
- SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
- switch (vvmType) {
+ SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(mSubId);
+ switch (mVvmType) {
case TelephonyManager.VVM_TYPE_OMTP:
messageSender = new OmtpStandardMessageSender(smsManager, (short) applicationPort,
destinationNumber, null, OmtpConstants.PROTOCOL_VERSION1_1, null);
@@ -100,7 +147,7 @@
destinationNumber);
break;
default:
- Log.w(TAG, "Unexpected visual voicemail type: "+vvmType);
+ Log.w(TAG, "Unexpected visual voicemail type: " + mVvmType);
}
return messageSender;
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 5284f0c..3ac1096 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -18,13 +18,17 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+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;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.phone.PhoneUtils;
+import com.android.phone.settings.VisualVoicemailSettingsUtil;
import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
/**
@@ -57,7 +61,24 @@
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- OmtpVvmCarrierConfigHelper.startActivation(context, subId);
+ OmtpVvmCarrierConfigHelper carrierConfigHelper =
+ new OmtpVvmCarrierConfigHelper(context, subId);
+
+ if (carrierConfigHelper.isOmtpVvmType()) {
+ PhoneAccountHandle phoneAccount = PhoneUtils.makePstnPhoneAccountHandle(
+ SubscriptionManager.getPhoneId(subId));
+
+ if (carrierConfigHelper.isEnabledByDefault()) {
+ VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(
+ context, phoneAccount, true);
+ carrierConfigHelper.startActivation();
+ } else {
+ // 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);
+ carrierConfigHelper.startDeactivation();
+ }
+ }
break;
}
}
diff --git a/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
new file mode 100644
index 0000000..1541f2d
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.vvm.omtp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.phone.PhoneUtils;
+import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
+
+import java.util.Set;
+
+/**
+ * When a new package is installed, check if it matches any of the vvm carrier apps of the currently
+ * enabled dialer vvm sources.
+ */
+public class VvmPackageInstallReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getData() == null) {
+ return;
+ }
+
+ String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+
+ OmtpVvmSourceManager vvmSourceManager = OmtpVvmSourceManager.getInstance(context);
+ Set<PhoneAccountHandle> phoneAccounts = vvmSourceManager.getOmtpVvmSources();
+ for (PhoneAccountHandle phoneAccount : phoneAccounts) {
+ OmtpVvmCarrierConfigHelper carrierConfigHelper = new OmtpVvmCarrierConfigHelper(
+ context, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccount));
+ if (packageName.equals(carrierConfigHelper.getCarrierVvmPackageName())) {
+ OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
+ carrierConfigHelper.startDeactivation();
+ }
+ }
+ }
+}
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index 3a197fc..684e5f5 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -66,14 +66,14 @@
mPhoneAccount = phoneAccount;
TempDirectory.setTempDirectory(context);
- String username = VisualVoicemailSettingsUtil.getCredentialForSource(context,
+ String username = VisualVoicemailSettingsUtil.getVisualVoicemailCredentials(context,
OmtpConstants.IMAP_USER_NAME, phoneAccount);
- String password = VisualVoicemailSettingsUtil.getCredentialForSource(context,
+ String password = VisualVoicemailSettingsUtil.getVisualVoicemailCredentials(context,
OmtpConstants.IMAP_PASSWORD, phoneAccount);
- String serverName = VisualVoicemailSettingsUtil.getCredentialForSource(context,
+ String serverName = VisualVoicemailSettingsUtil.getVisualVoicemailCredentials(context,
OmtpConstants.SERVER_ADDRESS, phoneAccount);
int port = Integer.parseInt(
- VisualVoicemailSettingsUtil.getCredentialForSource(context,
+ VisualVoicemailSettingsUtil.getVisualVoicemailCredentials(context,
OmtpConstants.IMAP_PORT, phoneAccount));
// TODO: determine the security protocol (e.g. ssl, tls, none, etc.)
mImapStore = new ImapStore(
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index 1911f2a..867a14f 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -48,7 +48,7 @@
mPhoneAccount = PhoneUtils.makePstnPhoneAccountHandle(
intent.getExtras().getInt(PhoneConstants.PHONE_KEY));
- if (!VisualVoicemailSettingsUtil.getVisualVoicemailEnabled(mContext, mPhoneAccount)) {
+ if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mContext, mPhoneAccount)) {
Log.v(TAG, "Received vvm message for disabled vvm source.");
return;
}
@@ -69,7 +69,7 @@
processSync(message);
} else if (messageData.getPrefix() == OmtpConstants.STATUS_SMS_PREFIX) {
StatusMessage message = new StatusMessage(messageData);
- updateAccount(message);
+ updateSource(message);
} else {
Log.e(TAG, "This should never have happened");
}
@@ -113,7 +113,7 @@
}
}
- private void updateAccount(StatusMessage message) {
+ private void updateSource(StatusMessage message) {
OmtpVvmSourceManager vvmSourceManager =
OmtpVvmSourceManager.getInstance(mContext);
VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
@@ -123,7 +123,7 @@
// Save the IMAP credentials in the corresponding account object so they are
// persistent and can be retrieved.
- VisualVoicemailSettingsUtil.setSourceCredentialsFromStatusMessage(
+ VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
mContext,
mPhoneAccount,
message);
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
index 9f49c2a..e7a189d 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
@@ -22,6 +22,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import com.android.internal.telephony.Phone;
import com.android.phone.PhoneUtils;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
@@ -30,7 +31,9 @@
import java.util.Set;
/**
- * A singleton class designed to remember the active OMTP visual voicemail sources.
+ * A singleton class designed to remember the active OMTP visual voicemail sources. Because a
+ * voicemail source is tied 1:1 to a phone account, the phone account handle is used as the key
+ * for each voicemail source and the associated data.
*/
public class OmtpVvmSourceManager {
public static final String TAG = "OmtpVvmSourceManager";
@@ -71,22 +74,30 @@
/**
* When a voicemail source is removed, we don't always know which one was removed. Check the
* list of registered phone accounts against the active subscriptions list and remove the
- * inactive accounts.
+ * inactive sources.
*/
public void removeInactiveSources() {
- Set<PhoneAccountHandle> sources = getOmtpVvmSources();
- for (PhoneAccountHandle source : sources) {
- if (!PhoneUtils.isPhoneAccountActive(mSubscriptionManager, source)) {
- VoicemailContract.Status.setStatus(mContext, source,
- VoicemailContract.Status.CONFIGURATION_STATE_NOT_CONFIGURED,
- VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION,
- VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
- removePhoneStateListener(source);
- VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mContext, source, false);
+ Set<PhoneAccountHandle> phoneAccounts = getOmtpVvmSources();
+ for (PhoneAccountHandle phoneAccount : phoneAccounts) {
+ if (!PhoneUtils.isPhoneAccountActive(mSubscriptionManager, phoneAccount)) {
+ removeSource(phoneAccount);
}
}
}
+ public void removeSource(Phone phone) {
+ removeSource(PhoneUtils.makePstnPhoneAccountHandle(phone));
+ }
+
+ public void removeSource(PhoneAccountHandle phoneAccount) {
+ VoicemailContract.Status.setStatus(mContext, phoneAccount,
+ VoicemailContract.Status.CONFIGURATION_STATE_NOT_CONFIGURED,
+ VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION,
+ VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+ removePhoneStateListener(phoneAccount);
+ VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mContext, phoneAccount, false);
+ }
+
public void addPhoneStateListener(PhoneAccountHandle phoneAccount) {
if (!mPhoneStateListenerMap.containsKey(phoneAccount)) {
VvmPhoneStateListener phoneStateListener = new VvmPhoneStateListener(mContext,
@@ -126,4 +137,4 @@
}
return false;
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index b5bd51f..59f5120 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -93,7 +93,7 @@
}
private void doSync(PhoneAccountHandle phoneAccount, String action) {
- if (!VisualVoicemailSettingsUtil.getVisualVoicemailEnabled(this, phoneAccount)) {
+ if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount)) {
Log.v(TAG, "Sync requested for disabled account");
return;
}