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
17package com.android.bluetooth.mapapi;
18
19import android.content.ContentResolver;
20import 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 */
57public final class BluetoothMapContract {
58    /**
59     * Constructor - should not be used
60     */
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     */
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     */
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     */
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     */
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     */
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     */
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