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