1 /**
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  * <p>http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * <p>Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11  * express or implied. See the License for the specific language governing permissions and
12  * limitations under the License
13  */
14 package com.android.voicemail.impl;
15 
16 import android.annotation.TargetApi;
17 import android.content.Context;
18 import android.content.Intent;
19 import android.content.SharedPreferences;
20 import android.os.Build.VERSION_CODES;
21 import android.os.PersistableBundle;
22 import android.preference.PreferenceManager;
23 import android.provider.VoicemailContract.Status;
24 import android.provider.VoicemailContract.Voicemails;
25 import android.support.annotation.MainThread;
26 import android.support.annotation.NonNull;
27 import android.support.annotation.Nullable;
28 import android.support.v4.os.BuildCompat;
29 import android.telecom.PhoneAccountHandle;
30 import android.telephony.TelephonyManager;
31 import com.android.dialer.common.Assert;
32 import com.android.dialer.common.LogUtil;
33 import com.android.dialer.configprovider.ConfigProviderComponent;
34 import com.android.voicemail.PinChanger;
35 import com.android.voicemail.VisualVoicemailTypeExtensions;
36 import com.android.voicemail.VoicemailClient;
37 import com.android.voicemail.VoicemailVersionConstants;
38 import com.android.voicemail.impl.configui.VoicemailSecretCodeActivity;
39 import com.android.voicemail.impl.settings.VisualVoicemailSettingsUtil;
40 import com.android.voicemail.impl.sync.VvmAccountManager;
41 import com.android.voicemail.impl.transcribe.TranscriptionBackfillService;
42 import com.android.voicemail.impl.transcribe.TranscriptionConfigProvider;
43 import java.util.List;
44 import javax.inject.Inject;
45 
46 /**
47  * {@link VoicemailClient} to be used when the voicemail module is activated. May only be used above
48  * O.
49  */
50 public class VoicemailClientImpl implements VoicemailClient {
51 
52   /**
53    * List of legacy OMTP voicemail packages that should be ignored. It could never be the active VVM
54    * package anymore. For example, voicemails in OC will no longer be handled by telephony, but
55    * legacy voicemails might still exist in the database due to upgrading from NYC. Dialer will
56    * fetch these voicemails again so it should be ignored.
57    */
58   private static final String[] OMTP_VOICEMAIL_BLACKLIST = {"com.android.phone"};
59 
60   // Flag name used for configuration
61   private static final String ALLOW_VOICEMAIL_ARCHIVE = "allow_voicemail_archive";
62 
63   private static final String[] OMTP_VOICEMAIL_TYPE = {
64     TelephonyManager.VVM_TYPE_OMTP,
65     TelephonyManager.VVM_TYPE_CVVM,
66     VisualVoicemailTypeExtensions.VVM_TYPE_VVM3
67   };
68 
69   @Inject
VoicemailClientImpl()70   public VoicemailClientImpl() {
71     Assert.checkArgument(BuildCompat.isAtLeastO());
72   }
73 
74   @Override
isVoicemailModuleEnabled()75   public boolean isVoicemailModuleEnabled() {
76     return true;
77   }
78 
79   @Override
hasCarrierSupport(Context context, PhoneAccountHandle phoneAccountHandle)80   public boolean hasCarrierSupport(Context context, PhoneAccountHandle phoneAccountHandle) {
81     OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle);
82     return config.isValid() && !config.isCarrierAppInstalled();
83   }
84 
85   @Override
isVoicemailEnabled(Context context, PhoneAccountHandle phoneAccountHandle)86   public boolean isVoicemailEnabled(Context context, PhoneAccountHandle phoneAccountHandle) {
87     return VisualVoicemailSettingsUtil.isEnabled(context, phoneAccountHandle);
88   }
89 
90   @Override
setVoicemailEnabled( Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled)91   public void setVoicemailEnabled(
92       Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled) {
93     VisualVoicemailSettingsUtil.setEnabled(context, phoneAccountHandle, enabled);
94   }
95 
96   @Override
isVoicemailArchiveEnabled(Context context, PhoneAccountHandle phoneAccountHandle)97   public boolean isVoicemailArchiveEnabled(Context context, PhoneAccountHandle phoneAccountHandle) {
98     return VisualVoicemailSettingsUtil.isArchiveEnabled(context, phoneAccountHandle);
99   }
100 
101   @Override
isVoicemailArchiveAvailable(Context context)102   public boolean isVoicemailArchiveAvailable(Context context) {
103     if (!BuildCompat.isAtLeastO()) {
104       LogUtil.i("VoicemailClientImpl.isVoicemailArchiveAllowed", "not running on O or later");
105       return false;
106     }
107 
108     if (!ConfigProviderComponent.get(context)
109         .getConfigProvider()
110         .getBoolean(ALLOW_VOICEMAIL_ARCHIVE, false)) {
111       LogUtil.i(
112           "VoicemailClientImpl.isVoicemailArchiveAllowed",
113           "feature disabled by config: %s",
114           ALLOW_VOICEMAIL_ARCHIVE);
115       return false;
116     }
117 
118     return true;
119   }
120 
121   @Override
setVoicemailArchiveEnabled( Context context, PhoneAccountHandle phoneAccountHandle, boolean value)122   public void setVoicemailArchiveEnabled(
123       Context context, PhoneAccountHandle phoneAccountHandle, boolean value) {
124     VisualVoicemailSettingsUtil.setArchiveEnabled(context, phoneAccountHandle, value);
125   }
126 
127   @Override
isVoicemailTranscriptionAvailable( Context context, PhoneAccountHandle phoneAccountHandle)128   public boolean isVoicemailTranscriptionAvailable(
129       Context context, PhoneAccountHandle phoneAccountHandle) {
130     if (phoneAccountHandle == null) {
131       LogUtil.i(
132           "VoicemailClientImpl.isVoicemailTranscriptionAvailable", "phone account handle is null");
133     }
134 
135     if (!BuildCompat.isAtLeastO()) {
136       LogUtil.i(
137           "VoicemailClientImpl.isVoicemailTranscriptionAvailable", "not running on O or later");
138       return false;
139     }
140 
141     if (!isVoicemailEnabled(context, phoneAccountHandle)) {
142       LogUtil.i(
143           "VoicemailClientImpl.isVoicemailTranscriptionAvailable",
144           "visual voicemail is not enabled");
145       return false;
146     }
147 
148     if (!isActivated(context, phoneAccountHandle)) {
149       LogUtil.i(
150           "VoicemailClientImpl.isVoicemailTranscriptionAvailable",
151           "visual voicemail is not activated");
152       return false;
153     }
154 
155     TranscriptionConfigProvider provider = new TranscriptionConfigProvider(context);
156     if (!provider.isVoicemailTranscriptionAvailable()) {
157       LogUtil.i(
158           "VoicemailClientImpl.isVoicemailTranscriptionAvailable", "feature disabled by config");
159       return false;
160     }
161 
162     return true;
163   }
164 
165   @Override
isVoicemailTranscriptionEnabled(Context context, PhoneAccountHandle account)166   public boolean isVoicemailTranscriptionEnabled(Context context, PhoneAccountHandle account) {
167     return isVoicemailTranscriptionAvailable(context, account)
168         && VisualVoicemailSettingsUtil.isVoicemailTranscriptionEnabled(context, account);
169   }
170 
171   @Override
isVoicemailDonationAvailable( Context context, PhoneAccountHandle phoneAccountHandle)172   public boolean isVoicemailDonationAvailable(
173       Context context, PhoneAccountHandle phoneAccountHandle) {
174     if (!isVoicemailTranscriptionAvailable(context, phoneAccountHandle)) {
175       LogUtil.i("VoicemailClientImpl.isVoicemailDonationAvailable", "transcription not available");
176       return false;
177     }
178 
179     if (!isVoicemailTranscriptionEnabled(context, phoneAccountHandle)) {
180       LogUtil.i("VoicemailClientImpl.isVoicemailDonationAvailable", "transcription not enabled");
181       return false;
182     }
183 
184     TranscriptionConfigProvider provider = new TranscriptionConfigProvider(context);
185     if (!provider.isVoicemailDonationAvailable()) {
186       LogUtil.i("VoicemailClientImpl.isVoicemailDonationAvailable", "feature disabled by config");
187       return false;
188     }
189 
190     return true;
191   }
192 
193   @Override
isVoicemailDonationEnabled(Context context, PhoneAccountHandle account)194   public boolean isVoicemailDonationEnabled(Context context, PhoneAccountHandle account) {
195     return isVoicemailTranscriptionEnabled(context, account)
196         && isVoicemailDonationAvailable(context, account)
197         && VisualVoicemailSettingsUtil.isVoicemailDonationEnabled(context, account);
198   }
199 
200   @Override
setVoicemailTranscriptionEnabled( Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled)201   public void setVoicemailTranscriptionEnabled(
202       Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled) {
203     Assert.checkArgument(
204         isVoicemailTranscriptionAvailable(context, phoneAccountHandle),
205         "transcription must be available before enabling/disabling it");
206     VisualVoicemailSettingsUtil.setVoicemailTranscriptionEnabled(
207         context, phoneAccountHandle, enabled);
208     if (enabled) {
209       TranscriptionBackfillService.scheduleTask(context, phoneAccountHandle);
210     }
211   }
212 
213   @Override
setVoicemailDonationEnabled( Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled)214   public void setVoicemailDonationEnabled(
215       Context context, PhoneAccountHandle phoneAccountHandle, boolean enabled) {
216     if (enabled) {
217       Assert.checkArgument(
218           isVoicemailTranscriptionAvailable(context, phoneAccountHandle)
219               && isVoicemailTranscriptionEnabled(context, phoneAccountHandle),
220           "should not be able to enable donation without transcription "
221               + "available(value: %b) and enabled (value:%b) for account:%s",
222           isVoicemailTranscriptionAvailable(context, phoneAccountHandle),
223           isVoicemailTranscriptionEnabled(context, phoneAccountHandle),
224           phoneAccountHandle.toString());
225     }
226     VisualVoicemailSettingsUtil.setVoicemailDonationEnabled(context, phoneAccountHandle, enabled);
227   }
228 
229   @Override
isActivated(Context context, PhoneAccountHandle phoneAccountHandle)230   public boolean isActivated(Context context, PhoneAccountHandle phoneAccountHandle) {
231     return VvmAccountManager.isAccountActivated(context, phoneAccountHandle);
232   }
233 
234   @Override
showConfigUi(@onNull Context context)235   public void showConfigUi(@NonNull Context context) {
236     Intent intent = new Intent(context, VoicemailSecretCodeActivity.class);
237     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
238     context.startActivity(intent);
239   }
240 
241   @Override
getConfig(Context context, PhoneAccountHandle phoneAccountHandle)242   public PersistableBundle getConfig(Context context, PhoneAccountHandle phoneAccountHandle) {
243     return new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle).getConfig();
244   }
245 
246   @Override
247   @MainThread
onBoot(@onNull Context context)248   public void onBoot(@NonNull Context context) {
249     OmtpService.onBoot(context);
250     StatusCheckJobService.schedule(context);
251   }
252 
253   @Override
254   @MainThread
onShutdown(@onNull Context context)255   public void onShutdown(@NonNull Context context) {
256     OmtpService.onShutdown(context);
257   }
258 
259   @Override
addActivationStateListener(ActivationStateListener listener)260   public void addActivationStateListener(ActivationStateListener listener) {
261     VvmAccountManager.addListener(listener);
262   }
263 
264   @Override
removeActivationStateListener(ActivationStateListener listener)265   public void removeActivationStateListener(ActivationStateListener listener) {
266     VvmAccountManager.removeListener(listener);
267   }
268 
269   @Override
createPinChanger(Context context, PhoneAccountHandle phoneAccountHandle)270   public PinChanger createPinChanger(Context context, PhoneAccountHandle phoneAccountHandle) {
271     return new PinChangerImpl(context, phoneAccountHandle);
272   }
273 
274   @TargetApi(VERSION_CODES.O)
275   @Override
appendOmtpVoicemailSelectionClause( Context context, StringBuilder where, List<String> selectionArgs)276   public void appendOmtpVoicemailSelectionClause(
277       Context context, StringBuilder where, List<String> selectionArgs) {
278     String omtpSource =
279         context.getSystemService(TelephonyManager.class).getVisualVoicemailPackageName();
280     if (where.length() != 0) {
281       where.append(" AND ");
282     }
283     where.append("(");
284     {
285       where.append("(");
286       {
287         where.append(Voicemails.IS_OMTP_VOICEMAIL).append(" != 1");
288         where.append(")");
289       }
290       where.append(" OR ");
291       where.append("(");
292       {
293         where.append(Voicemails.SOURCE_PACKAGE).append(" = ?");
294         selectionArgs.add(omtpSource);
295         where.append(")");
296       }
297       where.append(")");
298     }
299 
300     for (String blacklistedPackage : OMTP_VOICEMAIL_BLACKLIST) {
301       where.append("AND (").append(Voicemails.SOURCE_PACKAGE).append("!= ?)");
302       selectionArgs.add(blacklistedPackage);
303     }
304   }
305 
306   @TargetApi(VERSION_CODES.O)
307   @Override
appendOmtpVoicemailStatusSelectionClause( Context context, StringBuilder where, List<String> selectionArgs)308   public void appendOmtpVoicemailStatusSelectionClause(
309       Context context, StringBuilder where, List<String> selectionArgs) {
310     String omtpSource =
311         context.getSystemService(TelephonyManager.class).getVisualVoicemailPackageName();
312     if (where.length() != 0) {
313       where.append(" AND ");
314     }
315     where.append("(");
316     {
317       where.append("(");
318       {
319         where.append(Status.SOURCE_PACKAGE).append(" = ? ");
320         selectionArgs.add(omtpSource);
321         where.append(")");
322       }
323       where.append(" OR NOT (");
324       {
325         for (int i = 0; i < OMTP_VOICEMAIL_TYPE.length; i++) {
326           if (i != 0) {
327             where.append(" OR ");
328           }
329           where.append(" (");
330           {
331             where.append(Status.SOURCE_TYPE).append(" IS ?");
332             selectionArgs.add(OMTP_VOICEMAIL_TYPE[i]);
333             where.append(")");
334           }
335         }
336         where.append(")");
337       }
338       for (String blacklistedPackage : OMTP_VOICEMAIL_BLACKLIST) {
339         where.append("AND (");
340         {
341           where.append(Voicemails.SOURCE_PACKAGE).append("!= ?");
342           selectionArgs.add(blacklistedPackage);
343           where.append(")");
344         }
345       }
346       where.append(")");
347     }
348   }
349 
350   @Override
onTosAccepted(Context context, PhoneAccountHandle account)351   public void onTosAccepted(Context context, PhoneAccountHandle account) {
352     LogUtil.i("VoicemailClientImpl.onTosAccepted", "try backfilling voicemail transcriptions");
353     TranscriptionBackfillService.scheduleTask(context, account);
354   }
355 
356   @Override
hasAcceptedTos(Context context, PhoneAccountHandle phoneAccountHandle)357   public boolean hasAcceptedTos(Context context, PhoneAccountHandle phoneAccountHandle) {
358     SharedPreferences preferences =
359         PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
360     OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle);
361     boolean isVvm3 = VisualVoicemailTypeExtensions.VVM_TYPE_VVM3.equals(helper.getVvmType());
362     if (isVvm3) {
363       return preferences.getInt(VoicemailVersionConstants.PREF_VVM3_TOS_VERSION_ACCEPTED_KEY, 0)
364           >= VoicemailVersionConstants.CURRENT_VVM3_TOS_VERSION;
365     } else {
366       return preferences.getInt(VoicemailVersionConstants.PREF_DIALER_TOS_VERSION_ACCEPTED_KEY, 0)
367           >= VoicemailVersionConstants.CURRENT_DIALER_TOS_VERSION;
368     }
369   }
370 
371   @Override
372   @Nullable
getCarrierConfigString(Context context, PhoneAccountHandle account, String key)373   public String getCarrierConfigString(Context context, PhoneAccountHandle account, String key) {
374     OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, account);
375     return helper.isValid() ? helper.getString(key) : null;
376   }
377 }
378