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