• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 package com.android.voicemail.impl;
17 
18 import android.app.PendingIntent;
19 import android.content.Context;
20 import android.content.pm.PackageManager.NameNotFoundException;
21 import android.os.Bundle;
22 import android.os.PersistableBundle;
23 import android.support.annotation.NonNull;
24 import android.support.annotation.Nullable;
25 import android.support.annotation.VisibleForTesting;
26 import android.telecom.PhoneAccountHandle;
27 import android.telephony.CarrierConfigManager;
28 import android.telephony.TelephonyManager;
29 import android.telephony.VisualVoicemailSmsFilterSettings;
30 import android.text.TextUtils;
31 import android.util.ArraySet;
32 import com.android.dialer.common.Assert;
33 import com.android.voicemail.impl.protocol.VisualVoicemailProtocol;
34 import com.android.voicemail.impl.protocol.VisualVoicemailProtocolFactory;
35 import com.android.voicemail.impl.sms.StatusMessage;
36 import com.android.voicemail.impl.sync.VvmAccountManager;
37 import java.util.Collections;
38 import java.util.Set;
39 
40 /**
41  * Manages carrier dependent visual voicemail configuration values. The primary source is the value
42  * retrieved from CarrierConfigManager. If CarrierConfigManager does not provide the config
43  * (KEY_VVM_TYPE_STRING is empty, or "hidden" configs), then the value hardcoded in telephony will
44  * be used (in res/xml/vvm_config.xml)
45  *
46  * <p>Hidden configs are new configs that are planned for future APIs, or miscellaneous settings
47  * that may clutter CarrierConfigManager too much.
48  *
49  * <p>The current hidden configs are: {@link #getSslPort()} {@link #getDisabledCapabilities()}
50  */
51 public class OmtpVvmCarrierConfigHelper {
52 
53   private static final String TAG = "OmtpVvmCarrierCfgHlpr";
54 
55   static final String KEY_VVM_TYPE_STRING = CarrierConfigManager.KEY_VVM_TYPE_STRING;
56   static final String KEY_VVM_DESTINATION_NUMBER_STRING =
57       CarrierConfigManager.KEY_VVM_DESTINATION_NUMBER_STRING;
58   static final String KEY_VVM_PORT_NUMBER_INT = CarrierConfigManager.KEY_VVM_PORT_NUMBER_INT;
59   static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING =
60       CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING;
61   static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY =
62       "carrier_vvm_package_name_string_array";
63   static final String KEY_VVM_PREFETCH_BOOL = CarrierConfigManager.KEY_VVM_PREFETCH_BOOL;
64   static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL =
65       CarrierConfigManager.KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL;
66 
67   /** @see #getSslPort() */
68   static final String KEY_VVM_SSL_PORT_NUMBER_INT = "vvm_ssl_port_number_int";
69 
70   /** @see #isLegacyModeEnabled() */
71   static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL = "vvm_legacy_mode_enabled_bool";
72 
73   /**
74    * Ban a capability reported by the server from being used. The array of string should be a subset
75    * of the capabilities returned IMAP CAPABILITY command.
76    *
77    * @see #getDisabledCapabilities()
78    */
79   static final String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY =
80       "vvm_disabled_capabilities_string_array";
81 
82   static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
83 
84   private final Context mContext;
85   private final PersistableBundle mCarrierConfig;
86   private final String mVvmType;
87   private final VisualVoicemailProtocol mProtocol;
88   private final PersistableBundle mTelephonyConfig;
89 
90   private PhoneAccountHandle mPhoneAccountHandle;
91 
OmtpVvmCarrierConfigHelper(Context context, @Nullable PhoneAccountHandle handle)92   public OmtpVvmCarrierConfigHelper(Context context, @Nullable PhoneAccountHandle handle) {
93     mContext = context;
94     mPhoneAccountHandle = handle;
95     TelephonyManager telephonyManager =
96         context
97             .getSystemService(TelephonyManager.class)
98             .createForPhoneAccountHandle(mPhoneAccountHandle);
99     if (telephonyManager == null) {
100       VvmLog.e(TAG, "PhoneAccountHandle is invalid");
101       mCarrierConfig = null;
102       mTelephonyConfig = null;
103       mVvmType = null;
104       mProtocol = null;
105       return;
106     }
107 
108     mCarrierConfig = getCarrierConfig(telephonyManager);
109     mTelephonyConfig =
110         new TelephonyVvmConfigManager(context).getConfig(telephonyManager.getSimOperator());
111 
112     mVvmType = getVvmType();
113     mProtocol = VisualVoicemailProtocolFactory.create(mContext.getResources(), mVvmType);
114   }
115 
116   @VisibleForTesting
OmtpVvmCarrierConfigHelper( Context context, PersistableBundle carrierConfig, PersistableBundle telephonyConfig)117   OmtpVvmCarrierConfigHelper(
118       Context context, PersistableBundle carrierConfig, PersistableBundle telephonyConfig) {
119     mContext = context;
120     mCarrierConfig = carrierConfig;
121     mTelephonyConfig = telephonyConfig;
122     mVvmType = getVvmType();
123     mProtocol = VisualVoicemailProtocolFactory.create(mContext.getResources(), mVvmType);
124   }
125 
getContext()126   public Context getContext() {
127     return mContext;
128   }
129 
130   @Nullable
getPhoneAccountHandle()131   public PhoneAccountHandle getPhoneAccountHandle() {
132     return mPhoneAccountHandle;
133   }
134 
135   /**
136    * return whether the carrier's visual voicemail is supported, with KEY_VVM_TYPE_STRING set as a
137    * known protocol.
138    */
isValid()139   public boolean isValid() {
140     return mProtocol != null;
141   }
142 
143   @Nullable
getVvmType()144   public String getVvmType() {
145     return (String) getValue(KEY_VVM_TYPE_STRING);
146   }
147 
148   @Nullable
getProtocol()149   public VisualVoicemailProtocol getProtocol() {
150     return mProtocol;
151   }
152 
153   /** @returns arbitrary String stored in the config file. Used for protocol specific values. */
154   @Nullable
getString(String key)155   public String getString(String key) {
156     Assert.checkArgument(isValid());
157     return (String) getValue(key);
158   }
159 
160   @Nullable
getCarrierVvmPackageNames()161   public Set<String> getCarrierVvmPackageNames() {
162     Assert.checkArgument(isValid());
163     Set<String> names = getCarrierVvmPackageNames(mCarrierConfig);
164     if (names != null) {
165       return names;
166     }
167     return getCarrierVvmPackageNames(mTelephonyConfig);
168   }
169 
getCarrierVvmPackageNames(@ullable PersistableBundle bundle)170   private static Set<String> getCarrierVvmPackageNames(@Nullable PersistableBundle bundle) {
171     if (bundle == null) {
172       return null;
173     }
174     Set<String> names = new ArraySet<>();
175     if (bundle.containsKey(KEY_CARRIER_VVM_PACKAGE_NAME_STRING)) {
176       names.add(bundle.getString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING));
177     }
178     if (bundle.containsKey(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY)) {
179       String[] vvmPackages = bundle.getStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY);
180       if (vvmPackages != null && vvmPackages.length > 0) {
181         Collections.addAll(names, vvmPackages);
182       }
183     }
184     if (names.isEmpty()) {
185       return null;
186     }
187     return names;
188   }
189 
190   /**
191    * For checking upon sim insertion whether visual voicemail should be enabled. This method does so
192    * by checking if the carrier's voicemail app is installed.
193    */
isEnabledByDefault()194   public boolean isEnabledByDefault() {
195     if (!isValid()) {
196       return false;
197     }
198 
199     Set<String> carrierPackages = getCarrierVvmPackageNames();
200     if (carrierPackages == null) {
201       return true;
202     }
203     for (String packageName : carrierPackages) {
204       try {
205         mContext.getPackageManager().getPackageInfo(packageName, 0);
206         return false;
207       } catch (NameNotFoundException e) {
208         // Do nothing.
209       }
210     }
211     return true;
212   }
213 
isCellularDataRequired()214   public boolean isCellularDataRequired() {
215     Assert.checkArgument(isValid());
216     return (boolean) getValue(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false);
217   }
218 
isPrefetchEnabled()219   public boolean isPrefetchEnabled() {
220     Assert.checkArgument(isValid());
221     return (boolean) getValue(KEY_VVM_PREFETCH_BOOL, true);
222   }
223 
getApplicationPort()224   public int getApplicationPort() {
225     Assert.checkArgument(isValid());
226     return (int) getValue(KEY_VVM_PORT_NUMBER_INT, 0);
227   }
228 
229   @Nullable
getDestinationNumber()230   public String getDestinationNumber() {
231     Assert.checkArgument(isValid());
232     return (String) getValue(KEY_VVM_DESTINATION_NUMBER_STRING);
233   }
234 
235   /** @return Port to start a SSL IMAP connection directly. */
getSslPort()236   public int getSslPort() {
237     Assert.checkArgument(isValid());
238     return (int) getValue(KEY_VVM_SSL_PORT_NUMBER_INT, 0);
239   }
240 
241   /**
242    * Hidden Config.
243    *
244    * <p>Sometimes the server states it supports a certain feature but we found they have bug on the
245    * server side. For example, in b/28717550 the server reported AUTH=DIGEST-MD5 capability but
246    * using it to login will cause subsequent response to be erroneous.
247    *
248    * @return A set of capabilities that is reported by the IMAP CAPABILITY command, but determined
249    *     to have issues and should not be used.
250    */
251   @Nullable
getDisabledCapabilities()252   public Set<String> getDisabledCapabilities() {
253     Assert.checkArgument(isValid());
254     Set<String> disabledCapabilities = getDisabledCapabilities(mCarrierConfig);
255     if (disabledCapabilities != null) {
256       return disabledCapabilities;
257     }
258     return getDisabledCapabilities(mTelephonyConfig);
259   }
260 
261   @Nullable
getDisabledCapabilities(@ullable PersistableBundle bundle)262   private static Set<String> getDisabledCapabilities(@Nullable PersistableBundle bundle) {
263     if (bundle == null) {
264       return null;
265     }
266     if (!bundle.containsKey(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY)) {
267       return null;
268     }
269     String[] disabledCapabilities =
270         bundle.getStringArray(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY);
271     if (disabledCapabilities != null && disabledCapabilities.length > 0) {
272       ArraySet<String> result = new ArraySet<>();
273       Collections.addAll(result, disabledCapabilities);
274       return result;
275     }
276     return null;
277   }
278 
getClientPrefix()279   public String getClientPrefix() {
280     Assert.checkArgument(isValid());
281     String prefix = (String) getValue(KEY_VVM_CLIENT_PREFIX_STRING);
282     if (prefix != null) {
283       return prefix;
284     }
285     return "//VVM";
286   }
287 
288   /**
289    * Should legacy mode be used when the OMTP VVM client is disabled?
290    *
291    * <p>Legacy mode is a mode that on the carrier side visual voicemail is still activated, but on
292    * the client side all network operations are disabled. SMSs are still monitored so a new message
293    * SYNC SMS will be translated to show a message waiting indicator, like traditional voicemails.
294    *
295    * <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
296    * function without the data cost.
297    */
isLegacyModeEnabled()298   public boolean isLegacyModeEnabled() {
299     Assert.checkArgument(isValid());
300     return (boolean) getValue(KEY_VVM_LEGACY_MODE_ENABLED_BOOL, false);
301   }
302 
startActivation()303   public void startActivation() {
304     Assert.checkArgument(isValid());
305     PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle();
306     if (phoneAccountHandle == null) {
307       // This should never happen
308       // Error logged in getPhoneAccountHandle().
309       return;
310     }
311 
312     if (mVvmType == null || mVvmType.isEmpty()) {
313       // The VVM type is invalid; we should never have gotten here in the first place since
314       // this is loaded initially in the constructor, and callers should check isValid()
315       // before trying to start activation anyways.
316       VvmLog.e(TAG, "startActivation : vvmType is null or empty for account " + phoneAccountHandle);
317       return;
318     }
319 
320     if (mProtocol != null) {
321       ActivationTask.start(mContext, mPhoneAccountHandle, null);
322     }
323   }
324 
activateSmsFilter()325   public void activateSmsFilter() {
326     Assert.checkArgument(isValid());
327     TelephonyMangerCompat.setVisualVoicemailSmsFilterSettings(
328         mContext,
329         getPhoneAccountHandle(),
330         new VisualVoicemailSmsFilterSettings.Builder().setClientPrefix(getClientPrefix()).build());
331   }
332 
startDeactivation()333   public void startDeactivation() {
334     Assert.checkArgument(isValid());
335     VvmLog.i(TAG, "startDeactivation");
336     if (!isLegacyModeEnabled()) {
337       // SMS should still be filtered in legacy mode
338       TelephonyMangerCompat.setVisualVoicemailSmsFilterSettings(
339           mContext, getPhoneAccountHandle(), null);
340       VvmLog.i(TAG, "filter disabled");
341     }
342     if (mProtocol != null) {
343       mProtocol.startDeactivation(this);
344     }
345     VvmAccountManager.removeAccount(mContext, getPhoneAccountHandle());
346   }
347 
supportsProvisioning()348   public boolean supportsProvisioning() {
349     Assert.checkArgument(isValid());
350     return mProtocol.supportsProvisioning();
351   }
352 
startProvisioning( ActivationTask task, PhoneAccountHandle phone, VoicemailStatus.Editor status, StatusMessage message, Bundle data)353   public void startProvisioning(
354       ActivationTask task,
355       PhoneAccountHandle phone,
356       VoicemailStatus.Editor status,
357       StatusMessage message,
358       Bundle data) {
359     Assert.checkArgument(isValid());
360     mProtocol.startProvisioning(task, phone, this, status, message, data);
361   }
362 
requestStatus(@ullable PendingIntent sentIntent)363   public void requestStatus(@Nullable PendingIntent sentIntent) {
364     Assert.checkArgument(isValid());
365     mProtocol.requestStatus(this, sentIntent);
366   }
367 
handleEvent(VoicemailStatus.Editor status, OmtpEvents event)368   public void handleEvent(VoicemailStatus.Editor status, OmtpEvents event) {
369     Assert.checkArgument(isValid());
370     VvmLog.i(TAG, "OmtpEvent:" + event);
371     mProtocol.handleEvent(mContext, this, status, event);
372   }
373 
374   @Override
toString()375   public String toString() {
376     StringBuilder builder = new StringBuilder("OmtpVvmCarrierConfigHelper [");
377     builder
378         .append("phoneAccountHandle: ")
379         .append(mPhoneAccountHandle)
380         .append(", carrierConfig: ")
381         .append(mCarrierConfig != null)
382         .append(", telephonyConfig: ")
383         .append(mTelephonyConfig != null)
384         .append(", type: ")
385         .append(getVvmType())
386         .append(", destinationNumber: ")
387         .append(getDestinationNumber())
388         .append(", applicationPort: ")
389         .append(getApplicationPort())
390         .append(", sslPort: ")
391         .append(getSslPort())
392         .append(", isEnabledByDefault: ")
393         .append(isEnabledByDefault())
394         .append(", isCellularDataRequired: ")
395         .append(isCellularDataRequired())
396         .append(", isPrefetchEnabled: ")
397         .append(isPrefetchEnabled())
398         .append(", isLegacyModeEnabled: ")
399         .append(isLegacyModeEnabled())
400         .append("]");
401     return builder.toString();
402   }
403 
404   @Nullable
getCarrierConfig(@onNull TelephonyManager telephonyManager)405   private PersistableBundle getCarrierConfig(@NonNull TelephonyManager telephonyManager) {
406     CarrierConfigManager carrierConfigManager =
407         (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
408     if (carrierConfigManager == null) {
409       VvmLog.w(TAG, "No carrier config service found.");
410       return null;
411     }
412 
413     PersistableBundle config = telephonyManager.getCarrierConfig();
414 
415     if (TextUtils.isEmpty(config.getString(CarrierConfigManager.KEY_VVM_TYPE_STRING))) {
416       return null;
417     }
418     return config;
419   }
420 
421   @Nullable
getValue(String key)422   private Object getValue(String key) {
423     return getValue(key, null);
424   }
425 
426   @Nullable
getValue(String key, Object defaultValue)427   private Object getValue(String key, Object defaultValue) {
428     Object result;
429     if (mCarrierConfig != null) {
430       result = mCarrierConfig.get(key);
431       if (result != null) {
432         return result;
433       }
434     }
435     if (mTelephonyConfig != null) {
436       result = mTelephonyConfig.get(key);
437       if (result != null) {
438         return result;
439       }
440     }
441     return defaultValue;
442   }
443 }
444