| /* |
| * 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.voicemail.impl; |
| |
| import android.app.PendingIntent; |
| import android.content.Context; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.os.Bundle; |
| import android.os.PersistableBundle; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.Nullable; |
| import android.support.annotation.VisibleForTesting; |
| import android.telecom.PhoneAccountHandle; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.TelephonyManager; |
| import android.telephony.VisualVoicemailSmsFilterSettings; |
| import android.text.TextUtils; |
| import android.util.ArraySet; |
| import com.android.dialer.common.Assert; |
| import com.android.voicemail.impl.protocol.VisualVoicemailProtocol; |
| import com.android.voicemail.impl.protocol.VisualVoicemailProtocolFactory; |
| import com.android.voicemail.impl.sms.StatusMessage; |
| import com.android.voicemail.impl.sync.VvmAccountManager; |
| import java.util.Collections; |
| import java.util.Set; |
| |
| /** |
| * Manages carrier dependent visual voicemail configuration values. The primary source is the value |
| * retrieved from CarrierConfigManager. If CarrierConfigManager does not provide the config |
| * (KEY_VVM_TYPE_STRING is empty, or "hidden" configs), then the value hardcoded in telephony will |
| * be used (in res/xml/vvm_config.xml) |
| * |
| * <p>Hidden configs are new configs that are planned for future APIs, or miscellaneous settings |
| * that may clutter CarrierConfigManager too much. |
| * |
| * <p>The current hidden configs are: {@link #getSslPort()} {@link #getDisabledCapabilities()} |
| */ |
| public class OmtpVvmCarrierConfigHelper { |
| |
| private static final String TAG = "OmtpVvmCarrierCfgHlpr"; |
| |
| static final String KEY_VVM_TYPE_STRING = CarrierConfigManager.KEY_VVM_TYPE_STRING; |
| static final String KEY_VVM_DESTINATION_NUMBER_STRING = |
| CarrierConfigManager.KEY_VVM_DESTINATION_NUMBER_STRING; |
| static final String KEY_VVM_PORT_NUMBER_INT = CarrierConfigManager.KEY_VVM_PORT_NUMBER_INT; |
| static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = |
| CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING; |
| static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY = |
| "carrier_vvm_package_name_string_array"; |
| static final String KEY_VVM_PREFETCH_BOOL = CarrierConfigManager.KEY_VVM_PREFETCH_BOOL; |
| static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = |
| CarrierConfigManager.KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL; |
| |
| /** @see #getSslPort() */ |
| static final String KEY_VVM_SSL_PORT_NUMBER_INT = "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. |
| * |
| * @see #getDisabledCapabilities() |
| */ |
| static final String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY = |
| "vvm_disabled_capabilities_string_array"; |
| |
| static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string"; |
| |
| private final Context mContext; |
| private final PersistableBundle mCarrierConfig; |
| private final String mVvmType; |
| private final VisualVoicemailProtocol mProtocol; |
| private final PersistableBundle mTelephonyConfig; |
| |
| private PhoneAccountHandle mPhoneAccountHandle; |
| |
| public OmtpVvmCarrierConfigHelper(Context context, @Nullable PhoneAccountHandle handle) { |
| mContext = context; |
| mPhoneAccountHandle = handle; |
| TelephonyManager telephonyManager = |
| context |
| .getSystemService(TelephonyManager.class) |
| .createForPhoneAccountHandle(mPhoneAccountHandle); |
| if (telephonyManager == null) { |
| VvmLog.e(TAG, "PhoneAccountHandle is invalid"); |
| mCarrierConfig = null; |
| mTelephonyConfig = null; |
| mVvmType = null; |
| mProtocol = null; |
| return; |
| } |
| |
| mCarrierConfig = getCarrierConfig(telephonyManager); |
| mTelephonyConfig = |
| new TelephonyVvmConfigManager(context).getConfig(telephonyManager.getSimOperator()); |
| |
| mVvmType = getVvmType(); |
| mProtocol = VisualVoicemailProtocolFactory.create(mContext.getResources(), mVvmType); |
| } |
| |
| @VisibleForTesting |
| OmtpVvmCarrierConfigHelper( |
| Context context, PersistableBundle carrierConfig, PersistableBundle telephonyConfig) { |
| mContext = context; |
| mCarrierConfig = carrierConfig; |
| mTelephonyConfig = telephonyConfig; |
| mVvmType = getVvmType(); |
| mProtocol = VisualVoicemailProtocolFactory.create(mContext.getResources(), mVvmType); |
| } |
| |
| public Context getContext() { |
| return mContext; |
| } |
| |
| @Nullable |
| public PhoneAccountHandle getPhoneAccountHandle() { |
| return mPhoneAccountHandle; |
| } |
| |
| /** |
| * return whether the carrier's visual voicemail is supported, with KEY_VVM_TYPE_STRING set as a |
| * known protocol. |
| */ |
| public boolean isValid() { |
| return mProtocol != null; |
| } |
| |
| @Nullable |
| public String getVvmType() { |
| return (String) getValue(KEY_VVM_TYPE_STRING); |
| } |
| |
| @Nullable |
| public VisualVoicemailProtocol getProtocol() { |
| return mProtocol; |
| } |
| |
| /** @returns arbitrary String stored in the config file. Used for protocol specific values. */ |
| @Nullable |
| public String getString(String key) { |
| Assert.checkArgument(isValid()); |
| return (String) getValue(key); |
| } |
| |
| @Nullable |
| public Set<String> getCarrierVvmPackageNames() { |
| Assert.checkArgument(isValid()); |
| Set<String> names = getCarrierVvmPackageNames(mCarrierConfig); |
| if (names != null) { |
| return names; |
| } |
| return getCarrierVvmPackageNames(mTelephonyConfig); |
| } |
| |
| private static Set<String> getCarrierVvmPackageNames(@Nullable PersistableBundle bundle) { |
| if (bundle == null) { |
| return null; |
| } |
| Set<String> names = new ArraySet<>(); |
| if (bundle.containsKey(KEY_CARRIER_VVM_PACKAGE_NAME_STRING)) { |
| names.add(bundle.getString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING)); |
| } |
| if (bundle.containsKey(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY)) { |
| String[] vvmPackages = bundle.getStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY); |
| if (vvmPackages != null && vvmPackages.length > 0) { |
| Collections.addAll(names, vvmPackages); |
| } |
| } |
| if (names.isEmpty()) { |
| return null; |
| } |
| return names; |
| } |
| |
| /** |
| * 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() { |
| if (!isValid()) { |
| return false; |
| } |
| |
| Set<String> carrierPackages = getCarrierVvmPackageNames(); |
| if (carrierPackages == null) { |
| return true; |
| } |
| for (String packageName : carrierPackages) { |
| try { |
| mContext.getPackageManager().getPackageInfo(packageName, 0); |
| return false; |
| } catch (NameNotFoundException e) { |
| // Do nothing. |
| } |
| } |
| return true; |
| } |
| |
| public boolean isCellularDataRequired() { |
| Assert.checkArgument(isValid()); |
| return (boolean) getValue(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false); |
| } |
| |
| public boolean isPrefetchEnabled() { |
| Assert.checkArgument(isValid()); |
| return (boolean) getValue(KEY_VVM_PREFETCH_BOOL, true); |
| } |
| |
| public int getApplicationPort() { |
| Assert.checkArgument(isValid()); |
| return (int) getValue(KEY_VVM_PORT_NUMBER_INT, 0); |
| } |
| |
| @Nullable |
| public String getDestinationNumber() { |
| Assert.checkArgument(isValid()); |
| return (String) getValue(KEY_VVM_DESTINATION_NUMBER_STRING); |
| } |
| |
| /** @return Port to start a SSL IMAP connection directly. */ |
| public int getSslPort() { |
| Assert.checkArgument(isValid()); |
| return (int) getValue(KEY_VVM_SSL_PORT_NUMBER_INT, 0); |
| } |
| |
| /** |
| * Hidden Config. |
| * |
| * <p>Sometimes the server states it supports a certain feature but we found they have bug on the |
| * server side. For example, in b/28717550 the server reported AUTH=DIGEST-MD5 capability but |
| * using it to login will cause subsequent response to be erroneous. |
| * |
| * @return A set of capabilities that is reported by the IMAP CAPABILITY command, but determined |
| * to have issues and should not be used. |
| */ |
| @Nullable |
| public Set<String> getDisabledCapabilities() { |
| Assert.checkArgument(isValid()); |
| Set<String> disabledCapabilities = getDisabledCapabilities(mCarrierConfig); |
| if (disabledCapabilities != null) { |
| return disabledCapabilities; |
| } |
| return getDisabledCapabilities(mTelephonyConfig); |
| } |
| |
| @Nullable |
| private static Set<String> getDisabledCapabilities(@Nullable PersistableBundle bundle) { |
| if (bundle == null) { |
| return null; |
| } |
| if (!bundle.containsKey(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY)) { |
| return null; |
| } |
| String[] disabledCapabilities = |
| bundle.getStringArray(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY); |
| if (disabledCapabilities != null && disabledCapabilities.length > 0) { |
| ArraySet<String> result = new ArraySet<>(); |
| Collections.addAll(result, disabledCapabilities); |
| return result; |
| } |
| return null; |
| } |
| |
| public String getClientPrefix() { |
| Assert.checkArgument(isValid()); |
| String prefix = (String) getValue(KEY_VVM_CLIENT_PREFIX_STRING); |
| if (prefix != null) { |
| return prefix; |
| } |
| 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() { |
| Assert.checkArgument(isValid()); |
| return (boolean) getValue(KEY_VVM_LEGACY_MODE_ENABLED_BOOL, false); |
| } |
| |
| public void startActivation() { |
| Assert.checkArgument(isValid()); |
| PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle(); |
| if (phoneAccountHandle == null) { |
| // This should never happen |
| // Error logged in getPhoneAccountHandle(). |
| return; |
| } |
| |
| if (mVvmType == null || mVvmType.isEmpty()) { |
| // The VVM type is invalid; we should never have gotten here in the first place since |
| // this is loaded initially in the constructor, and callers should check isValid() |
| // before trying to start activation anyways. |
| VvmLog.e(TAG, "startActivation : vvmType is null or empty for account " + phoneAccountHandle); |
| return; |
| } |
| |
| if (mProtocol != null) { |
| ActivationTask.start(mContext, mPhoneAccountHandle, null); |
| } |
| } |
| |
| public void activateSmsFilter() { |
| Assert.checkArgument(isValid()); |
| TelephonyMangerCompat.setVisualVoicemailSmsFilterSettings( |
| mContext, |
| getPhoneAccountHandle(), |
| new VisualVoicemailSmsFilterSettings.Builder().setClientPrefix(getClientPrefix()).build()); |
| } |
| |
| public void startDeactivation() { |
| Assert.checkArgument(isValid()); |
| VvmLog.i(TAG, "startDeactivation"); |
| if (!isLegacyModeEnabled()) { |
| // SMS should still be filtered in legacy mode |
| TelephonyMangerCompat.setVisualVoicemailSmsFilterSettings( |
| mContext, getPhoneAccountHandle(), null); |
| VvmLog.i(TAG, "filter disabled"); |
| } |
| if (mProtocol != null) { |
| mProtocol.startDeactivation(this); |
| } |
| VvmAccountManager.removeAccount(mContext, getPhoneAccountHandle()); |
| } |
| |
| public boolean supportsProvisioning() { |
| Assert.checkArgument(isValid()); |
| return mProtocol.supportsProvisioning(); |
| } |
| |
| public void startProvisioning( |
| ActivationTask task, |
| PhoneAccountHandle phone, |
| VoicemailStatus.Editor status, |
| StatusMessage message, |
| Bundle data) { |
| Assert.checkArgument(isValid()); |
| mProtocol.startProvisioning(task, phone, this, status, message, data); |
| } |
| |
| public void requestStatus(@Nullable PendingIntent sentIntent) { |
| Assert.checkArgument(isValid()); |
| mProtocol.requestStatus(this, sentIntent); |
| } |
| |
| public void handleEvent(VoicemailStatus.Editor status, OmtpEvents event) { |
| Assert.checkArgument(isValid()); |
| VvmLog.i(TAG, "OmtpEvent:" + event); |
| mProtocol.handleEvent(mContext, this, status, event); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder("OmtpVvmCarrierConfigHelper ["); |
| builder |
| .append("phoneAccountHandle: ") |
| .append(mPhoneAccountHandle) |
| .append(", carrierConfig: ") |
| .append(mCarrierConfig != null) |
| .append(", telephonyConfig: ") |
| .append(mTelephonyConfig != null) |
| .append(", type: ") |
| .append(getVvmType()) |
| .append(", destinationNumber: ") |
| .append(getDestinationNumber()) |
| .append(", applicationPort: ") |
| .append(getApplicationPort()) |
| .append(", sslPort: ") |
| .append(getSslPort()) |
| .append(", isEnabledByDefault: ") |
| .append(isEnabledByDefault()) |
| .append(", isCellularDataRequired: ") |
| .append(isCellularDataRequired()) |
| .append(", isPrefetchEnabled: ") |
| .append(isPrefetchEnabled()) |
| .append(", isLegacyModeEnabled: ") |
| .append(isLegacyModeEnabled()) |
| .append("]"); |
| return builder.toString(); |
| } |
| |
| @Nullable |
| private PersistableBundle getCarrierConfig(@NonNull TelephonyManager telephonyManager) { |
| CarrierConfigManager carrierConfigManager = |
| (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| if (carrierConfigManager == null) { |
| VvmLog.w(TAG, "No carrier config service found."); |
| return null; |
| } |
| |
| PersistableBundle config = telephonyManager.getCarrierConfig(); |
| |
| if (TextUtils.isEmpty(config.getString(CarrierConfigManager.KEY_VVM_TYPE_STRING))) { |
| return null; |
| } |
| return config; |
| } |
| |
| @Nullable |
| private Object getValue(String key) { |
| return getValue(key, null); |
| } |
| |
| @Nullable |
| private Object getValue(String key, Object defaultValue) { |
| Object result; |
| if (mCarrierConfig != null) { |
| result = mCarrierConfig.get(key); |
| if (result != null) { |
| return result; |
| } |
| } |
| if (mTelephonyConfig != null) { |
| result = mTelephonyConfig.get(key); |
| if (result != null) { |
| return result; |
| } |
| } |
| return defaultValue; |
| } |
| } |