1 /* 2 * Copyright (C) 2013 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.bluetooth.mapapi; 18 19 import android.content.ContentResolver; 20 import android.net.Uri; 21 22 23 /** 24 * This class defines the minimum sets of data needed for an E-mail client to 25 * implement to claim support for the Bluetooth Message Access Profile. 26 * Access to three data sets are needed: 27 * <ul> 28 * <li>Message data set containing lists of messages.</li> 29 * <li>Account data set containing info on the existing accounts, and whether to expose 30 * these accounts. The content of the account data set is often sensitive information, 31 * hence care must be taken, not to reveal any personal information nor passwords. 32 * The accounts in this data base will be exposed in the settings menu, where the user 33 * is able to enable and disable the EXPOSE_FLAG, and thereby provide access to an 34 * account from another device, without any password protection the e-mail client 35 * might provide.</li> 36 * <li>Folder data set with the folder structure for the messages. Each message is linked to an 37 * entry in this data set.</li> 38 * </ul> 39 * 40 * To enable that the Bluetooth Message Access Server can detect the content provider implementing 41 * this interface, the {@code provider} tag for the bluetooth related content provider must 42 * have an intent-filter like the following in the manifest: 43 * <pre class="prettyprint"><provider android:authorities="[PROVIDER AUTHORITY]" 44 android:exported="true" 45 android:enabled="true" 46 android:permission="android.permission.BLUETOOTH_MAP"> 47 * ... 48 * <intent-filter> 49 <action android:name="android.content.action.BLEUETOOT_MAP_PROVIDER" /> 50 </intent-filter> 51 * ... 52 * </provider> 53 * [PROVIDER AUTHORITY] shall be the providers authority value which implements this 54 * contract. Only a single authority shall be used. The android.permission.BLUETOOTH_MAP 55 * permission is needed for the provider. 56 */ 57 public final class BluetoothMapContract { 58 /** 59 * Constructor - should not be used 60 */ BluetoothMapContract()61 private BluetoothMapContract(){ 62 /* class should not be instantiated */ 63 } 64 65 /** 66 * Provider interface that should be used as intent-filter action in the provider section 67 * of the manifest file. 68 */ 69 public static final String PROVIDER_INTERFACE = "android.bluetooth.action.BLUETOOTH_MAP_PROVIDER"; 70 71 /** 72 * The Bluetooth Message Access profile allows a remote BT-MAP client to trigger 73 * an update of a folder for a specific e-mail account, register for reception 74 * of new messages from the server. 75 * 76 * Additionally the Bluetooth Message Access profile allows a remote BT-MAP client 77 * to push a message to a folder - e.g. outbox or draft. The Bluetooth profile 78 * implementation will place a new message in one of these existing folders through 79 * the content provider. 80 * 81 * ContentProvider.call() is used for these purposes, and the METHOD_UPDATE_FOLDER 82 * method name shall trigger an update of the specified folder for a specified 83 * account. 84 * 85 * This shall be a non blocking call simply starting the update, and the update should 86 * both send and receive messages, depending on what makes sense for the specified 87 * folder. 88 * Bundle extra parameter will carry two INTEGER (long) values: 89 * EXTRA_UPDATE_ACCOUNT_ID containing the account_id 90 * EXTRA_UPDATE_FOLDER_ID containing the folder_id of the folder to update 91 * 92 * The status for send complete of messages shall be reported by updating the sent-flag 93 * and e.g. for outbox messages, move them to the sent folder in the message table of the 94 * content provider and trigger a change notification to any attached content observer. 95 */ 96 public static final String METHOD_UPDATE_FOLDER = "UpdateFolder"; 97 public static final String EXTRA_UPDATE_ACCOUNT_ID = "UpdateAccountId"; 98 public static final String EXTRA_UPDATE_FOLDER_ID = "UpdateFolderId"; 99 100 /** 101 * These column names are used as last path segment of the URI (getLastPathSegment()). 102 * Access to a specific row in the tables is done by using the where-clause, hence 103 * support for .../#id if not needed for the Email clients. 104 * The URI format for accessing the tables are as follows: 105 * content://ProviderAuthority/TABLE_ACCOUNT 106 * content://ProviderAuthority/account_id/TABLE_MESSAGE 107 * content://ProviderAuthority/account_id/TABLE_FOLDER 108 */ 109 110 /** 111 * Build URI representing the given Accounts data-set in a 112 * bluetooth provider. When queried, the direct URI for the account 113 * with the given accountID is returned. 114 */ buildAccountUri(String authority)115 public static Uri buildAccountUri(String authority) { 116 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 117 .authority(authority).appendPath(TABLE_ACCOUNT).build(); 118 } 119 /** 120 * Build URI representing the given Account data-set with specific Id in a 121 * Bluetooth provider. When queried, the direct URI for the account 122 * with the given accountID is returned. 123 */ buildAccountUriwithId(String authority, String accountId)124 public static Uri buildAccountUriwithId(String authority, String accountId) { 125 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 126 .authority(authority) 127 .appendPath(TABLE_ACCOUNT) 128 .appendPath(accountId) 129 .build(); 130 } 131 /** 132 * Build URI representing the entire Message table in a 133 * bluetooth provider. 134 */ buildMessageUri(String authority)135 public static Uri buildMessageUri(String authority) { 136 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 137 .authority(authority) 138 .appendPath(TABLE_MESSAGE) 139 .build(); 140 } 141 /** 142 * Build URI representing the given Message data-set in a 143 * bluetooth provider. When queried, the URI for the Messages 144 * with the given accountID is returned. 145 */ buildMessageUri(String authority, String accountId)146 public static Uri buildMessageUri(String authority, String accountId) { 147 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 148 .authority(authority) 149 .appendPath(accountId) 150 .appendPath(TABLE_MESSAGE) 151 .build(); 152 } 153 /** 154 * Build URI representing the given Message data-set with specific messageId in a 155 * bluetooth provider. When queried, the direct URI for the account 156 * with the given accountID is returned. 157 */ buildMessageUriWithId(String authority, String accountId,String messageId)158 public static Uri buildMessageUriWithId(String authority, String accountId,String messageId) { 159 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 160 .authority(authority) 161 .appendPath(accountId) 162 .appendPath(TABLE_MESSAGE) 163 .appendPath(messageId) 164 .build(); 165 } 166 /** 167 * Build URI representing the given Message data-set in a 168 * bluetooth provider. When queried, the direct URI for the account 169 * with the given accountID is returned. 170 */ buildFolderUri(String authority, String accountId)171 public static Uri buildFolderUri(String authority, String accountId) { 172 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 173 .authority(authority) 174 .appendPath(accountId) 175 .appendPath(TABLE_FOLDER) 176 .build(); 177 } 178 179 /** 180 * @hide 181 */ 182 public static final String TABLE_ACCOUNT = "Account"; 183 public static final String TABLE_MESSAGE = "Message"; 184 public static final String TABLE_FOLDER = "Folder"; 185 186 /** 187 * Mandatory folders for the Bluetooth message access profile. 188 * The email client shall at least implement the following root folders. 189 * E.g. as a mapping for them such that the naming will match the underlying 190 * matching folder ID's. 191 */ 192 public static final String FOLDER_NAME_INBOX = "inbox"; 193 public static final String FOLDER_NAME_OUTBOX = "outbox"; 194 public static final String FOLDER_NAME_SENT = "sent"; 195 public static final String FOLDER_NAME_DELETED = "deleted"; 196 public static final String FOLDER_NAME_DRAFT = "draft"; 197 198 199 /** 200 * To push RFC2822 encoded messages into a folder and read RFC2822 encoded messages from 201 * a folder, the openFile() interface will be used as follows: 202 * Open a file descriptor to a message. 203 * Two modes supported for read: With and without attachments. 204 * One mode exist for write and the actual content will be with or without 205 * attachments. 206 * 207 * mode will be "r" for read and "w" for write, never "rw". 208 * 209 * URI format: 210 * The URI scheme is as follows. 211 * For reading messages with attachments: 212 * content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId 213 * Note: This shall be an offline operation, including only message parts and attachments 214 * already downloaded to the device. 215 * 216 * For reading messages without attachments: 217 * content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId/FILE_MSG_NO_ATTACHMENTS 218 * Note: This shall be an offline operation, including only message parts already 219 * downloaded to the device. 220 * 221 * For downloading and reading messages with attachments: 222 * content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId/FILE_MSG_DOWNLOAD 223 * Note: This shall download the message content and all attachments if possible, 224 * else throw an IOException. 225 * 226 * For downloading and reading messages without attachments: 227 * content://ProviderAuthority/account_id/TABLE_MESSAGE/msgId/FILE_MSG_DOWNLOAD_NO_ATTACHMENTS 228 * Note: This shall download the message content if possible, else throw an IOException. 229 * 230 * When reading from the file descriptor, the content provider shall return a stream 231 * of bytes containing a RFC2822 encoded message, as if the message was send to an email 232 * server. 233 * 234 * When a byte stream is written to the file descriptor, the content provider shall 235 * decode the RFC2822 encoded data and insert the message into the TABLE_MESSAGE at the ID 236 * supplied in URI - additionally the message content shall be stored in the underlying 237 * data base structure as if the message was received from an email server. The Message ID 238 * will be created using a insert on the TABLE_MESSAGE prior to calling openFile(). 239 * Hence the procedure for inserting a message is: 240 * - uri/msgId = insert(uri, value: folderId=xxx) 241 * - fd = openFile(uri/msgId) 242 * - fd.write (RFC2822 encoded data) 243 * 244 * The Bluetooth Message Access Client might not know what to put into the From: 245 * header nor have a valid time stamp, hence the content provider shall check 246 * if the From: and Date: headers have been set by the message written, else 247 * it must fill in appropriate values. 248 */ 249 public static final String FILE_MSG_NO_ATTACHMENTS = "NO_ATTACHMENTS"; 250 public static final String FILE_MSG_DOWNLOAD = "DOWNLOAD"; 251 public static final String FILE_MSG_DOWNLOAD_NO_ATTACHMENTS = "DOWNLOAD_NO_ATTACHMENTS"; 252 253 /** 254 * Account Table 255 * The columns needed to supply account information. 256 * The e-mail client app may choose to expose all e-mails as being from the same account, 257 * but it is not recommended, as this would be a violation of the Bluetooth specification. 258 * The Bluetooth Message Access settings activity will provide the user the ability to 259 * change the FLAG_EXPOSE values for each account in this table. 260 * The Bluetooth Message Access service will read the values when Bluetooth is turned on, 261 * and again on every notified change through the content observer interface. 262 */ 263 public interface AccountColumns { 264 265 /** 266 * The unique ID for a row. 267 * <P>Type: INTEGER (long)</P> 268 */ 269 public static final String _ID = "_id"; 270 271 /** 272 * The account name to display to the user on the device when selecting whether 273 * or not to share the account over Bluetooth. 274 * 275 * The account display name should not reveal any sensitive information e.g. email- 276 * address, as it will be added to the Bluetooth SDP record, which can be read by 277 * any Bluetooth enabled device. (Access to any account content is only provided to 278 * authenticated devices). It is recommended that if the email client uses the email 279 * address as account name, then the address should be obfuscated (i.e. replace "@" 280 * with ".") 281 * <P>Type: TEXT</P> 282 * read-only 283 */ 284 public static final String ACCOUNT_DISPLAY_NAME = "account_display_name"; 285 286 /** 287 * Expose this account to other authenticated Bluetooth devices. If the expose flag 288 * is set, this account will be listed as an available account to access from another 289 * Bluetooth device. 290 * 291 * This is a read/write flag, that can be set either from within the E-mail client 292 * UI or the Bluetooth settings menu. 293 * 294 * It is recommended to either ask the user whether to expose the account, or set this 295 * to "show" as default. 296 * 297 * This setting shall not be used to enforce whether or not an account should be shared 298 * or not if the account is bound by an administrative security policy. In this case 299 * the email app should not list the account at all if it is not to be shareable over BT. 300 * 301 * <P>Type: INTEGER (boolean) hide = 0, show = 1</P> 302 */ 303 public static final String FLAG_EXPOSE = "flag_expose"; 304 305 } 306 307 /** 308 * The actual message table containing all messages. 309 * Content that must support filtering using WHERE clauses: 310 * - To, From, Cc, Bcc, Date, ReadFlag, PriorityFlag, folder_id, account_id 311 * Additional content that must be supplied: 312 * - Subject, AttachmentFlag, LoadedState, MessageSize, AttachmentSize 313 * Content that must support update: 314 * - FLAG_READ and FOLDER_ID (FOLDER_ID is used to move a message to deleted) 315 * Additional insert of a new message with the following values shall be supported: 316 * - FOLDER_ID 317 * 318 * When doing an insert on this table, the actual content of the message (subject, 319 * date etc) written through file-i/o takes precedence over the inserted values and should 320 * overwrite them. 321 */ 322 public interface MessageColumns { 323 324 /** 325 * The unique ID for a row. 326 * <P>Type: INTEGER (long)</P> 327 */ 328 public static final String _ID = "_id"; 329 330 /** 331 * The date the message was received as a unix timestamp 332 * (miliseconds since 00:00:00 UTC 1/1-1970). 333 * 334 * <P>Type: INTEGER (long)</P> 335 * read-only 336 */ 337 public static final String DATE = "date"; 338 339 /** 340 * Message subject. 341 * <P>Type: TEXT</P> 342 * read-only. 343 */ 344 public static final String SUBJECT = "subject"; 345 346 /** 347 * Message Read flag 348 * <P>Type: INTEGER (boolean) unread = 0, read = 1</P> 349 * read/write 350 */ 351 public static final String FLAG_READ = "flag_read"; 352 353 /** 354 * Message Priority flag 355 * <P>Type: INTEGER (boolean) normal priority = 0, high priority = 1</P> 356 * read-only 357 */ 358 public static final String FLAG_HIGH_PRIORITY = "high_priority"; 359 360 /** 361 * Reception state - the amount of the message that have been loaded from the server. 362 * <P>Type: INTEGER see RECEPTION_STATE_ constants below </P> 363 * read-only 364 */ 365 public static final String RECEPTION_STATE = "reception_state"; 366 367 /** To be able to filter messages with attachments, we need this flag. 368 * <P>Type: INTEGER (boolean) no attachment = 0, attachment = 1 </P> 369 * read-only 370 */ 371 public static final String FLAG_ATTACHMENT = "flag_attachment"; 372 373 /** The overall size in bytes of the attachments of the message. 374 * <P>Type: INTEGER </P> 375 */ 376 public static final String ATTACHMENT_SIZE = "attachment_size"; 377 378 /** The overall size in bytes of the message including any attachments. 379 * This value is informative only and should be the size an email client 380 * would display as size for the message. 381 * <P>Type: INTEGER </P> 382 * read-only 383 */ 384 public static final String MESSAGE_SIZE = "message_size"; 385 386 /** Indicates that the message or a part of it is protected by a DRM scheme. 387 * <P>Type: INTEGER (boolean) no DRM = 0, DRM protected = 1 </P> 388 * read-only 389 */ 390 public static final String FLAG_PROTECTED = "flag_protected"; 391 392 /** 393 * A comma-delimited list of FROM addresses in RFC2822 format. 394 * The list must be compatible with Rfc822Tokenizer.tokenize(); 395 * <P>Type: TEXT</P> 396 * read-only 397 */ 398 public static final String FROM_LIST = "from_list"; 399 400 /** 401 * A comma-delimited list of TO addresses in RFC2822 format. 402 * The list must be compatible with Rfc822Tokenizer.tokenize(); 403 * <P>Type: TEXT</P> 404 * read-only 405 */ 406 public static final String TO_LIST = "to_list"; 407 408 /** 409 * A comma-delimited list of CC addresses in RFC2822 format. 410 * The list must be compatible with Rfc822Tokenizer.tokenize(); 411 * <P>Type: TEXT</P> 412 * read-only 413 */ 414 public static final String CC_LIST = "cc_list"; 415 416 /** 417 * A comma-delimited list of BCC addresses in RFC2822 format. 418 * The list must be compatible with Rfc822Tokenizer.tokenize(); 419 * <P>Type: TEXT</P> 420 * read-only 421 */ 422 public static final String BCC_LIST = "bcc_list"; 423 424 /** 425 * A comma-delimited list of REPLY-TO addresses in RFC2822 format. 426 * The list must be compatible with Rfc822Tokenizer.tokenize(); 427 * <P>Type: TEXT</P> 428 * read-only 429 */ 430 public static final String REPLY_TO_LIST = "reply_to_List"; 431 432 /** 433 * The unique ID for a row in the folder table in which this message belongs. 434 * <P>Type: INTEGER (long)</P> 435 * read/write 436 */ 437 public static final String FOLDER_ID = "folder_id"; 438 439 /** 440 * The unique ID for a row in the account table which owns this message. 441 * <P>Type: INTEGER (long)</P> 442 * read-only 443 */ 444 public static final String ACCOUNT_ID = "account_id"; 445 446 /** 447 * The ID identify the thread a message belongs to. If no thread id is available, 448 * set value to "-1" 449 * <P>Type: INTEGER (long)</P> 450 * read-only 451 */ 452 public static final String THREAD_ID = "thread_id"; 453 } 454 455 /** 456 * Indicates that the message, including any attachments, has been received from the 457 * server to the device. 458 */ 459 public static final String RECEPTION_STATE_COMPLETE = "complete"; 460 /** 461 * Indicates the message is partially received from the email server. 462 */ 463 public static final String RECEPTION_STATE_FRACTIONED = "fractioned"; 464 /** 465 * Indicates that only a notification about the message have been received. 466 */ 467 public static final String RECEPTION_STATE_NOTIFICATION = "notification"; 468 469 /** 470 * Message folder structure 471 * MAP enforces use of a folder structure with mandatory folders: 472 * - inbox, outbox, sent, deleted, draft 473 * User defined folders are supported. 474 * The folder table must provide filtering (use of WHERE clauses) of the following entries: 475 * - account_id (linking the folder to an e-mail account) 476 * - parent_id (linking the folders individually) 477 * The folder table must have a folder name for each entry, and the mandatory folders 478 * MUST exist for each account_id. The folders may be empty. 479 * Use the FOLDER_NAME_xxx constants for the mandatory folders. Their names must 480 * not be translated into other languages, as the folder browsing is string based, and 481 * many Bluetooth Message Clients will use these strings to navigate to the folders. 482 */ 483 public interface FolderColumns { 484 485 /** 486 * The unique ID for a row. 487 * <P>Type: INTEGER (long)</P> 488 * read-only 489 */ 490 public static final String _ID = "_id"; 491 492 /** 493 * The folder display name to present to the user. 494 * <P>Type: TEXT</P> 495 * read-only 496 */ 497 public static final String NAME = "name"; 498 499 /** 500 * The _id-key to the account this folder refers to. 501 * <P>Type: INTEGER (long)</P> 502 * read-only 503 */ 504 public static final String ACCOUNT_ID = "account_id"; 505 506 /** 507 * The _id-key to the parent folder. -1 for root folders. 508 * <P>Type: INTEGER (long)</P> 509 * read-only 510 */ 511 public static final String PARENT_FOLDER_ID = "parent_id"; 512 } 513 /** 514 * A projection of all the columns in the Message table 515 */ 516 public static final String[] BT_MESSAGE_PROJECTION = new String[] { 517 MessageColumns._ID, 518 MessageColumns.DATE, 519 MessageColumns.SUBJECT, 520 MessageColumns.FLAG_READ, 521 MessageColumns.FLAG_ATTACHMENT, 522 MessageColumns.FOLDER_ID, 523 MessageColumns.ACCOUNT_ID, 524 MessageColumns.FROM_LIST, 525 MessageColumns.TO_LIST, 526 MessageColumns.CC_LIST, 527 MessageColumns.BCC_LIST, 528 MessageColumns.REPLY_TO_LIST, 529 MessageColumns.FLAG_PROTECTED, 530 MessageColumns.FLAG_HIGH_PRIORITY, 531 MessageColumns.MESSAGE_SIZE, 532 MessageColumns.ATTACHMENT_SIZE, 533 MessageColumns.RECEPTION_STATE, 534 MessageColumns.THREAD_ID 535 }; 536 537 /** 538 * A projection of all the columns in the Account table 539 */ 540 public static final String[] BT_ACCOUNT_PROJECTION = new String[] { 541 AccountColumns._ID, 542 AccountColumns.ACCOUNT_DISPLAY_NAME, 543 AccountColumns.FLAG_EXPOSE, 544 }; 545 546 /** 547 * A projection of all the columns in the Folder table 548 */ 549 public static final String[] BT_FOLDER_PROJECTION = new String[] { 550 FolderColumns._ID, 551 FolderColumns.NAME, 552 FolderColumns.ACCOUNT_ID, 553 FolderColumns.PARENT_FOLDER_ID 554 }; 555 556 557 } 558