15a60e47497f21f64e6d79420dc4c56c1907df22akschulz/*
25a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Copyright (C) 2015 Samsung System LSI
35a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Licensed under the Apache License, Version 2.0 (the "License");
45a60e47497f21f64e6d79420dc4c56c1907df22akschulz* you may not use this file except in compliance with the License.
55a60e47497f21f64e6d79420dc4c56c1907df22akschulz* You may obtain a copy of the License at
65a60e47497f21f64e6d79420dc4c56c1907df22akschulz*
75a60e47497f21f64e6d79420dc4c56c1907df22akschulz*      http://www.apache.org/licenses/LICENSE-2.0
85a60e47497f21f64e6d79420dc4c56c1907df22akschulz*
95a60e47497f21f64e6d79420dc4c56c1907df22akschulz* Unless required by applicable law or agreed to in writing, software
105a60e47497f21f64e6d79420dc4c56c1907df22akschulz* distributed under the License is distributed on an "AS IS" BASIS,
115a60e47497f21f64e6d79420dc4c56c1907df22akschulz* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
125a60e47497f21f64e6d79420dc4c56c1907df22akschulz* See the License for the specific language governing permissions and
135a60e47497f21f64e6d79420dc4c56c1907df22akschulz* limitations under the License.
145a60e47497f21f64e6d79420dc4c56c1907df22akschulz*/
155a60e47497f21f64e6d79420dc4c56c1907df22akschulz
165a60e47497f21f64e6d79420dc4c56c1907df22akschulzpackage com.android.bluetooth.mapapi;
175a60e47497f21f64e6d79420dc4c56c1907df22akschulz
185a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.content.ContentProvider;
195a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.content.ContentResolver;
205a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.content.ContentValues;
215a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.content.Context;
225a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.content.UriMatcher;
235a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.content.pm.ProviderInfo;
245a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.database.Cursor;
255a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.net.Uri;
265a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.os.Binder;
275a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.os.Bundle;
285a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.util.Log;
295a60e47497f21f64e6d79420dc4c56c1907df22akschulz
305a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.List;
315a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Map;
325a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Map.Entry;
335a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Set;
345a60e47497f21f64e6d79420dc4c56c1907df22akschulz
355a60e47497f21f64e6d79420dc4c56c1907df22akschulz/**
365a60e47497f21f64e6d79420dc4c56c1907df22akschulz * A base implementation of the BluetoothMapContract.
375a60e47497f21f64e6d79420dc4c56c1907df22akschulz * A base class for a ContentProvider that allows access to Instant messages from a Bluetooth
385a60e47497f21f64e6d79420dc4c56c1907df22akschulz * device through the Message Access Profile.
395a60e47497f21f64e6d79420dc4c56c1907df22akschulz */
405a60e47497f21f64e6d79420dc4c56c1907df22akschulzpublic abstract class BluetoothMapIMProvider extends ContentProvider {
415a60e47497f21f64e6d79420dc4c56c1907df22akschulz
425a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private static final String TAG = "BluetoothMapIMProvider";
435a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private static final boolean D = true;
445a60e47497f21f64e6d79420dc4c56c1907df22akschulz
455a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private static final int MATCH_ACCOUNT = 1;
465a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private static final int MATCH_MESSAGE = 3;
475a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private static final int MATCH_CONVERSATION = 4;
485a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private static final int MATCH_CONVOCONTACT = 5;
495a60e47497f21f64e6d79420dc4c56c1907df22akschulz
505a60e47497f21f64e6d79420dc4c56c1907df22akschulz    protected ContentResolver mResolver;
515a60e47497f21f64e6d79420dc4c56c1907df22akschulz
525a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private Uri CONTENT_URI = null;
535a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private String mAuthority;
545a60e47497f21f64e6d79420dc4c56c1907df22akschulz    private UriMatcher mMatcher;
555a60e47497f21f64e6d79420dc4c56c1907df22akschulz
565a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
575a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return the CONTENT_URI exposed. This will be used to send out notifications.
585a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
59dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract Uri getContentUri();
605a60e47497f21f64e6d79420dc4c56c1907df22akschulz
615a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
625a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Implementation is provided by the parent class.
635a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
645a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
655a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public void attachInfo(Context context, ProviderInfo info) {
66c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        mAuthority = info.authority;
67c4fbd756e2645147470c486ae96f2253f5e13a52Jack He
68c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
69c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        mMatcher.addURI(mAuthority, BluetoothMapContract.TABLE_ACCOUNT, MATCH_ACCOUNT);
70c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_MESSAGE, MATCH_MESSAGE);
71c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_CONVERSATION,
72c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                MATCH_CONVERSATION);
73c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        mMatcher.addURI(mAuthority, "#/" + BluetoothMapContract.TABLE_CONVOCONTACT,
74c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                MATCH_CONVOCONTACT);
75c4fbd756e2645147470c486ae96f2253f5e13a52Jack He
76c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        // Sanity check our setup
77c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (!info.exported) {
78c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            throw new SecurityException("Provider must be exported");
79c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
80c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        // Enforce correct permissions are used
81c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (!android.Manifest.permission.BLUETOOTH_MAP.equals(info.writePermission)) {
82c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            throw new SecurityException(
83c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    "Provider must be protected by " + android.Manifest.permission.BLUETOOTH_MAP);
84c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
85c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (D) {
86c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            Log.d(TAG, "attachInfo() mAuthority = " + mAuthority);
87c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
88c4fbd756e2645147470c486ae96f2253f5e13a52Jack He
89c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        mResolver = context.getContentResolver();
90c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        super.attachInfo(context, info);
91c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    }
925a60e47497f21f64e6d79420dc4c56c1907df22akschulz
935a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
945a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This function shall be called when any Account database content have changed
955a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * to Notify any attached observers.
965a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the account that changed. Null is a valid value,
975a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        if accountId is unknown or multiple accounts changed.
985a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
995a60e47497f21f64e6d79420dc4c56c1907df22akschulz    protected void onAccountChanged(String accountId) {
1005a60e47497f21f64e6d79420dc4c56c1907df22akschulz        Uri newUri = null;
1015a60e47497f21f64e6d79420dc4c56c1907df22akschulz
102c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (mAuthority == null) {
1035a60e47497f21f64e6d79420dc4c56c1907df22akschulz            return;
1045a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
105c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (accountId == null) {
1065a60e47497f21f64e6d79420dc4c56c1907df22akschulz            newUri = BluetoothMapContract.buildAccountUri(mAuthority);
1075a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } else {
1085a60e47497f21f64e6d79420dc4c56c1907df22akschulz            newUri = BluetoothMapContract.buildAccountUriwithId(mAuthority, accountId);
1095a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
1105a60e47497f21f64e6d79420dc4c56c1907df22akschulz
111c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (D) {
112c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            Log.d(TAG, "onAccountChanged() accountId = " + accountId + " URI: " + newUri);
113c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
1145a60e47497f21f64e6d79420dc4c56c1907df22akschulz        mResolver.notifyChange(newUri, null);
1155a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
1165a60e47497f21f64e6d79420dc4c56c1907df22akschulz
1175a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
1185a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This function shall be called when any Message database content have changed
1195a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * to notify any attached observers.
1205a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId Null is a valid value, if accountId is unknown, but
1215a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        recommended for increased performance.
1225a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param messageId Null is a valid value, if multiple messages changed or the
1235a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        messageId is unknown, but recommended for increased performance.
1245a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
1255a60e47497f21f64e6d79420dc4c56c1907df22akschulz    protected void onMessageChanged(String accountId, String messageId) {
1265a60e47497f21f64e6d79420dc4c56c1907df22akschulz        Uri newUri = null;
1275a60e47497f21f64e6d79420dc4c56c1907df22akschulz
128c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (mAuthority == null) {
1295a60e47497f21f64e6d79420dc4c56c1907df22akschulz            return;
1305a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
131c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (accountId == null) {
1325a60e47497f21f64e6d79420dc4c56c1907df22akschulz            newUri = BluetoothMapContract.buildMessageUri(mAuthority);
1335a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } else {
134c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (messageId == null) {
135c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                newUri = BluetoothMapContract.buildMessageUri(mAuthority, accountId);
1365a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else {
137c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                newUri = BluetoothMapContract.buildMessageUriWithId(mAuthority, accountId,
1385a60e47497f21f64e6d79420dc4c56c1907df22akschulz                        messageId);
1395a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
1405a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
141c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (D) {
142c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            Log.d(TAG, "onMessageChanged() accountId = " + accountId + " messageId = " + messageId
143c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    + " URI: " + newUri);
144c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
1455a60e47497f21f64e6d79420dc4c56c1907df22akschulz        mResolver.notifyChange(newUri, null);
1465a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
1475a60e47497f21f64e6d79420dc4c56c1907df22akschulz
1485a60e47497f21f64e6d79420dc4c56c1907df22akschulz
1495a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
1505a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This function shall be called when any Message database content have changed
1515a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * to notify any attached observers.
1525a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId Null is a valid value, if accountId is unknown, but
1535a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        recommended for increased performance.
1545a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param contactId Null is a valid value, if multiple contacts changed or the
1555a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        contactId is unknown, but recommended for increased performance.
1565a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
1575a60e47497f21f64e6d79420dc4c56c1907df22akschulz    protected void onContactChanged(String accountId, String contactId) {
1585a60e47497f21f64e6d79420dc4c56c1907df22akschulz        Uri newUri = null;
1595a60e47497f21f64e6d79420dc4c56c1907df22akschulz
160c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (mAuthority == null) {
1615a60e47497f21f64e6d79420dc4c56c1907df22akschulz            return;
1625a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
163c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (accountId == null) {
1645a60e47497f21f64e6d79420dc4c56c1907df22akschulz            newUri = BluetoothMapContract.buildConvoContactsUri(mAuthority);
1655a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } else {
166c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (contactId == null) {
167c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                newUri = BluetoothMapContract.buildConvoContactsUri(mAuthority, accountId);
1685a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else {
1695a60e47497f21f64e6d79420dc4c56c1907df22akschulz                newUri = BluetoothMapContract.buildConvoContactsUriWithId(mAuthority, accountId,
1705a60e47497f21f64e6d79420dc4c56c1907df22akschulz                        contactId);
1715a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
1725a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
173c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (D) {
174c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            Log.d(TAG, "onContactChanged() accountId = " + accountId + " contactId = " + contactId
175c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    + " URI: " + newUri);
176c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
1775a60e47497f21f64e6d79420dc4c56c1907df22akschulz        mResolver.notifyChange(newUri, null);
1785a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
1795a60e47497f21f64e6d79420dc4c56c1907df22akschulz
1805a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
1815a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Not used, this is just a dummy implementation.
1825a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * TODO: We might need to something intelligent here after introducing IM
1835a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
1845a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
1855a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public String getType(Uri uri) {
1865a60e47497f21f64e6d79420dc4c56c1907df22akschulz        return "InstantMessage";
1875a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
1885a60e47497f21f64e6d79420dc4c56c1907df22akschulz
1895a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
1905a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * The MAP specification states that a delete request from MAP client is a folder shift to the
1915a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * 'deleted' folder.
1925a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Only use case of delete() is when transparency is requested for push messages, then
1935a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * message should not remain in sent folder and therefore must be deleted
1945a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
1955a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
1965a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public int delete(Uri uri, String where, String[] selectionArgs) {
197c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (D) {
198c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            Log.d(TAG, "delete(): uri=" + uri.toString());
199c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
2005a60e47497f21f64e6d79420dc4c56c1907df22akschulz        int result = 0;
2015a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2025a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String table = uri.getPathSegments().get(1);
203c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (table == null) {
2045a60e47497f21f64e6d79420dc4c56c1907df22akschulz            throw new IllegalArgumentException("Table missing in URI");
205c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
2065a60e47497f21f64e6d79420dc4c56c1907df22akschulz        // the id of the entry to be deleted from the database
2075a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String messageId = uri.getLastPathSegment();
208c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (messageId == null) {
2095a60e47497f21f64e6d79420dc4c56c1907df22akschulz            throw new IllegalArgumentException("Message ID missing in update values!");
210c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
2115a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2125a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String accountId = getAccountId(uri);
213c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (accountId == null) {
2145a60e47497f21f64e6d79420dc4c56c1907df22akschulz            throw new IllegalArgumentException("Account ID missing in update values!");
215c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
2165a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2175a60e47497f21f64e6d79420dc4c56c1907df22akschulz        long callingId = Binder.clearCallingIdentity();
2185a60e47497f21f64e6d79420dc4c56c1907df22akschulz        try {
219c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) {
2205a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return deleteMessage(accountId, messageId);
2215a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else {
222c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (D) {
223c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    Log.w(TAG, "Unknown table name: " + table);
224c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
2255a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return result;
2265a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
2275a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } finally {
2285a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Binder.restoreCallingIdentity(callingId);
2295a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
2305a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
2315a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2325a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
2335a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This function deletes a message.
2345a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the Account
2355a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param messageId the ID of the message to delete.
2365a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return the number of messages deleted - 0 if the message was not found.
2375a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
238dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract int deleteMessage(String accountId, String messageId);
2395a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2405a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
2415a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Insert is used to add new messages to the data base.
2425a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Insert message approach:
2435a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *   - Insert an empty message to get an _id with only a folder_id
2445a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *   - Open the _id for write
2455a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *   - Write the message content
2465a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *     (When the writer completes, this provider should do an update of the message)
2475a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
2485a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
2495a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public Uri insert(Uri uri, ContentValues values) {
2505a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String table = uri.getLastPathSegment();
251c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (table == null) {
2525a60e47497f21f64e6d79420dc4c56c1907df22akschulz            throw new IllegalArgumentException("Table missing in URI");
253c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
2545a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2555a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String accountId = getAccountId(uri);
256c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (accountId == null) {
2575a60e47497f21f64e6d79420dc4c56c1907df22akschulz            throw new IllegalArgumentException("Account ID missing in URI");
258c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
2595a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2605a60e47497f21f64e6d79420dc4c56c1907df22akschulz        // TODO: validate values?
2615a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2625a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String id; // the id of the entry inserted into the database
2635a60e47497f21f64e6d79420dc4c56c1907df22akschulz        long callingId = Binder.clearCallingIdentity();
264c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        Log.d(TAG, "insert(): uri=" + uri.toString() + " - getLastPathSegment() = "
265c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                + uri.getLastPathSegment());
2665a60e47497f21f64e6d79420dc4c56c1907df22akschulz        try {
267c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) {
2685a60e47497f21f64e6d79420dc4c56c1907df22akschulz                id = insertMessage(accountId, values);
269c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (D) {
270c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    Log.i(TAG, "insert() ID: " + id);
271c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
2725a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return Uri.parse(uri.toString() + "/" + id);
2735a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else {
2745a60e47497f21f64e6d79420dc4c56c1907df22akschulz                Log.w(TAG, "Unknown table name: " + table);
2755a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return null;
2765a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
2775a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } finally {
2785a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Binder.restoreCallingIdentity(callingId);
2795a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
2805a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
2815a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2825a60e47497f21f64e6d79420dc4c56c1907df22akschulz
2835a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
2845a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Inserts an empty message into the Message data base in the specified folder.
2855a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This is done before the actual message content is written by fileIO.
2865a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the account
2875a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param folderId the ID of the folder to create a new message in.
2885a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return the message id as a string
2895a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
290dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract String insertMessage(String accountId, ContentValues values);
2915a60e47497f21f64e6d79420dc4c56c1907df22akschulz
292c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    /**
2935a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Utility function to build a projection based on a projectionMap.
2945a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *
2955a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *   "btColumnName" -> "imColumnName as btColumnName" for each entry.
2965a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *
2975a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This supports SQL statements in the column name entry.
2985a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param projection
2995a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param projectionMap <string, string>
3005a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return the converted projection
3015a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
302c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    protected String[] convertProjection(String[] projection, Map<String, String> projectionMap) {
3035a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String[] newProjection = new String[projection.length];
304c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        for (int i = 0; i < projection.length; i++) {
3055a60e47497f21f64e6d79420dc4c56c1907df22akschulz            newProjection[i] = projectionMap.get(projection[i]) + " as " + projection[i];
3065a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
3075a60e47497f21f64e6d79420dc4c56c1907df22akschulz        return newProjection;
3085a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
3095a60e47497f21f64e6d79420dc4c56c1907df22akschulz
3105a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
3115a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This query needs to map from the data used in the e-mail client to
3125a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * BluetoothMapContract type of data.
3135a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
3145a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
3155a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
3165a60e47497f21f64e6d79420dc4c56c1907df22akschulz            String sortOrder) {
3175a60e47497f21f64e6d79420dc4c56c1907df22akschulz        long callingId = Binder.clearCallingIdentity();
3185a60e47497f21f64e6d79420dc4c56c1907df22akschulz        try {
3195a60e47497f21f64e6d79420dc4c56c1907df22akschulz            String accountId = null;
320c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (D) {
321c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                Log.w(TAG, "query(): uri =" + mAuthority + " uri=" + uri.toString());
322c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            }
3235a60e47497f21f64e6d79420dc4c56c1907df22akschulz
3245a60e47497f21f64e6d79420dc4c56c1907df22akschulz            switch (mMatcher.match(uri)) {
3255a60e47497f21f64e6d79420dc4c56c1907df22akschulz                case MATCH_ACCOUNT:
3265a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    return queryAccount(projection, selection, selectionArgs, sortOrder);
3275a60e47497f21f64e6d79420dc4c56c1907df22akschulz                case MATCH_MESSAGE:
3285a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    // TODO: Extract account from URI
3295a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    accountId = getAccountId(uri);
3305a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    return queryMessage(accountId, projection, selection, selectionArgs, sortOrder);
3315a60e47497f21f64e6d79420dc4c56c1907df22akschulz                case MATCH_CONVERSATION:
3325a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    accountId = getAccountId(uri);
3335a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    String value;
3345a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    String searchString =
3355a60e47497f21f64e6d79420dc4c56c1907df22akschulz                            uri.getQueryParameter(BluetoothMapContract.FILTER_ORIGINATOR_SUBSTRING);
3365a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    Long periodBegin = null;
3375a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    value = uri.getQueryParameter(BluetoothMapContract.FILTER_PERIOD_BEGIN);
338c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    if (value != null) {
3395a60e47497f21f64e6d79420dc4c56c1907df22akschulz                        periodBegin = Long.parseLong(value);
3405a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    }
3415a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    Long periodEnd = null;
3425a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    value = uri.getQueryParameter(BluetoothMapContract.FILTER_PERIOD_END);
343c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    if (value != null) {
3445a60e47497f21f64e6d79420dc4c56c1907df22akschulz                        periodEnd = Long.parseLong(value);
3455a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    }
3465a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    Boolean read = null;
3475a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    value = uri.getQueryParameter(BluetoothMapContract.FILTER_READ_STATUS);
348c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    if (value != null) {
3495a60e47497f21f64e6d79420dc4c56c1907df22akschulz                        read = value.equalsIgnoreCase("true");
3505a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    }
3515a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    Long threadId = null;
3525a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    value = uri.getQueryParameter(BluetoothMapContract.FILTER_THREAD_ID);
353c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    if (value != null) {
3545a60e47497f21f64e6d79420dc4c56c1907df22akschulz                        threadId = Long.parseLong(value);
3555a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    }
3565a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    return queryConversation(accountId, threadId, read, periodEnd, periodBegin,
3575a60e47497f21f64e6d79420dc4c56c1907df22akschulz                            searchString, projection, sortOrder);
3585a60e47497f21f64e6d79420dc4c56c1907df22akschulz                case MATCH_CONVOCONTACT:
3595a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    accountId = getAccountId(uri);
3605a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    long contactId = 0;
361c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    return queryConvoContact(accountId, contactId, projection, selection,
362c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                            selectionArgs, sortOrder);
3635a60e47497f21f64e6d79420dc4c56c1907df22akschulz                default:
3645a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    throw new UnsupportedOperationException("Unsupported Uri " + uri);
3655a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
3665a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } finally {
3675a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Binder.restoreCallingIdentity(callingId);
3685a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
3695a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
3705a60e47497f21f64e6d79420dc4c56c1907df22akschulz
3715a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
3725a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Query account information.
3735a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * This function shall return only exposable e-mail accounts. Hence shall not
3745a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * return accounts that has policies suggesting not to be shared them.
3755a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param projection
3765a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selection
3775a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selectionArgs
3785a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param sortOrder
3795a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return a cursor to the accounts that are subject to exposure over BT.
3805a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
381dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract Cursor queryAccount(String[] projection, String selection,
3825a60e47497f21f64e6d79420dc4c56c1907df22akschulz            String[] selectionArgs, String sortOrder);
3835a60e47497f21f64e6d79420dc4c56c1907df22akschulz
3845a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
3855a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * For the message table the selection (where clause) can only include the following columns:
3865a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    date: less than, greater than and equals
3875a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    flagRead: = 1 or = 0
3885a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    flagPriority: = 1 or = 0
3895a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    folder_id: the ID of the folder only equals
3905a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    toList: partial name/address search
3915a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    fromList: partial name/address search
3925a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Additionally the COUNT and OFFSET shall be supported.
3935a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the account
3945a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param projection
3955a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selection
3965a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selectionArgs
3975a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param sortOrder
3985a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return a cursor to query result
3995a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
400dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract Cursor queryMessage(String accountId, String[] projection, String selection,
4015a60e47497f21f64e6d79420dc4c56c1907df22akschulz            String[] selectionArgs, String sortOrder);
4025a60e47497f21f64e6d79420dc4c56c1907df22akschulz
4035a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
4045a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * For the Conversation table the selection (where clause) can only include
4055a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * the following columns:
4065a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    _id: the ID of the conversation only equals
4075a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    name: partial name search
4085a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    last_activity: less than, greater than and equals
4095a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    version_counter: updated IDs are regenerated
4105a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Additionally the COUNT and OFFSET shall be supported.
4115a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the account
4125a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param threadId the ID of the conversation
4135a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param projection
4145a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selection
4155a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selectionArgs
4165a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param sortOrder
4175a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return a cursor to query result
4185a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
4195a60e47497f21f64e6d79420dc4c56c1907df22akschulz//    abstract protected Cursor queryConversation(Long threadId, String[] projection,
4205a60e47497f21f64e6d79420dc4c56c1907df22akschulz//    String selection, String[] selectionArgs, String sortOrder);
4215a60e47497f21f64e6d79420dc4c56c1907df22akschulz
4225a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
4235a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Query for conversations with contact information. The expected result is a cursor pointing
4245a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * to one row for each contact in a conversation.
4255a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * E.g.:
4265a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * ThreadId | ThreadName | ... | ContactName | ContactPrecence | ... |
4275a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        1 |  "Bowling" | ... |        Hans |               1 | ... |
4285a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        1 |  "Bowling" | ... |       Peter |               2 | ... |
4295a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        2 |         "" | ... |       Peter |               2 | ... |
4305a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *        3 |         "" | ... |        Hans |               1 | ... |
4315a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *
432c4fbd756e2645147470c486ae96f2253f5e13a52Jack He     * @param accountId the ID of the account
4335a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param threadId filter on a single threadId - null if no filtering is needed.
4345a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param read filter on a read status:
4355a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *             null: no filtering on read is needed.
4365a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *             true: return only threads that has NO unread messages.
4375a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *             false: return only threads that has unread messages.
4385a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param periodEnd   last_activity time stamp of the the newest thread to include in the
4395a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *                    result.
4405a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param periodBegin last_activity time stamp of the the oldest thread to include in the
4415a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *                    result.
4425a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param searchString if not null, include only threads that has contacts that matches the
4435a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *                     searchString as part of the contact name or nickName.
4445a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param projection A list of the columns that is needed in the result
4455a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param sortOrder  the sort order
4465a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return a Cursor representing the query result.
4475a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
448dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract Cursor queryConversation(String accountId, Long threadId, Boolean read,
4495a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Long periodEnd, Long periodBegin, String searchString, String[] projection,
4505a60e47497f21f64e6d79420dc4c56c1907df22akschulz            String sortOrder);
4515a60e47497f21f64e6d79420dc4c56c1907df22akschulz
4525a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
4535a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * For the ConvoContact table the selection (where clause) can only include the
4545a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * following columns:
4555a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    _id: the ID of the contact only equals
4565a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    convo_id: id of conversation contact is part of
4575a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    name: partial name search
4585a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    x_bt_uid: the ID of the bt uid only equals
4595a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    chat_state: active, inactive, gone, composing, paused
4605a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    last_active: less than, greater than and equals
4615a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    presence_state: online, do_not_disturb, away, offline
4625a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    priority: level of priority 0 - 100
4635a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *    last_online: less than, greater than and equals
4645a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the account
4655a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param contactId the ID of the contact
4665a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param projection
4675a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selection
4685a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param selectionArgs
4695a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param sortOrder
4705a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return a cursor to query result
4715a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
472dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract Cursor queryConvoContact(String accountId, Long contactId,
4735a60e47497f21f64e6d79420dc4c56c1907df22akschulz            String[] projection, String selection, String[] selectionArgs, String sortOrder);
4745a60e47497f21f64e6d79420dc4c56c1907df22akschulz
4755a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
4765a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * update()
4775a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Messages can be modified in the following cases:
4785a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the folder_key of a message - hence the message can be moved to a new folder,
4795a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *                                  but the content cannot be modified.
4805a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the FLAG_READ state can be changed.
4815a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Conversations can be modified in the following cases:
4825a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the read status - changing between read, unread
4835a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the last activity - the time stamp of last message sent of received in the conversation
4845a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * ConvoContacts can be modified in the following cases:
4855a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the chat_state - chat status of the contact in conversation
4865a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the last_active - the time stamp of last action in the conversation
4875a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the presence_state - the time stamp of last time contact online
4885a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the status - the status text of the contact available in a conversation
4895a60e47497f21f64e6d79420dc4c56c1907df22akschulz     *  - the last_online - the time stamp of last time contact online
4905a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * The selection statement will always be selection of a message ID, when updating a message,
4915a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * hence this function will be called multiple times if multiple messages must be updated
4925a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * due to the nature of the Bluetooth Message Access profile.
4935a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
4945a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
4955a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
4965a60e47497f21f64e6d79420dc4c56c1907df22akschulz
4975a60e47497f21f64e6d79420dc4c56c1907df22akschulz        String table = uri.getLastPathSegment();
498c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (table == null) {
4995a60e47497f21f64e6d79420dc4c56c1907df22akschulz            throw new IllegalArgumentException("Table missing in URI");
5005a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
501c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (selection != null) {
502c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            throw new IllegalArgumentException(
503c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    "selection shall not be used, ContentValues " + "shall contain the data");
5045a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
5055a60e47497f21f64e6d79420dc4c56c1907df22akschulz
5065a60e47497f21f64e6d79420dc4c56c1907df22akschulz        long callingId = Binder.clearCallingIdentity();
507c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (D) {
508c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            Log.w(TAG, "update(): uri=" + uri.toString() + " - getLastPathSegment() = "
509c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    + uri.getLastPathSegment());
510c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
5115a60e47497f21f64e6d79420dc4c56c1907df22akschulz        try {
512c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (table.equals(BluetoothMapContract.TABLE_ACCOUNT)) {
5135a60e47497f21f64e6d79420dc4c56c1907df22akschulz                String accountId = values.getAsString(BluetoothMapContract.AccountColumns._ID);
514c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (accountId == null) {
5155a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    throw new IllegalArgumentException("Account ID missing in update values!");
5165a60e47497f21f64e6d79420dc4c56c1907df22akschulz                }
517c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                Integer exposeFlag =
518c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                        values.getAsInteger(BluetoothMapContract.AccountColumns.FLAG_EXPOSE);
519c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (exposeFlag == null) {
5205a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    throw new IllegalArgumentException("Expose flag missing in update values!");
5215a60e47497f21f64e6d79420dc4c56c1907df22akschulz                }
5225a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return updateAccount(accountId, exposeFlag);
523c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (table.equals(BluetoothMapContract.TABLE_FOLDER)) {
5245a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return 0; // We do not support changing folders
525c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (table.equals(BluetoothMapContract.TABLE_MESSAGE)) {
5265a60e47497f21f64e6d79420dc4c56c1907df22akschulz                String accountId = getAccountId(uri);
527c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (accountId == null) {
5285a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    throw new IllegalArgumentException("Account ID missing in update values!");
5295a60e47497f21f64e6d79420dc4c56c1907df22akschulz                }
5305a60e47497f21f64e6d79420dc4c56c1907df22akschulz                Long messageId = values.getAsLong(BluetoothMapContract.MessageColumns._ID);
531c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (messageId == null) {
5325a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    throw new IllegalArgumentException("Message ID missing in update values!");
5335a60e47497f21f64e6d79420dc4c56c1907df22akschulz                }
5345a60e47497f21f64e6d79420dc4c56c1907df22akschulz                Long folderId = values.getAsLong(BluetoothMapContract.MessageColumns.FOLDER_ID);
535c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                Boolean flagRead =
536c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                        values.getAsBoolean(BluetoothMapContract.MessageColumns.FLAG_READ);
5375a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return updateMessage(accountId, messageId, folderId, flagRead);
538c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (table.equals(BluetoothMapContract.TABLE_CONVERSATION)) {
5395a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return 0; // We do not support changing conversation
540c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (table.equals(BluetoothMapContract.TABLE_CONVOCONTACT)) {
5415a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return 0; // We do not support changing contacts
5425a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else {
543c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (D) {
544c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    Log.w(TAG, "Unknown table name: " + table);
545c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                }
5465a60e47497f21f64e6d79420dc4c56c1907df22akschulz                return 0;
5475a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
5485a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } finally {
5495a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Binder.restoreCallingIdentity(callingId);
5505a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
5515a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
5525a60e47497f21f64e6d79420dc4c56c1907df22akschulz
5535a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
5545a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Update an entry in the account table. Only the expose flag will be
5555a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * changed through this interface.
5565a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the account to change.
5575a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param flagExpose the updated value.
5585a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return the number of entries changed - 0 if account not found or value cannot be changed.
5595a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
560dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract int updateAccount(String accountId, Integer flagExpose);
5615a60e47497f21f64e6d79420dc4c56c1907df22akschulz
5625a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
5635a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Update an entry in the message table.
5645a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId ID of the account to which the messageId relates
5655a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param messageId the ID of the message to update
5665a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param folderId the new folder ID value to set - ignore if null.
5675a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param flagRead the new flagRead value to set - ignore if null.
5685a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return
5695a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
570dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract int updateMessage(String accountId, Long messageId, Long folderId,
5715a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Boolean flagRead);
5725a60e47497f21f64e6d79420dc4c56c1907df22akschulz
5735a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
5745a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Utility function to Creates a ContentValues object based on a modified valuesSet.
5755a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * To be used after changing the keys and optionally values of a valueSet obtained
5765a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * from a ContentValues object received in update().
5775a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param valueSet the values as received in the contentProvider
5785a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param keyMap the key map <btKey, emailKey>
5795a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return a new ContentValues object with the keys replaced as specified in the
5805a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * keyMap
5815a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
582c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    protected ContentValues createContentValues(Set<Entry<String, Object>> valueSet,
5835a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Map<String, String> keyMap) {
5845a60e47497f21f64e6d79420dc4c56c1907df22akschulz        ContentValues values = new ContentValues(valueSet.size());
585c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        for (Entry<String, Object> ent : valueSet) {
5865a60e47497f21f64e6d79420dc4c56c1907df22akschulz            String key = keyMap.get(ent.getKey()); // Convert the key name
5875a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Object value = ent.getValue();
588c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (value == null) {
5895a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.putNull(key);
590c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof Boolean) {
5915a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (Boolean) value);
592c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof Byte) {
5935a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (Byte) value);
594c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof byte[]) {
5955a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (byte[]) value);
596c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof Double) {
5975a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (Double) value);
598c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof Float) {
5995a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (Float) value);
600c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof Integer) {
6015a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (Integer) value);
602c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof Long) {
6035a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (Long) value);
604c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof Short) {
6055a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (Short) value);
606c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            } else if (ent.getValue() instanceof String) {
6075a60e47497f21f64e6d79420dc4c56c1907df22akschulz                values.put(key, (String) value);
6085a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else {
6095a60e47497f21f64e6d79420dc4c56c1907df22akschulz                throw new IllegalArgumentException("Unknown data type in content value");
6105a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
6115a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
6125a60e47497f21f64e6d79420dc4c56c1907df22akschulz        return values;
6135a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
6145a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6155a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
6165a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public Bundle call(String method, String arg, Bundle extras) {
6175a60e47497f21f64e6d79420dc4c56c1907df22akschulz        long callingId = Binder.clearCallingIdentity();
618c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (D) {
619c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            Log.w(TAG, "call(): method=" + method + " arg=" + arg + "ThreadId: "
620c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                    + Thread.currentThread().getId());
621c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        }
6225a60e47497f21f64e6d79420dc4c56c1907df22akschulz        int ret = -1;
6235a60e47497f21f64e6d79420dc4c56c1907df22akschulz        try {
624c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            if (method.equals(BluetoothMapContract.METHOD_UPDATE_FOLDER)) {
6255a60e47497f21f64e6d79420dc4c56c1907df22akschulz                long accountId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_ACCOUNT_ID, -1);
626c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (accountId == -1) {
6275a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    Log.w(TAG, "No account ID in CALL");
6285a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    return null;
6295a60e47497f21f64e6d79420dc4c56c1907df22akschulz                }
6305a60e47497f21f64e6d79420dc4c56c1907df22akschulz                long folderId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_FOLDER_ID, -1);
631c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                if (folderId == -1) {
6325a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    Log.w(TAG, "No folder ID in CALL");
6335a60e47497f21f64e6d79420dc4c56c1907df22akschulz                    return null;
6345a60e47497f21f64e6d79420dc4c56c1907df22akschulz                }
6355a60e47497f21f64e6d79420dc4c56c1907df22akschulz                ret = syncFolder(accountId, folderId);
6365a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else if (method.equals(BluetoothMapContract.METHOD_SET_OWNER_STATUS)) {
6375a60e47497f21f64e6d79420dc4c56c1907df22akschulz                int presenceState = extras.getInt(BluetoothMapContract.EXTRA_PRESENCE_STATE);
638c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                String presenceStatus =
639c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                        extras.getString(BluetoothMapContract.EXTRA_PRESENCE_STATUS);
6405a60e47497f21f64e6d79420dc4c56c1907df22akschulz                long lastActive = extras.getLong(BluetoothMapContract.EXTRA_LAST_ACTIVE);
6415a60e47497f21f64e6d79420dc4c56c1907df22akschulz                int chatState = extras.getInt(BluetoothMapContract.EXTRA_CHAT_STATE);
6425a60e47497f21f64e6d79420dc4c56c1907df22akschulz                String convoId = extras.getString(BluetoothMapContract.EXTRA_CONVERSATION_ID);
6435a60e47497f21f64e6d79420dc4c56c1907df22akschulz                ret = setOwnerStatus(presenceState, presenceStatus, lastActive, chatState, convoId);
6445a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6455a60e47497f21f64e6d79420dc4c56c1907df22akschulz            } else if (method.equals(BluetoothMapContract.METHOD_SET_BLUETOOTH_STATE)) {
646c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                boolean bluetoothState =
647c4fbd756e2645147470c486ae96f2253f5e13a52Jack He                        extras.getBoolean(BluetoothMapContract.EXTRA_BLUETOOTH_STATE);
6485a60e47497f21f64e6d79420dc4c56c1907df22akschulz                ret = setBluetoothStatus(bluetoothState);
6495a60e47497f21f64e6d79420dc4c56c1907df22akschulz            }
6505a60e47497f21f64e6d79420dc4c56c1907df22akschulz        } finally {
6515a60e47497f21f64e6d79420dc4c56c1907df22akschulz            Binder.restoreCallingIdentity(callingId);
6525a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
653c4fbd756e2645147470c486ae96f2253f5e13a52Jack He        if (ret == 0) {
6545a60e47497f21f64e6d79420dc4c56c1907df22akschulz            return new Bundle();
6555a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
6565a60e47497f21f64e6d79420dc4c56c1907df22akschulz        return null;
6575a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
6585a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6595a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
6605a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Trigger a sync of the specified folder.
6615a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param accountId the ID of the account that owns the folder
6625a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param folderId the ID of the folder.
6635a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return 0 at success
6645a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
665dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract int syncFolder(long accountId, long folderId);
6665a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6675a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
6685a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Set the properties that should change presence or chat state of owner
6695a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * e.g. when the owner is active on a BT client device but not on the BT server device
6705a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * where the IM application is installed, it should still be possible to show an active status.
6715a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param presenceState should follow the contract specified values
6725a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param presenceStatus string the owners current status
6735a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param lastActive time stamp of the owners last activity
6745a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param chatState should follow the contract specified values
6755a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param convoId ID to the conversation to change
6765a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return 0 at success
6775a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
678c4fbd756e2645147470c486ae96f2253f5e13a52Jack He    protected abstract int setOwnerStatus(int presenceState, String presenceStatus, long lastActive,
679c4fbd756e2645147470c486ae96f2253f5e13a52Jack He            int chatState, String convoId);
6805a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6815a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
6825a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Notify the application of the Bluetooth state
6835a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @param bluetoothState 'on' of 'off'
6845a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * @return 0 at success
6855a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
686dbf5c4e1c9e57fa6e5cc96ee9f442da8aa391a5dJack He    protected abstract int setBluetoothStatus(boolean bluetoothState);
6875a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6885a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6895a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
6905a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Need this to suppress warning in unit tests.
6915a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
6925a60e47497f21f64e6d79420dc4c56c1907df22akschulz    @Override
6935a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public void shutdown() {
6945a60e47497f21f64e6d79420dc4c56c1907df22akschulz        // Don't call super.shutdown(), which emits a warning...
6955a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
6965a60e47497f21f64e6d79420dc4c56c1907df22akschulz
6975a60e47497f21f64e6d79420dc4c56c1907df22akschulz    /**
6985a60e47497f21f64e6d79420dc4c56c1907df22akschulz     * Extract the BluetoothMapContract.AccountColumns._ID from the given URI.
6995a60e47497f21f64e6d79420dc4c56c1907df22akschulz     */
7005a60e47497f21f64e6d79420dc4c56c1907df22akschulz    public static String getAccountId(Uri uri) {
7015a60e47497f21f64e6d79420dc4c56c1907df22akschulz        final List<String> segments = uri.getPathSegments();
7025a60e47497f21f64e6d79420dc4c56c1907df22akschulz        if (segments.size() < 1) {
7035a60e47497f21f64e6d79420dc4c56c1907df22akschulz            throw new IllegalArgumentException("No AccountId pressent in URI: " + uri);
7045a60e47497f21f64e6d79420dc4c56c1907df22akschulz        }
7055a60e47497f21f64e6d79420dc4c56c1907df22akschulz        return segments.get(0);
7065a60e47497f21f64e6d79420dc4c56c1907df22akschulz    }
7075a60e47497f21f64e6d79420dc4c56c1907df22akschulz}
708