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">&lt;provider  android:authorities="[PROVIDER AUTHORITY]"
44               android:exported="true"
45               android:enabled="true"
46               android:permission="android.permission.BLUETOOTH_MAP"&gt;
47  *   ...
48  *      &lt;intent-filter&gt;
49            &lt;action android:name="android.content.action.BLEUETOOT_MAP_PROVIDER" /&gt;
50         &lt;/intent-filter&gt;
51  *   ...
52  *   &lt;/provider&gt;
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