VoicemailContract.java revision 16b84b0ab586309ee4d76301c2e578dbfbd7c7db
1/* 2 * Copyright (C) 2011 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 17package android.provider; 18 19import android.Manifest; 20import android.annotation.SdkConstant; 21import android.annotation.SdkConstant.SdkConstantType; 22import android.content.ContentResolver; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.Intent; 26import android.database.ContentObserver; 27import android.database.Cursor; 28import android.net.Uri; 29import android.provider.CallLog.Calls; 30import android.telecom.PhoneAccountHandle; 31import android.telecom.Voicemail; 32 33import java.util.List; 34 35/** 36 * The contract between the voicemail provider and applications. Contains 37 * definitions for the supported URIs and columns. 38 * 39 * <P>The content providers exposes two tables through this interface: 40 * <ul> 41 * <li> Voicemails table: This stores the actual voicemail records. The 42 * columns and URIs for accessing this table are defined by the 43 * {@link Voicemails} class. 44 * </li> 45 * <li> Status table: This provides a way for the voicemail source application 46 * to convey its current state to the system. The columns and URIS for 47 * accessing this table are defined by the {@link Status} class. 48 * </li> 49 * </ul> 50 * 51 * <P> The minimum permission needed to access this content provider is 52 * {@link Manifest.permission#ADD_VOICEMAIL} 53 * 54 * <P>Voicemails are inserted by what is called as a "voicemail source" 55 * application, which is responsible for syncing voicemail data between a remote 56 * server and the local voicemail content provider. "voicemail source" 57 * application should always set the {@link #PARAM_KEY_SOURCE_PACKAGE} in the 58 * URI to identify its package. 59 * 60 * <P>In addition to the {@link ContentObserver} notifications the voicemail 61 * provider also generates broadcast intents to notify change for applications 62 * that are not active and therefore cannot listen to ContentObserver 63 * notifications. Broadcast intents with following actions are generated: 64 * <ul> 65 * <li> {@link #ACTION_NEW_VOICEMAIL} is generated for each new voicemail 66 * inserted. 67 * </li> 68 * <li> {@link Intent#ACTION_PROVIDER_CHANGED} is generated for any change 69 * made into the database, including new voicemail. 70 * </li> 71 * </ul> 72 */ 73public class VoicemailContract { 74 /** Not instantiable. */ 75 private VoicemailContract() { 76 } 77 78 /** The authority used by the voicemail provider. */ 79 public static final String AUTHORITY = "com.android.voicemail"; 80 /** 81 * Parameter key used in the URI to specify the voicemail source package name. 82 * <p> This field must be set in all requests that originate from a voicemail source. 83 */ 84 public static final String PARAM_KEY_SOURCE_PACKAGE = "source_package"; 85 86 /** Broadcast intent when a new voicemail record is inserted. */ 87 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 88 public static final String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL"; 89 90 /** 91 * Broadcast intent to request a voicemail source to fetch voicemail content of a specific 92 * voicemail from the remote server. The voicemail to fetch is specified by the data uri 93 * of the intent. 94 * <p> 95 * All voicemail sources are expected to handle this event. After storing the content 96 * the application should also set {@link Voicemails#HAS_CONTENT} to 1; 97 */ 98 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 99 public static final String ACTION_FETCH_VOICEMAIL = "android.intent.action.FETCH_VOICEMAIL"; 100 101 /** 102 * Extra included in {@link Intent#ACTION_PROVIDER_CHANGED} broadcast intents to indicate if the 103 * receiving package made this change. 104 */ 105 public static final String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE"; 106 107 /** 108 * Name of the source package field, which must be same across all voicemail related tables. 109 * This is an internal field. 110 * @hide 111 */ 112 public static final String SOURCE_PACKAGE_FIELD = "source_package"; 113 114 /** Defines fields exposed through the /voicemail path of this content provider. */ 115 public static final class Voicemails implements BaseColumns, OpenableColumns { 116 /** Not instantiable. */ 117 private Voicemails() { 118 } 119 120 /** URI to insert/retrieve voicemails. */ 121 public static final Uri CONTENT_URI = 122 Uri.parse("content://" + AUTHORITY + "/voicemail"); 123 124 /** The MIME type for a collection of voicemails. */ 125 public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails"; 126 127 /** The MIME type for a single voicemail. */ 128 public static final String ITEM_TYPE = "vnd.android.cursor.item/voicemail"; 129 130 /** 131 * Phone number of the voicemail sender. 132 * <P>Type: TEXT</P> 133 */ 134 public static final String NUMBER = Calls.NUMBER; 135 /** 136 * The date the voicemail was sent, in milliseconds since the epoch 137 * <P>Type: INTEGER (long)</P> 138 */ 139 public static final String DATE = Calls.DATE; 140 /** 141 * The duration of the voicemail in seconds. 142 * <P>Type: INTEGER (long)</P> 143 */ 144 public static final String DURATION = Calls.DURATION; 145 /** 146 * Whether this item has been read or otherwise consumed by the user. 147 * <P>Type: INTEGER (boolean)</P> 148 */ 149 public static final String IS_READ = Calls.IS_READ; 150 /** 151 * The mail box state of the voicemail. This field is currently not used by the system. 152 * <P> Possible values: {@link #STATE_INBOX}, {@link #STATE_DELETED}, 153 * {@link #STATE_UNDELETED}. 154 * <P>Type: INTEGER</P> 155 * @hide 156 */ 157 public static final String STATE = "state"; 158 /** 159 * Value of {@link #STATE} when the voicemail is in inbox. 160 * @hide 161 */ 162 public static int STATE_INBOX = 0; 163 /** 164 * Value of {@link #STATE} when the voicemail has been marked as deleted. 165 * @hide 166 */ 167 public static int STATE_DELETED = 1; 168 /** 169 * Value of {@link #STATE} when the voicemail has marked as undeleted. 170 * @hide 171 */ 172 public static int STATE_UNDELETED = 2; 173 /** 174 * Package name of the source application that inserted the voicemail. 175 * <P>Type: TEXT</P> 176 */ 177 public static final String SOURCE_PACKAGE = SOURCE_PACKAGE_FIELD; 178 /** 179 * Application-specific data available to the source application that 180 * inserted the voicemail. This is typically used to store the source 181 * specific message id to identify this voicemail on the remote 182 * voicemail server. 183 * <P>Type: TEXT</P> 184 * <P> Note that this is NOT the voicemail media content data. 185 */ 186 public static final String SOURCE_DATA = "source_data"; 187 /** 188 * Whether the media content for this voicemail is available for 189 * consumption. 190 * <P>Type: INTEGER (boolean)</P> 191 */ 192 public static final String HAS_CONTENT = "has_content"; 193 /** 194 * MIME type of the media content for the voicemail. 195 * <P>Type: TEXT</P> 196 */ 197 public static final String MIME_TYPE = "mime_type"; 198 /** 199 * The transcription of the voicemail entry. This will only be populated if the voicemail 200 * entry has a valid transcription. 201 * <P>Type: TEXT</P> 202 */ 203 public static final String TRANSCRIPTION = "transcription"; 204 /** 205 * Path to the media content file. Internal only field. 206 * @hide 207 */ 208 public static final String _DATA = "_data"; 209 210 // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming 211 // that was encoded into call log databases. 212 213 /** 214 * The component name of the account in string form. 215 * <P>Type: TEXT</P> 216 */ 217 public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name"; 218 219 /** 220 * The identifier of a account that is unique to a specified component. 221 * <P>Type: TEXT</P> 222 */ 223 public static final String PHONE_ACCOUNT_ID = "subscription_id"; 224 225 /** 226 * Flag used to indicate that local, unsynced changes are present. 227 * Currently, this is used to indicate that the voicemail was read or deleted. 228 * The value will be 1 if dirty is true, 0 if false. 229 * <P>Type: INTEGER (boolean)</P> 230 */ 231 public static final String DIRTY = "dirty"; 232 233 /** 234 * Flag used to indicate that the voicemail was deleted but not synced to the server. 235 * A deleted row should be ignored. 236 * The value will be 1 if deleted is true, 0 if false. 237 * <P>Type: INTEGER (boolean)</P> 238 */ 239 public static final String DELETED = "deleted"; 240 241 /** 242 * A convenience method to build voicemail URI specific to a source package by appending 243 * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI. 244 */ 245 public static Uri buildSourceUri(String packageName) { 246 return Voicemails.CONTENT_URI.buildUpon() 247 .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName) 248 .build(); 249 } 250 251 /** 252 * Inserts a new voicemail into the voicemail content provider. 253 * 254 * @param context The context of the app doing the inserting 255 * @param voicemail Data to be inserted 256 * @return {@link Uri} of the newly inserted {@link Voicemail} 257 * 258 * @hide 259 */ 260 public static Uri insert(Context context, Voicemail voicemail) { 261 ContentResolver contentResolver = context.getContentResolver(); 262 ContentValues contentValues = getContentValues(voicemail); 263 return contentResolver.insert(buildSourceUri(context.getPackageName()), contentValues); 264 } 265 266 /** 267 * Inserts a list of voicemails into the voicemail content provider. 268 * 269 * @param context The context of the app doing the inserting 270 * @param voicemails Data to be inserted 271 * @return the number of voicemails inserted 272 * 273 * @hide 274 */ 275 public static int insert(Context context, List<Voicemail> voicemails) { 276 ContentResolver contentResolver = context.getContentResolver(); 277 int count = voicemails.size(); 278 for (int i = 0; i < count; i++) { 279 ContentValues contentValues = getContentValues(voicemails.get(i)); 280 contentResolver.insert(buildSourceUri(context.getPackageName()), contentValues); 281 } 282 return count; 283 } 284 285 /** 286 * Clears all voicemails accessible to this voicemail content provider for the calling 287 * package. By default, a package only has permission to delete voicemails it inserted. 288 * 289 * @return the number of voicemails deleted 290 * 291 * @hide 292 */ 293 public static int deleteAll(Context context) { 294 return context.getContentResolver().delete( 295 buildSourceUri(context.getPackageName()), "", new String[0]); 296 } 297 298 /** 299 * Maps structured {@link Voicemail} to {@link ContentValues} in content provider. 300 */ 301 private static ContentValues getContentValues(Voicemail voicemail) { 302 ContentValues contentValues = new ContentValues(); 303 contentValues.put(Voicemails.DATE, String.valueOf(voicemail.getTimestampMillis())); 304 contentValues.put(Voicemails.NUMBER, voicemail.getNumber()); 305 contentValues.put(Voicemails.DURATION, String.valueOf(voicemail.getDuration())); 306 contentValues.put(Voicemails.SOURCE_PACKAGE, voicemail.getSourcePackage()); 307 contentValues.put(Voicemails.SOURCE_DATA, voicemail.getSourceData()); 308 contentValues.put(Voicemails.IS_READ, voicemail.isRead() ? 1 : 0); 309 310 PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount(); 311 if (voicemail.getPhoneAccount() != null) { 312 contentValues.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, 313 phoneAccount.getComponentName().flattenToString()); 314 contentValues.put(Voicemails.PHONE_ACCOUNT_ID, phoneAccount.getId()); 315 } 316 return contentValues; 317 } 318 } 319 320 /** Defines fields exposed through the /status path of this content provider. */ 321 public static final class Status implements BaseColumns { 322 /** URI to insert/retrieve status of voicemail source. */ 323 public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/status"); 324 /** The MIME type for a collection of voicemail source statuses. */ 325 public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemail.source.status"; 326 /** The MIME type for a single voicemail source status entry. */ 327 public static final String ITEM_TYPE = "vnd.android.cursor.item/voicemail.source.status"; 328 329 /** Not instantiable. */ 330 private Status() { 331 } 332 /** 333 * The package name of the voicemail source. There can only be a one entry per account 334 * per source. 335 * <P>Type: TEXT</P> 336 */ 337 public static final String SOURCE_PACKAGE = SOURCE_PACKAGE_FIELD; 338 339 // Note: Multiple entries may exist for a single source if they are differentiated by the 340 // PHONE_ACCOUNT_* fields. 341 342 /** 343 * The component name of the account in string form. 344 * <P>Type: TEXT</P> 345 */ 346 public static final String PHONE_ACCOUNT_COMPONENT_NAME = "phone_account_component_name"; 347 348 /** 349 * The identifier of a account that is unique to a specified component. 350 * <P>Type: TEXT</P> 351 */ 352 public static final String PHONE_ACCOUNT_ID = "phone_account_id"; 353 354 /** 355 * The URI to call to invoke source specific voicemail settings screen. On a user request 356 * to setup voicemail an intent with action VIEW with this URI will be fired by the system. 357 * <P>Type: TEXT</P> 358 */ 359 public static final String SETTINGS_URI = "settings_uri"; 360 /** 361 * The URI to call when the user requests to directly access the voicemail from the remote 362 * server. In case of an IVR voicemail system this is typically set to the the voicemail 363 * number specified using a tel:/ URI. 364 * <P>Type: TEXT</P> 365 */ 366 public static final String VOICEMAIL_ACCESS_URI = "voicemail_access_uri"; 367 /** 368 * The configuration state of the voicemail source. 369 * <P> Possible values: 370 * {@link #CONFIGURATION_STATE_OK}, 371 * {@link #CONFIGURATION_STATE_NOT_CONFIGURED}, 372 * {@link #CONFIGURATION_STATE_CAN_BE_CONFIGURED} 373 * <P>Type: INTEGER</P> 374 */ 375 public static final String CONFIGURATION_STATE = "configuration_state"; 376 /** Value of {@link #CONFIGURATION_STATE} to indicate an all OK configuration status. */ 377 public static final int CONFIGURATION_STATE_OK = 0; 378 /** 379 * Value of {@link #CONFIGURATION_STATE} to indicate the visual voicemail is not 380 * yet configured on this device. 381 */ 382 public static final int CONFIGURATION_STATE_NOT_CONFIGURED = 1; 383 /** 384 * Value of {@link #CONFIGURATION_STATE} to indicate the visual voicemail is not 385 * yet configured on this device but can be configured by the user. 386 * <p> This state must be used when the source has verified that the current user can be 387 * upgraded to visual voicemail and would like to show a set up invitation message. 388 */ 389 public static final int CONFIGURATION_STATE_CAN_BE_CONFIGURED = 2; 390 /** 391 * The data channel state of the voicemail source. This the channel through which the source 392 * pulls voicemail data from a remote server. 393 * <P> Possible values: 394 * {@link #DATA_CHANNEL_STATE_OK}, 395 * {@link #DATA_CHANNEL_STATE_NO_CONNECTION} 396 * </P> 397 * <P>Type: INTEGER</P> 398 */ 399 public static final String DATA_CHANNEL_STATE = "data_channel_state"; 400 /** 401 * Value of {@link #DATA_CHANNEL_STATE} to indicate that data channel is working fine. 402 */ 403 public static final int DATA_CHANNEL_STATE_OK = 0; 404 /** 405 * Value of {@link #DATA_CHANNEL_STATE} to indicate that data channel connection is not 406 * working. 407 */ 408 public static final int DATA_CHANNEL_STATE_NO_CONNECTION = 1; 409 /** 410 * The notification channel state of the voicemail source. This is the channel through which 411 * the source gets notified of new voicemails on the remote server. 412 * <P> Possible values: 413 * {@link #NOTIFICATION_CHANNEL_STATE_OK}, 414 * {@link #NOTIFICATION_CHANNEL_STATE_NO_CONNECTION}, 415 * {@link #NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING} 416 * </P> 417 * <P>Type: INTEGER</P> 418 */ 419 public static final String NOTIFICATION_CHANNEL_STATE = "notification_channel_state"; 420 /** 421 * Value of {@link #NOTIFICATION_CHANNEL_STATE} to indicate that the notification channel is 422 * working fine. 423 */ 424 public static final int NOTIFICATION_CHANNEL_STATE_OK = 0; 425 /** 426 * Value of {@link #NOTIFICATION_CHANNEL_STATE} to indicate that the notification channel 427 * connection is not working. 428 */ 429 public static final int NOTIFICATION_CHANNEL_STATE_NO_CONNECTION = 1; 430 /** 431 * Value of {@link #NOTIFICATION_CHANNEL_STATE} to indicate that there are messages waiting 432 * on the server but the details are not known. 433 * <p> Use this state when the notification can only tell that there are pending messages on 434 * the server but no details of the sender/time etc are known. 435 */ 436 public static final int NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING = 2; 437 438 /** 439 * A convenience method to build status URI specific to a source package by appending 440 * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI. 441 */ 442 public static Uri buildSourceUri(String packageName) { 443 return Status.CONTENT_URI.buildUpon() 444 .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build(); 445 } 446 447 /** 448 * A helper method to set the status of a voicemail source. 449 * 450 * @param context The context from the package calling the method. This will be the source. 451 * @param accountHandle The handle for the account the source is associated with. 452 * @param configurationState See {@link Status#CONFIGURATION_STATE} 453 * @param dataChannelState See {@link Status#DATA_CHANNEL_STATE} 454 * @param notificationChannelState See {@link Status#NOTIFICATION_CHANNEL_STATE} 455 * 456 * @hide 457 */ 458 public static void setStatus(Context context, PhoneAccountHandle accountHandle, 459 int configurationState, int dataChannelState, int notificationChannelState) { 460 ContentResolver contentResolver = context.getContentResolver(); 461 Uri statusUri = buildSourceUri(context.getPackageName()); 462 ContentValues values = new ContentValues(); 463 values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME, 464 accountHandle.getComponentName().toString()); 465 values.put(Status.PHONE_ACCOUNT_ID, accountHandle.getId()); 466 values.put(Status.CONFIGURATION_STATE, configurationState); 467 values.put(Status.DATA_CHANNEL_STATE, dataChannelState); 468 values.put(Status.NOTIFICATION_CHANNEL_STATE, notificationChannelState); 469 470 if (isStatusPresent(contentResolver, statusUri)) { 471 contentResolver.update(statusUri, values, null, null); 472 } else { 473 contentResolver.insert(statusUri, values); 474 } 475 } 476 477 /** 478 * Determines if a voicemail source exists in the status table. 479 * 480 * @param contentResolver A content resolver constructed from the appropriate context. 481 * @param statusUri The content uri for the source. 482 * @return {@code true} if a status entry for this source exists 483 */ 484 private static boolean isStatusPresent(ContentResolver contentResolver, Uri statusUri) { 485 Cursor cursor = null; 486 try { 487 cursor = contentResolver.query(statusUri, null, null, null, null); 488 return cursor != null && cursor.getCount() != 0; 489 } finally { 490 if (cursor != null) cursor.close(); 491 } 492 } 493 } 494} 495