1 /* 2 * Copyright (C) 2016 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 17 package com.android.dialer.app.voicemail.error; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.database.Cursor; 22 import android.net.Uri; 23 import android.os.Build.VERSION; 24 import android.os.Build.VERSION_CODES; 25 import android.provider.Settings; 26 import android.provider.Settings.Global; 27 import android.provider.VoicemailContract.Status; 28 import android.support.annotation.Nullable; 29 import android.support.v4.os.BuildCompat; 30 import android.telecom.PhoneAccountHandle; 31 import android.telephony.ServiceState; 32 import android.telephony.TelephonyManager; 33 import com.android.dialer.common.LogUtil; 34 import com.android.dialer.database.VoicemailStatusQuery; 35 36 /** Structured data from {@link android.provider.VoicemailContract.Status} */ 37 public class VoicemailStatus { 38 39 public final String sourcePackage; 40 public final String type; 41 42 public final String phoneAccountComponentName; 43 public final String phoneAccountId; 44 45 @Nullable public final Uri settingsUri; 46 @Nullable public final Uri voicemailAccessUri; 47 48 public final int configurationState; 49 public final int dataChannelState; 50 public final int notificationChannelState; 51 52 public final int quotaOccupied; 53 public final int quotaTotal; 54 55 // System status 56 57 public final boolean isAirplaneMode; 58 59 /** Wraps the row currently pointed by <code>statusCursor</code> */ VoicemailStatus(Context context, Cursor statusCursor)60 public VoicemailStatus(Context context, Cursor statusCursor) { 61 sourcePackage = getString(statusCursor, VoicemailStatusQuery.SOURCE_PACKAGE_INDEX, ""); 62 63 settingsUri = getUri(statusCursor, VoicemailStatusQuery.SETTINGS_URI_INDEX); 64 voicemailAccessUri = getUri(statusCursor, VoicemailStatusQuery.VOICEMAIL_ACCESS_URI_INDEX); 65 66 if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) { 67 type = 68 getString( 69 statusCursor, VoicemailStatusQuery.SOURCE_TYPE_INDEX, TelephonyManager.VVM_TYPE_OMTP); 70 phoneAccountComponentName = 71 getString(statusCursor, VoicemailStatusQuery.PHONE_ACCOUNT_COMPONENT_NAME, ""); 72 phoneAccountId = getString(statusCursor, VoicemailStatusQuery.PHONE_ACCOUNT_ID, ""); 73 } else { 74 type = TelephonyManager.VVM_TYPE_OMTP; 75 phoneAccountComponentName = ""; 76 phoneAccountId = ""; 77 } 78 79 configurationState = 80 getInt( 81 statusCursor, 82 VoicemailStatusQuery.CONFIGURATION_STATE_INDEX, 83 Status.CONFIGURATION_STATE_NOT_CONFIGURED); 84 dataChannelState = 85 getInt( 86 statusCursor, 87 VoicemailStatusQuery.DATA_CHANNEL_STATE_INDEX, 88 Status.DATA_CHANNEL_STATE_NO_CONNECTION); 89 90 /* Before O, the NOTIFICATION_CHANNEL_STATE in the voicemail status table for the system 91 * visual voicemail client always correspond to the service state (cellular signal availability) 92 * Tracking the state in the background is redundant because it will not be visible to the 93 * user. It is much simpler to poll the status on the UI side. The result is injected back to 94 * the status query result so the handling will be consistent with other voicemail clients. 95 */ 96 if (BuildCompat.isAtLeastO() && sourcePackage.equals(context.getPackageName())) { 97 notificationChannelState = 98 getNotificationChannelStateFormTelephony(context, getPhoneAccountHandle()); 99 } else { 100 notificationChannelState = 101 getInt( 102 statusCursor, 103 VoicemailStatusQuery.NOTIFICATION_CHANNEL_STATE_INDEX, 104 Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION); 105 } 106 isAirplaneMode = 107 Settings.System.getInt(context.getContentResolver(), Global.AIRPLANE_MODE_ON, 0) != 0; 108 109 if (VERSION.SDK_INT >= VERSION_CODES.N) { 110 quotaOccupied = 111 getInt(statusCursor, VoicemailStatusQuery.QUOTA_OCCUPIED_INDEX, Status.QUOTA_UNAVAILABLE); 112 quotaTotal = 113 getInt(statusCursor, VoicemailStatusQuery.QUOTA_TOTAL_INDEX, Status.QUOTA_UNAVAILABLE); 114 } else { 115 quotaOccupied = Status.QUOTA_UNAVAILABLE; 116 quotaTotal = Status.QUOTA_UNAVAILABLE; 117 } 118 } 119 getNotificationChannelStateFormTelephony( Context context, PhoneAccountHandle phoneAccountHandle)120 private static int getNotificationChannelStateFormTelephony( 121 Context context, PhoneAccountHandle phoneAccountHandle) { 122 TelephonyManager telephonyManager = 123 context 124 .getSystemService(TelephonyManager.class) 125 .createForPhoneAccountHandle(phoneAccountHandle); 126 if (telephonyManager == null) { 127 LogUtil.e("VoicemailStatus.constructor", "invalid PhoneAccountHandle"); 128 return Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION; 129 } else { 130 int state = telephonyManager.getServiceState().getState(); 131 if (state == ServiceState.STATE_IN_SERVICE) { 132 return Status.NOTIFICATION_CHANNEL_STATE_OK; 133 } else { 134 return Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION; 135 } 136 } 137 } 138 VoicemailStatus(Builder builder)139 private VoicemailStatus(Builder builder) { 140 sourcePackage = builder.sourcePackage; 141 phoneAccountComponentName = builder.phoneAccountComponentName; 142 phoneAccountId = builder.phoneAccountId; 143 type = builder.type; 144 settingsUri = builder.settingsUri; 145 voicemailAccessUri = builder.voicemailAccessUri; 146 configurationState = builder.configurationState; 147 dataChannelState = builder.dataChannelState; 148 notificationChannelState = builder.notificationChannelState; 149 quotaOccupied = builder.quotaOccupied; 150 quotaTotal = builder.quotaTotal; 151 isAirplaneMode = builder.isAirplaneMode; 152 } 153 154 static class Builder { 155 156 private String sourcePackage = ""; 157 private String type = TelephonyManager.VVM_TYPE_OMTP; 158 private String phoneAccountComponentName = ""; 159 private String phoneAccountId = ""; 160 161 @Nullable private Uri settingsUri; 162 @Nullable private Uri voicemailAccessUri; 163 164 private int configurationState = Status.CONFIGURATION_STATE_NOT_CONFIGURED; 165 private int dataChannelState = Status.DATA_CHANNEL_STATE_NO_CONNECTION; 166 private int notificationChannelState = Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION; 167 168 private int quotaOccupied = Status.QUOTA_UNAVAILABLE; 169 private int quotaTotal = Status.QUOTA_UNAVAILABLE; 170 171 private boolean isAirplaneMode; 172 build()173 public VoicemailStatus build() { 174 return new VoicemailStatus(this); 175 } 176 setSourcePackage(String sourcePackage)177 public Builder setSourcePackage(String sourcePackage) { 178 this.sourcePackage = sourcePackage; 179 return this; 180 } 181 setType(String type)182 public Builder setType(String type) { 183 this.type = type; 184 return this; 185 } 186 setPhoneAccountComponentName(String name)187 public Builder setPhoneAccountComponentName(String name) { 188 this.phoneAccountComponentName = name; 189 return this; 190 } 191 setPhoneAccountId(String id)192 public Builder setPhoneAccountId(String id) { 193 this.phoneAccountId = id; 194 return this; 195 } 196 setSettingsUri(Uri settingsUri)197 public Builder setSettingsUri(Uri settingsUri) { 198 this.settingsUri = settingsUri; 199 return this; 200 } 201 setVoicemailAccessUri(Uri voicemailAccessUri)202 public Builder setVoicemailAccessUri(Uri voicemailAccessUri) { 203 this.voicemailAccessUri = voicemailAccessUri; 204 return this; 205 } 206 setConfigurationState(int configurationState)207 public Builder setConfigurationState(int configurationState) { 208 this.configurationState = configurationState; 209 return this; 210 } 211 setDataChannelState(int dataChannelState)212 public Builder setDataChannelState(int dataChannelState) { 213 this.dataChannelState = dataChannelState; 214 return this; 215 } 216 setNotificationChannelState(int notificationChannelState)217 public Builder setNotificationChannelState(int notificationChannelState) { 218 this.notificationChannelState = notificationChannelState; 219 return this; 220 } 221 setQuotaOccupied(int quotaOccupied)222 public Builder setQuotaOccupied(int quotaOccupied) { 223 this.quotaOccupied = quotaOccupied; 224 return this; 225 } 226 setQuotaTotal(int quotaTotal)227 public Builder setQuotaTotal(int quotaTotal) { 228 this.quotaTotal = quotaTotal; 229 return this; 230 } 231 setAirplaneMode(boolean isAirplaneMode)232 public Builder setAirplaneMode(boolean isAirplaneMode) { 233 this.isAirplaneMode = isAirplaneMode; 234 return this; 235 } 236 } 237 isActive()238 public boolean isActive() { 239 switch (configurationState) { 240 case Status.CONFIGURATION_STATE_NOT_CONFIGURED: 241 case Status.CONFIGURATION_STATE_DISABLED: 242 return false; 243 default: 244 return true; 245 } 246 } 247 248 @Override toString()249 public String toString() { 250 return "VoicemailStatus[" 251 + "sourcePackage: " 252 + sourcePackage 253 + ", type:" 254 + type 255 + ", settingsUri: " 256 + settingsUri 257 + ", voicemailAccessUri: " 258 + voicemailAccessUri 259 + ", configurationState: " 260 + configurationState 261 + ", dataChannelState: " 262 + dataChannelState 263 + ", notificationChannelState: " 264 + notificationChannelState 265 + ", quotaOccupied: " 266 + quotaOccupied 267 + ", quotaTotal: " 268 + quotaTotal 269 + ", isAirplaneMode: " 270 + isAirplaneMode 271 + "]"; 272 } 273 274 @Nullable getUri(Cursor cursor, int index)275 private static Uri getUri(Cursor cursor, int index) { 276 if (cursor.getString(index) != null) { 277 return Uri.parse(cursor.getString(index)); 278 } 279 return null; 280 } 281 getInt(Cursor cursor, int index, int defaultValue)282 private static int getInt(Cursor cursor, int index, int defaultValue) { 283 if (cursor.isNull(index)) { 284 return defaultValue; 285 } 286 return cursor.getInt(index); 287 } 288 getString(Cursor cursor, int index, String defaultValue)289 private static String getString(Cursor cursor, int index, String defaultValue) { 290 if (cursor.isNull(index)) { 291 return defaultValue; 292 } 293 return cursor.getString(index); 294 } 295 getPhoneAccountHandle()296 public PhoneAccountHandle getPhoneAccountHandle() { 297 return new PhoneAccountHandle( 298 ComponentName.unflattenFromString(phoneAccountComponentName), phoneAccountId); 299 } 300 } 301