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