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