535 lines
18 KiB
Java
535 lines
18 KiB
Java
/*
|
|
* 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.annotation.TargetApi;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageManager.NameNotFoundException;
|
|
import android.os.Build.VERSION_CODES;
|
|
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.configui.ConfigOverrideFragment;
|
|
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.Optional;
|
|
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()}
|
|
*
|
|
* <p>TODO(twyen): refactor this to an interface.
|
|
*/
|
|
@TargetApi(VERSION_CODES.O)
|
|
@SuppressWarnings({"missingpermission"})
|
|
public class OmtpVvmCarrierConfigHelper {
|
|
|
|
private static final String TAG = "OmtpVvmCarrierCfgHlpr";
|
|
|
|
public static final String KEY_VVM_TYPE_STRING = CarrierConfigManager.KEY_VVM_TYPE_STRING;
|
|
public static final String KEY_VVM_DESTINATION_NUMBER_STRING =
|
|
CarrierConfigManager.KEY_VVM_DESTINATION_NUMBER_STRING;
|
|
public static final String KEY_VVM_PORT_NUMBER_INT = CarrierConfigManager.KEY_VVM_PORT_NUMBER_INT;
|
|
public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING =
|
|
CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING;
|
|
public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY =
|
|
"carrier_vvm_package_name_string_array";
|
|
public static final String KEY_VVM_PREFETCH_BOOL = CarrierConfigManager.KEY_VVM_PREFETCH_BOOL;
|
|
public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL =
|
|
CarrierConfigManager.KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL;
|
|
|
|
/** @see #getSslPort() */
|
|
public static final String KEY_VVM_SSL_PORT_NUMBER_INT = "vvm_ssl_port_number_int";
|
|
|
|
/** @see #isLegacyModeEnabled() */
|
|
public 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()
|
|
*/
|
|
public static final String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY =
|
|
"vvm_disabled_capabilities_string_array";
|
|
|
|
public static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
|
|
private static final String KEY_IGNORE_TRANSCRIPTION_BOOL = "vvm_ignore_transcription";
|
|
|
|
@Nullable private static PersistableBundle overrideConfigForTest;
|
|
|
|
private final Context context;
|
|
private final PersistableBundle carrierConfig;
|
|
private final String vvmType;
|
|
private final VisualVoicemailProtocol protocol;
|
|
private final PersistableBundle telephonyConfig;
|
|
|
|
@Nullable private final PersistableBundle overrideConfig;
|
|
|
|
private PhoneAccountHandle phoneAccountHandle;
|
|
|
|
public OmtpVvmCarrierConfigHelper(Context context, @Nullable PhoneAccountHandle handle) {
|
|
this.context = context;
|
|
phoneAccountHandle = handle;
|
|
if (overrideConfigForTest != null) {
|
|
overrideConfig = overrideConfigForTest;
|
|
carrierConfig = new PersistableBundle();
|
|
telephonyConfig = new PersistableBundle();
|
|
} else {
|
|
Optional<CarrierIdentifier> carrierIdentifier = CarrierIdentifier.forHandle(context, handle);
|
|
TelephonyManager telephonyManager =
|
|
context
|
|
.getSystemService(TelephonyManager.class)
|
|
.createForPhoneAccountHandle(phoneAccountHandle);
|
|
if (telephonyManager == null || !carrierIdentifier.isPresent()) {
|
|
VvmLog.e(TAG, "PhoneAccountHandle is invalid");
|
|
carrierConfig = null;
|
|
telephonyConfig = null;
|
|
overrideConfig = null;
|
|
vvmType = null;
|
|
protocol = null;
|
|
return;
|
|
}
|
|
if (ConfigOverrideFragment.isOverridden(context)) {
|
|
overrideConfig = ConfigOverrideFragment.getConfig(context);
|
|
VvmLog.w(TAG, "Config override is activated: " + overrideConfig);
|
|
} else {
|
|
overrideConfig = null;
|
|
}
|
|
|
|
carrierConfig = getCarrierConfig(telephonyManager);
|
|
telephonyConfig = new DialerVvmConfigManager(context).getConfig(carrierIdentifier.get());
|
|
}
|
|
|
|
vvmType = getVvmType();
|
|
protocol = VisualVoicemailProtocolFactory.create(this.context.getResources(), vvmType);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
OmtpVvmCarrierConfigHelper(
|
|
Context context,
|
|
PersistableBundle carrierConfig,
|
|
PersistableBundle telephonyConfig,
|
|
@Nullable PhoneAccountHandle phoneAccountHandle) {
|
|
this.context = context;
|
|
this.carrierConfig = carrierConfig;
|
|
this.telephonyConfig = telephonyConfig;
|
|
this.phoneAccountHandle = phoneAccountHandle;
|
|
overrideConfig = null;
|
|
vvmType = getVvmType();
|
|
protocol = VisualVoicemailProtocolFactory.create(this.context.getResources(), vvmType);
|
|
}
|
|
|
|
public PersistableBundle getConfig() {
|
|
PersistableBundle result = new PersistableBundle();
|
|
if (telephonyConfig != null) {
|
|
result.putAll(telephonyConfig);
|
|
}
|
|
if (carrierConfig != null) {
|
|
result.putAll(carrierConfig);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public Context getContext() {
|
|
return context;
|
|
}
|
|
|
|
@Nullable
|
|
public PhoneAccountHandle getPhoneAccountHandle() {
|
|
return phoneAccountHandle;
|
|
}
|
|
|
|
/**
|
|
* return whether the carrier's visual voicemail is supported, with KEY_VVM_TYPE_STRING set as a
|
|
* known protocol.
|
|
*/
|
|
public boolean isValid() {
|
|
if (protocol == null) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Nullable
|
|
public String getVvmType() {
|
|
return (String) getValue(KEY_VVM_TYPE_STRING);
|
|
}
|
|
|
|
@Nullable
|
|
public VisualVoicemailProtocol getProtocol() {
|
|
return protocol;
|
|
}
|
|
|
|
/** @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
|
|
private Set<String> getCarrierVvmPackageNamesWithoutValidation() {
|
|
Set<String> names = getCarrierVvmPackageNames(overrideConfig);
|
|
if (names != null) {
|
|
return names;
|
|
}
|
|
names = getCarrierVvmPackageNames(carrierConfig);
|
|
if (names != null) {
|
|
return names;
|
|
}
|
|
return getCarrierVvmPackageNames(telephonyConfig);
|
|
}
|
|
|
|
@Nullable
|
|
public Set<String> getCarrierVvmPackageNames() {
|
|
Assert.checkArgument(isValid());
|
|
return getCarrierVvmPackageNamesWithoutValidation();
|
|
}
|
|
|
|
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;
|
|
}
|
|
return !isCarrierAppInstalled();
|
|
}
|
|
|
|
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 a bug 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;
|
|
disabledCapabilities = getDisabledCapabilities(overrideConfig);
|
|
if (disabledCapabilities != null) {
|
|
return disabledCapabilities;
|
|
}
|
|
disabledCapabilities = getDisabledCapabilities(carrierConfig);
|
|
if (disabledCapabilities != null) {
|
|
return disabledCapabilities;
|
|
}
|
|
return getDisabledCapabilities(telephonyConfig);
|
|
}
|
|
|
|
@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() {
|
|
PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle();
|
|
if (phoneAccountHandle == null) {
|
|
// This should never happen
|
|
// Error logged in getPhoneAccountHandle().
|
|
return;
|
|
}
|
|
|
|
if (!isValid()) {
|
|
VvmLog.e(TAG, "startActivation : invalid config for account " + phoneAccountHandle);
|
|
return;
|
|
}
|
|
|
|
ActivationTask.start(context, this.phoneAccountHandle, null);
|
|
}
|
|
|
|
public void activateSmsFilter() {
|
|
Assert.checkArgument(isValid());
|
|
context
|
|
.getSystemService(TelephonyManager.class)
|
|
.createForPhoneAccountHandle(getPhoneAccountHandle())
|
|
.setVisualVoicemailSmsFilterSettings(
|
|
new VisualVoicemailSmsFilterSettings.Builder()
|
|
.setClientPrefix(getClientPrefix())
|
|
.build());
|
|
}
|
|
|
|
public void startDeactivation() {
|
|
VvmLog.i(TAG, "startDeactivation");
|
|
if (isValid()) {
|
|
if (!isLegacyModeEnabled()) {
|
|
// SMS should still be filtered in legacy mode
|
|
context
|
|
.getSystemService(TelephonyManager.class)
|
|
.createForPhoneAccountHandle(getPhoneAccountHandle())
|
|
.setVisualVoicemailSmsFilterSettings(null);
|
|
VvmLog.i(TAG, "filter disabled");
|
|
}
|
|
protocol.startDeactivation(this);
|
|
}
|
|
VvmAccountManager.removeAccount(context, getPhoneAccountHandle());
|
|
}
|
|
|
|
public boolean supportsProvisioning() {
|
|
Assert.checkArgument(isValid());
|
|
return protocol.supportsProvisioning();
|
|
}
|
|
|
|
public void startProvisioning(
|
|
ActivationTask task,
|
|
PhoneAccountHandle phone,
|
|
VoicemailStatus.Editor status,
|
|
StatusMessage message,
|
|
Bundle data,
|
|
boolean isCarrierInitiated) {
|
|
Assert.checkArgument(isValid());
|
|
protocol.startProvisioning(task, phone, this, status, message, data, isCarrierInitiated);
|
|
}
|
|
|
|
public void requestStatus(@Nullable PendingIntent sentIntent) {
|
|
Assert.checkArgument(isValid());
|
|
protocol.requestStatus(this, sentIntent);
|
|
}
|
|
|
|
public void handleEvent(VoicemailStatus.Editor status, OmtpEvents event) {
|
|
Assert.checkArgument(isValid());
|
|
VvmLog.i(TAG, "OmtpEvent:" + event);
|
|
protocol.handleEvent(context, this, status, event);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuilder builder = new StringBuilder("OmtpVvmCarrierConfigHelper [");
|
|
builder
|
|
.append("phoneAccountHandle: ")
|
|
.append(phoneAccountHandle)
|
|
.append(", carrierConfig: ")
|
|
.append(carrierConfig != null)
|
|
.append(", telephonyConfig: ")
|
|
.append(telephonyConfig != 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) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
|
|
if (carrierConfigManager == null) {
|
|
VvmLog.w(TAG, "No carrier config service found.");
|
|
return null;
|
|
}
|
|
|
|
PersistableBundle config = telephonyManager.getCarrierConfig();
|
|
if (config == null) {
|
|
return null;
|
|
}
|
|
|
|
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 (overrideConfig != null) {
|
|
result = overrideConfig.get(key);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (carrierConfig != null) {
|
|
result = carrierConfig.get(key);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
if (telephonyConfig != null) {
|
|
result = telephonyConfig.get(key);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public static void setOverrideConfigForTest(PersistableBundle config) {
|
|
overrideConfigForTest = config;
|
|
}
|
|
|
|
/** Checks if the carrier VVM app is installed. */
|
|
public boolean isCarrierAppInstalled() {
|
|
Set<String> carrierPackages = getCarrierVvmPackageNamesWithoutValidation();
|
|
if (carrierPackages == null) {
|
|
return false;
|
|
}
|
|
for (String packageName : carrierPackages) {
|
|
try {
|
|
ApplicationInfo info = getContext().getPackageManager().getApplicationInfo(packageName, 0);
|
|
if (!info.enabled) {
|
|
continue;
|
|
}
|
|
return true;
|
|
} catch (NameNotFoundException e) {
|
|
continue;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Suppress the behavior of treating any text attachment with MIME "text/*" as transcription,
|
|
* default to false.
|
|
*/
|
|
public boolean ignoreTranscription() {
|
|
Assert.checkArgument(isValid());
|
|
return (boolean) getValue(KEY_IGNORE_TRANSCRIPTION_BOOL, false);
|
|
}
|
|
}
|