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.AsyncTask; 275a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.os.Binder; 285a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.os.Bundle; 295a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.os.ParcelFileDescriptor; 305a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport android.util.Log; 315a60e47497f21f64e6d79420dc4c56c1907df22akschulz 325a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.FileInputStream; 335a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.FileNotFoundException; 345a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.FileOutputStream; 355a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.io.IOException; 365a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.List; 375a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Map; 385a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Map.Entry; 395a60e47497f21f64e6d79420dc4c56c1907df22akschulzimport java.util.Set; 405a60e47497f21f64e6d79420dc4c56c1907df22akschulz 415a60e47497f21f64e6d79420dc4c56c1907df22akschulz/** 425a60e47497f21f64e6d79420dc4c56c1907df22akschulz * A base implementation of the BluetoothMapContract. 435a60e47497f21f64e6d79420dc4c56c1907df22akschulz * A base class for a ContentProvider that allows access to Instant messages from a Bluetooth 445a60e47497f21f64e6d79420dc4c56c1907df22akschulz * device through the Message Access Profile. 455a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 465a60e47497f21f64e6d79420dc4c56c1907df22akschulzpublic abstract class BluetoothMapIMProvider extends ContentProvider { 475a60e47497f21f64e6d79420dc4c56c1907df22akschulz 485a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final String TAG = "BluetoothMapIMProvider"; 495a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final boolean D = true; 505a60e47497f21f64e6d79420dc4c56c1907df22akschulz 515a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int MATCH_ACCOUNT = 1; 525a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int MATCH_MESSAGE = 3; 535a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int MATCH_CONVERSATION = 4; 545a60e47497f21f64e6d79420dc4c56c1907df22akschulz private static final int MATCH_CONVOCONTACT = 5; 555a60e47497f21f64e6d79420dc4c56c1907df22akschulz 565a60e47497f21f64e6d79420dc4c56c1907df22akschulz protected ContentResolver mResolver; 575a60e47497f21f64e6d79420dc4c56c1907df22akschulz 585a60e47497f21f64e6d79420dc4c56c1907df22akschulz private Uri CONTENT_URI = null; 595a60e47497f21f64e6d79420dc4c56c1907df22akschulz private String mAuthority; 605a60e47497f21f64e6d79420dc4c56c1907df22akschulz private UriMatcher mMatcher; 615a60e47497f21f64e6d79420dc4c56c1907df22akschulz 625a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 635a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the CONTENT_URI exposed. This will be used to send out notifications. 645a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 655a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected Uri getContentUri(); 665a60e47497f21f64e6d79420dc4c56c1907df22akschulz 675a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 685a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Implementation is provided by the parent class. 695a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 705a60e47497f21f64e6d79420dc4c56c1907df22akschulz @Override 715a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void attachInfo(Context context, ProviderInfo info) { 725a60e47497f21f64e6d79420dc4c56c1907df22akschulz mAuthority = info.authority; 735a60e47497f21f64e6d79420dc4c56c1907df22akschulz 745a60e47497f21f64e6d79420dc4c56c1907df22akschulz mMatcher = new UriMatcher(UriMatcher.NO_MATCH); 755a60e47497f21f64e6d79420dc4c56c1907df22akschulz mMatcher.addURI(mAuthority, BluetoothMapContract.TABLE_ACCOUNT, MATCH_ACCOUNT); 765a60e47497f21f64e6d79420dc4c56c1907df22akschulz mMatcher.addURI(mAuthority, "#/"+ BluetoothMapContract.TABLE_MESSAGE, MATCH_MESSAGE); 775a60e47497f21f64e6d79420dc4c56c1907df22akschulz mMatcher.addURI(mAuthority, "#/"+ BluetoothMapContract.TABLE_CONVERSATION, 785a60e47497f21f64e6d79420dc4c56c1907df22akschulz MATCH_CONVERSATION); 795a60e47497f21f64e6d79420dc4c56c1907df22akschulz mMatcher.addURI(mAuthority, "#/"+ BluetoothMapContract.TABLE_CONVOCONTACT, 805a60e47497f21f64e6d79420dc4c56c1907df22akschulz MATCH_CONVOCONTACT); 815a60e47497f21f64e6d79420dc4c56c1907df22akschulz 825a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Sanity check our setup 835a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (!info.exported) { 845a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new SecurityException("Provider must be exported"); 855a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 865a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Enforce correct permissions are used 875a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (!android.Manifest.permission.BLUETOOTH_MAP.equals(info.writePermission)){ 885a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new SecurityException("Provider must be protected by " + 895a60e47497f21f64e6d79420dc4c56c1907df22akschulz android.Manifest.permission.BLUETOOTH_MAP); 905a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 915a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.d(TAG,"attachInfo() mAuthority = " + mAuthority); 925a60e47497f21f64e6d79420dc4c56c1907df22akschulz 935a60e47497f21f64e6d79420dc4c56c1907df22akschulz mResolver = context.getContentResolver(); 945a60e47497f21f64e6d79420dc4c56c1907df22akschulz super.attachInfo(context, info); 955a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 965a60e47497f21f64e6d79420dc4c56c1907df22akschulz 975a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 985a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This function shall be called when any Account database content have changed 995a60e47497f21f64e6d79420dc4c56c1907df22akschulz * to Notify any attached observers. 1005a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account that changed. Null is a valid value, 1015a60e47497f21f64e6d79420dc4c56c1907df22akschulz * if accountId is unknown or multiple accounts changed. 1025a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 1035a60e47497f21f64e6d79420dc4c56c1907df22akschulz protected void onAccountChanged(String accountId) { 1045a60e47497f21f64e6d79420dc4c56c1907df22akschulz Uri newUri = null; 1055a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1065a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mAuthority == null){ 1075a60e47497f21f64e6d79420dc4c56c1907df22akschulz return; 1085a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1095a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(accountId == null){ 1105a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildAccountUri(mAuthority); 1115a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1125a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildAccountUriwithId(mAuthority, accountId); 1135a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1145a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1155a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.d(TAG,"onAccountChanged() accountId = " + accountId + " URI: " + newUri); 1165a60e47497f21f64e6d79420dc4c56c1907df22akschulz mResolver.notifyChange(newUri, null); 1175a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1185a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1195a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 1205a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This function shall be called when any Message database content have changed 1215a60e47497f21f64e6d79420dc4c56c1907df22akschulz * to notify any attached observers. 1225a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId Null is a valid value, if accountId is unknown, but 1235a60e47497f21f64e6d79420dc4c56c1907df22akschulz * recommended for increased performance. 1245a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param messageId Null is a valid value, if multiple messages changed or the 1255a60e47497f21f64e6d79420dc4c56c1907df22akschulz * messageId is unknown, but recommended for increased performance. 1265a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 1275a60e47497f21f64e6d79420dc4c56c1907df22akschulz protected void onMessageChanged(String accountId, String messageId) { 1285a60e47497f21f64e6d79420dc4c56c1907df22akschulz Uri newUri = null; 1295a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1305a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mAuthority == null){ 1315a60e47497f21f64e6d79420dc4c56c1907df22akschulz return; 1325a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1335a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(accountId == null){ 1345a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildMessageUri(mAuthority); 1355a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1365a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(messageId == null) 1375a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 1385a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildMessageUri(mAuthority,accountId); 1395a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1405a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildMessageUriWithId(mAuthority,accountId, 1415a60e47497f21f64e6d79420dc4c56c1907df22akschulz messageId); 1425a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1435a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1445a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.d(TAG,"onMessageChanged() accountId = " + accountId 1455a60e47497f21f64e6d79420dc4c56c1907df22akschulz + " messageId = " + messageId + " URI: " + newUri); 1465a60e47497f21f64e6d79420dc4c56c1907df22akschulz mResolver.notifyChange(newUri, null); 1475a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1485a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1495a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1505a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 1515a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This function shall be called when any Message database content have changed 1525a60e47497f21f64e6d79420dc4c56c1907df22akschulz * to notify any attached observers. 1535a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId Null is a valid value, if accountId is unknown, but 1545a60e47497f21f64e6d79420dc4c56c1907df22akschulz * recommended for increased performance. 1555a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param contactId Null is a valid value, if multiple contacts changed or the 1565a60e47497f21f64e6d79420dc4c56c1907df22akschulz * contactId is unknown, but recommended for increased performance. 1575a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 1585a60e47497f21f64e6d79420dc4c56c1907df22akschulz protected void onContactChanged(String accountId, String contactId) { 1595a60e47497f21f64e6d79420dc4c56c1907df22akschulz Uri newUri = null; 1605a60e47497f21f64e6d79420dc4c56c1907df22akschulz 1615a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mAuthority == null){ 1625a60e47497f21f64e6d79420dc4c56c1907df22akschulz return; 1635a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1645a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(accountId == null){ 1655a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildConvoContactsUri(mAuthority); 1665a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1675a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(contactId == null) 1685a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 1695a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildConvoContactsUri(mAuthority,accountId); 1705a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 1715a60e47497f21f64e6d79420dc4c56c1907df22akschulz newUri = BluetoothMapContract.buildConvoContactsUriWithId(mAuthority, accountId, 1725a60e47497f21f64e6d79420dc4c56c1907df22akschulz contactId); 1735a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1745a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1755a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.d(TAG,"onContactChanged() accountId = " + accountId 1765a60e47497f21f64e6d79420dc4c56c1907df22akschulz + " contactId = " + contactId + " URI: " + newUri); 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) { 1975a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (D) Log.d(TAG, "delete(): uri=" + uri.toString() ); 1985a60e47497f21f64e6d79420dc4c56c1907df22akschulz int result = 0; 1995a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2005a60e47497f21f64e6d79420dc4c56c1907df22akschulz String table = uri.getPathSegments().get(1); 2015a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(table == null) 2025a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Table missing in URI"); 2035a60e47497f21f64e6d79420dc4c56c1907df22akschulz // the id of the entry to be deleted from the database 2045a60e47497f21f64e6d79420dc4c56c1907df22akschulz String messageId = uri.getLastPathSegment(); 2055a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (messageId == null) 2065a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Message ID missing in update values!"); 2075a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2085a60e47497f21f64e6d79420dc4c56c1907df22akschulz String accountId = getAccountId(uri); 2095a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (accountId == null) 2105a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Account ID missing in update values!"); 2115a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2125a60e47497f21f64e6d79420dc4c56c1907df22akschulz long callingId = Binder.clearCallingIdentity(); 2135a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 2145a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(table.equals(BluetoothMapContract.TABLE_MESSAGE)) { 2155a60e47497f21f64e6d79420dc4c56c1907df22akschulz return deleteMessage(accountId, messageId); 2165a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 2175a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (D) Log.w(TAG, "Unknown table name: " + table); 2185a60e47497f21f64e6d79420dc4c56c1907df22akschulz return result; 2195a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2205a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 2215a60e47497f21f64e6d79420dc4c56c1907df22akschulz Binder.restoreCallingIdentity(callingId); 2225a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2235a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2245a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2255a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 2265a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This function deletes a message. 2275a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the Account 2285a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param messageId the ID of the message to delete. 2295a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the number of messages deleted - 0 if the message was not found. 2305a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 2315a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected int deleteMessage(String accountId, String messageId); 2325a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2335a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 2345a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Insert is used to add new messages to the data base. 2355a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Insert message approach: 2365a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - Insert an empty message to get an _id with only a folder_id 2375a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - Open the _id for write 2385a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - Write the message content 2395a60e47497f21f64e6d79420dc4c56c1907df22akschulz * (When the writer completes, this provider should do an update of the message) 2405a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 2415a60e47497f21f64e6d79420dc4c56c1907df22akschulz @Override 2425a60e47497f21f64e6d79420dc4c56c1907df22akschulz public Uri insert(Uri uri, ContentValues values) { 2435a60e47497f21f64e6d79420dc4c56c1907df22akschulz String table = uri.getLastPathSegment(); 2445a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(table == null) 2455a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Table missing in URI"); 2465a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2475a60e47497f21f64e6d79420dc4c56c1907df22akschulz String accountId = getAccountId(uri); 2485a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (accountId == null) 2495a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Account ID missing in URI"); 2505a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2515a60e47497f21f64e6d79420dc4c56c1907df22akschulz // TODO: validate values? 2525a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2535a60e47497f21f64e6d79420dc4c56c1907df22akschulz String id; // the id of the entry inserted into the database 2545a60e47497f21f64e6d79420dc4c56c1907df22akschulz long callingId = Binder.clearCallingIdentity(); 2555a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.d(TAG, "insert(): uri=" + uri.toString() + " - getLastPathSegment() = " + 2565a60e47497f21f64e6d79420dc4c56c1907df22akschulz uri.getLastPathSegment()); 2575a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 2585a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(table.equals(BluetoothMapContract.TABLE_MESSAGE)) { 2595a60e47497f21f64e6d79420dc4c56c1907df22akschulz id = insertMessage(accountId, values); 2605a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D) Log.i(TAG, "insert() ID: " + id); 2615a60e47497f21f64e6d79420dc4c56c1907df22akschulz return Uri.parse(uri.toString() + "/" + id); 2625a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 2635a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.w(TAG, "Unknown table name: " + table); 2645a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 2655a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2665a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 2675a60e47497f21f64e6d79420dc4c56c1907df22akschulz Binder.restoreCallingIdentity(callingId); 2685a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2695a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2705a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2715a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2725a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 2735a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Inserts an empty message into the Message data base in the specified folder. 2745a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This is done before the actual message content is written by fileIO. 2755a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account 2765a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param folderId the ID of the folder to create a new message in. 2775a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the message id as a string 2785a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 2795a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected String insertMessage(String accountId, ContentValues values); 2805a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2815a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 2825a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Utility function to build a projection based on a projectionMap. 2835a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 2845a60e47497f21f64e6d79420dc4c56c1907df22akschulz * "btColumnName" -> "imColumnName as btColumnName" for each entry. 2855a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 2865a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This supports SQL statements in the column name entry. 2875a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param projection 2885a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param projectionMap <string, string> 2895a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the converted projection 2905a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 2915a60e47497f21f64e6d79420dc4c56c1907df22akschulz protected String[] convertProjection(String[] projection, Map<String,String> projectionMap) { 2925a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] newProjection = new String[projection.length]; 2935a60e47497f21f64e6d79420dc4c56c1907df22akschulz for(int i = 0; i < projection.length; i++) { 2945a60e47497f21f64e6d79420dc4c56c1907df22akschulz newProjection[i] = projectionMap.get(projection[i]) + " as " + projection[i]; 2955a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2965a60e47497f21f64e6d79420dc4c56c1907df22akschulz return newProjection; 2975a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2985a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2995a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 3005a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This query needs to map from the data used in the e-mail client to 3015a60e47497f21f64e6d79420dc4c56c1907df22akschulz * BluetoothMapContract type of data. 3025a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 3035a60e47497f21f64e6d79420dc4c56c1907df22akschulz @Override 3045a60e47497f21f64e6d79420dc4c56c1907df22akschulz public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 3055a60e47497f21f64e6d79420dc4c56c1907df22akschulz String sortOrder) { 3065a60e47497f21f64e6d79420dc4c56c1907df22akschulz long callingId = Binder.clearCallingIdentity(); 3075a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 3085a60e47497f21f64e6d79420dc4c56c1907df22akschulz String accountId = null; 3095a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D)Log.w(TAG, "query(): uri =" + mAuthority + " uri=" + uri.toString()); 3105a60e47497f21f64e6d79420dc4c56c1907df22akschulz 3115a60e47497f21f64e6d79420dc4c56c1907df22akschulz switch (mMatcher.match(uri)) { 3125a60e47497f21f64e6d79420dc4c56c1907df22akschulz case MATCH_ACCOUNT: 3135a60e47497f21f64e6d79420dc4c56c1907df22akschulz return queryAccount(projection, selection, selectionArgs, sortOrder); 3145a60e47497f21f64e6d79420dc4c56c1907df22akschulz case MATCH_MESSAGE: 3155a60e47497f21f64e6d79420dc4c56c1907df22akschulz // TODO: Extract account from URI 3165a60e47497f21f64e6d79420dc4c56c1907df22akschulz accountId = getAccountId(uri); 3175a60e47497f21f64e6d79420dc4c56c1907df22akschulz return queryMessage(accountId, projection, selection, selectionArgs, sortOrder); 3185a60e47497f21f64e6d79420dc4c56c1907df22akschulz case MATCH_CONVERSATION: 3195a60e47497f21f64e6d79420dc4c56c1907df22akschulz accountId = getAccountId(uri); 3205a60e47497f21f64e6d79420dc4c56c1907df22akschulz String value; 3215a60e47497f21f64e6d79420dc4c56c1907df22akschulz String searchString = 3225a60e47497f21f64e6d79420dc4c56c1907df22akschulz uri.getQueryParameter(BluetoothMapContract.FILTER_ORIGINATOR_SUBSTRING); 3235a60e47497f21f64e6d79420dc4c56c1907df22akschulz Long periodBegin = null; 3245a60e47497f21f64e6d79420dc4c56c1907df22akschulz value = uri.getQueryParameter(BluetoothMapContract.FILTER_PERIOD_BEGIN); 3255a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(value != null) { 3265a60e47497f21f64e6d79420dc4c56c1907df22akschulz periodBegin = Long.parseLong(value); 3275a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3285a60e47497f21f64e6d79420dc4c56c1907df22akschulz Long periodEnd = null; 3295a60e47497f21f64e6d79420dc4c56c1907df22akschulz value = uri.getQueryParameter(BluetoothMapContract.FILTER_PERIOD_END); 3305a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(value != null) { 3315a60e47497f21f64e6d79420dc4c56c1907df22akschulz periodEnd = Long.parseLong(value); 3325a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3335a60e47497f21f64e6d79420dc4c56c1907df22akschulz Boolean read = null; 3345a60e47497f21f64e6d79420dc4c56c1907df22akschulz value = uri.getQueryParameter(BluetoothMapContract.FILTER_READ_STATUS); 3355a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(value != null) { 3365a60e47497f21f64e6d79420dc4c56c1907df22akschulz read = value.equalsIgnoreCase("true"); 3375a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3385a60e47497f21f64e6d79420dc4c56c1907df22akschulz Long threadId = null; 3395a60e47497f21f64e6d79420dc4c56c1907df22akschulz value = uri.getQueryParameter(BluetoothMapContract.FILTER_THREAD_ID); 3405a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(value != null) { 3415a60e47497f21f64e6d79420dc4c56c1907df22akschulz threadId = Long.parseLong(value); 3425a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3435a60e47497f21f64e6d79420dc4c56c1907df22akschulz return queryConversation(accountId, threadId, read, periodEnd, periodBegin, 3445a60e47497f21f64e6d79420dc4c56c1907df22akschulz searchString, projection, sortOrder); 3455a60e47497f21f64e6d79420dc4c56c1907df22akschulz case MATCH_CONVOCONTACT: 3465a60e47497f21f64e6d79420dc4c56c1907df22akschulz accountId = getAccountId(uri); 3475a60e47497f21f64e6d79420dc4c56c1907df22akschulz long contactId = 0; 3485a60e47497f21f64e6d79420dc4c56c1907df22akschulz return queryConvoContact(accountId, contactId, projection, 3495a60e47497f21f64e6d79420dc4c56c1907df22akschulz selection, selectionArgs, sortOrder); 3505a60e47497f21f64e6d79420dc4c56c1907df22akschulz default: 3515a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new UnsupportedOperationException("Unsupported Uri " + uri); 3525a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3535a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 3545a60e47497f21f64e6d79420dc4c56c1907df22akschulz Binder.restoreCallingIdentity(callingId); 3555a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3565a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3575a60e47497f21f64e6d79420dc4c56c1907df22akschulz 3585a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 3595a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Query account information. 3605a60e47497f21f64e6d79420dc4c56c1907df22akschulz * This function shall return only exposable e-mail accounts. Hence shall not 3615a60e47497f21f64e6d79420dc4c56c1907df22akschulz * return accounts that has policies suggesting not to be shared them. 3625a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param projection 3635a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selection 3645a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selectionArgs 3655a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param sortOrder 3665a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return a cursor to the accounts that are subject to exposure over BT. 3675a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 3685a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected Cursor queryAccount(String[] projection, String selection, 3695a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] selectionArgs, String sortOrder); 3705a60e47497f21f64e6d79420dc4c56c1907df22akschulz 3715a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 3725a60e47497f21f64e6d79420dc4c56c1907df22akschulz * For the message table the selection (where clause) can only include the following columns: 3735a60e47497f21f64e6d79420dc4c56c1907df22akschulz * date: less than, greater than and equals 3745a60e47497f21f64e6d79420dc4c56c1907df22akschulz * flagRead: = 1 or = 0 3755a60e47497f21f64e6d79420dc4c56c1907df22akschulz * flagPriority: = 1 or = 0 3765a60e47497f21f64e6d79420dc4c56c1907df22akschulz * folder_id: the ID of the folder only equals 3775a60e47497f21f64e6d79420dc4c56c1907df22akschulz * toList: partial name/address search 3785a60e47497f21f64e6d79420dc4c56c1907df22akschulz * fromList: partial name/address search 3795a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Additionally the COUNT and OFFSET shall be supported. 3805a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account 3815a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param projection 3825a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selection 3835a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selectionArgs 3845a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param sortOrder 3855a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return a cursor to query result 3865a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 3875a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected Cursor queryMessage(String accountId, String[] projection, String selection, 3885a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] selectionArgs, String sortOrder); 3895a60e47497f21f64e6d79420dc4c56c1907df22akschulz 3905a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 3915a60e47497f21f64e6d79420dc4c56c1907df22akschulz * For the Conversation table the selection (where clause) can only include 3925a60e47497f21f64e6d79420dc4c56c1907df22akschulz * the following columns: 3935a60e47497f21f64e6d79420dc4c56c1907df22akschulz * _id: the ID of the conversation only equals 3945a60e47497f21f64e6d79420dc4c56c1907df22akschulz * name: partial name search 3955a60e47497f21f64e6d79420dc4c56c1907df22akschulz * last_activity: less than, greater than and equals 3965a60e47497f21f64e6d79420dc4c56c1907df22akschulz * version_counter: updated IDs are regenerated 3975a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Additionally the COUNT and OFFSET shall be supported. 3985a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account 3995a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param threadId the ID of the conversation 4005a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param projection 4015a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selection 4025a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selectionArgs 4035a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param sortOrder 4045a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return a cursor to query result 4055a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 4065a60e47497f21f64e6d79420dc4c56c1907df22akschulz// abstract protected Cursor queryConversation(Long threadId, String[] projection, 4075a60e47497f21f64e6d79420dc4c56c1907df22akschulz// String selection, String[] selectionArgs, String sortOrder); 4085a60e47497f21f64e6d79420dc4c56c1907df22akschulz 4095a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 4105a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Query for conversations with contact information. The expected result is a cursor pointing 4115a60e47497f21f64e6d79420dc4c56c1907df22akschulz * to one row for each contact in a conversation. 4125a60e47497f21f64e6d79420dc4c56c1907df22akschulz * E.g.: 4135a60e47497f21f64e6d79420dc4c56c1907df22akschulz * ThreadId | ThreadName | ... | ContactName | ContactPrecence | ... | 4145a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 1 | "Bowling" | ... | Hans | 1 | ... | 4155a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 1 | "Bowling" | ... | Peter | 2 | ... | 4165a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 2 | "" | ... | Peter | 2 | ... | 4175a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 3 | "" | ... | Hans | 1 | ... | 4185a60e47497f21f64e6d79420dc4c56c1907df22akschulz * 4195a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account 4205a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param threadId filter on a single threadId - null if no filtering is needed. 4215a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param read filter on a read status: 4225a60e47497f21f64e6d79420dc4c56c1907df22akschulz * null: no filtering on read is needed. 4235a60e47497f21f64e6d79420dc4c56c1907df22akschulz * true: return only threads that has NO unread messages. 4245a60e47497f21f64e6d79420dc4c56c1907df22akschulz * false: return only threads that has unread messages. 4255a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param periodEnd last_activity time stamp of the the newest thread to include in the 4265a60e47497f21f64e6d79420dc4c56c1907df22akschulz * result. 4275a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param periodBegin last_activity time stamp of the the oldest thread to include in the 4285a60e47497f21f64e6d79420dc4c56c1907df22akschulz * result. 4295a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param searchString if not null, include only threads that has contacts that matches the 4305a60e47497f21f64e6d79420dc4c56c1907df22akschulz * searchString as part of the contact name or nickName. 4315a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param projection A list of the columns that is needed in the result 4325a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param sortOrder the sort order 4335a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return a Cursor representing the query result. 4345a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 4355a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected Cursor queryConversation(String accountId, Long threadId, Boolean read, 4365a60e47497f21f64e6d79420dc4c56c1907df22akschulz Long periodEnd, Long periodBegin, String searchString, String[] projection, 4375a60e47497f21f64e6d79420dc4c56c1907df22akschulz String sortOrder); 4385a60e47497f21f64e6d79420dc4c56c1907df22akschulz 4395a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 4405a60e47497f21f64e6d79420dc4c56c1907df22akschulz * For the ConvoContact table the selection (where clause) can only include the 4415a60e47497f21f64e6d79420dc4c56c1907df22akschulz * following columns: 4425a60e47497f21f64e6d79420dc4c56c1907df22akschulz * _id: the ID of the contact only equals 4435a60e47497f21f64e6d79420dc4c56c1907df22akschulz * convo_id: id of conversation contact is part of 4445a60e47497f21f64e6d79420dc4c56c1907df22akschulz * name: partial name search 4455a60e47497f21f64e6d79420dc4c56c1907df22akschulz * x_bt_uid: the ID of the bt uid only equals 4465a60e47497f21f64e6d79420dc4c56c1907df22akschulz * chat_state: active, inactive, gone, composing, paused 4475a60e47497f21f64e6d79420dc4c56c1907df22akschulz * last_active: less than, greater than and equals 4485a60e47497f21f64e6d79420dc4c56c1907df22akschulz * presence_state: online, do_not_disturb, away, offline 4495a60e47497f21f64e6d79420dc4c56c1907df22akschulz * priority: level of priority 0 - 100 4505a60e47497f21f64e6d79420dc4c56c1907df22akschulz * last_online: less than, greater than and equals 4515a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account 4525a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param contactId the ID of the contact 4535a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param projection 4545a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selection 4555a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param selectionArgs 4565a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param sortOrder 4575a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return a cursor to query result 4585a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 4595a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected Cursor queryConvoContact(String accountId, Long contactId, 4605a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] projection, String selection, String[] selectionArgs, String sortOrder); 4615a60e47497f21f64e6d79420dc4c56c1907df22akschulz 4625a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 4635a60e47497f21f64e6d79420dc4c56c1907df22akschulz * update() 4645a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Messages can be modified in the following cases: 4655a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the folder_key of a message - hence the message can be moved to a new folder, 4665a60e47497f21f64e6d79420dc4c56c1907df22akschulz * but the content cannot be modified. 4675a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the FLAG_READ state can be changed. 4685a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Conversations can be modified in the following cases: 4695a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the read status - changing between read, unread 4705a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the last activity - the time stamp of last message sent of received in the conversation 4715a60e47497f21f64e6d79420dc4c56c1907df22akschulz * ConvoContacts can be modified in the following cases: 4725a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the chat_state - chat status of the contact in conversation 4735a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the last_active - the time stamp of last action in the conversation 4745a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the presence_state - the time stamp of last time contact online 4755a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the status - the status text of the contact available in a conversation 4765a60e47497f21f64e6d79420dc4c56c1907df22akschulz * - the last_online - the time stamp of last time contact online 4775a60e47497f21f64e6d79420dc4c56c1907df22akschulz * The selection statement will always be selection of a message ID, when updating a message, 4785a60e47497f21f64e6d79420dc4c56c1907df22akschulz * hence this function will be called multiple times if multiple messages must be updated 4795a60e47497f21f64e6d79420dc4c56c1907df22akschulz * due to the nature of the Bluetooth Message Access profile. 4805a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 4815a60e47497f21f64e6d79420dc4c56c1907df22akschulz @Override 4825a60e47497f21f64e6d79420dc4c56c1907df22akschulz public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 4835a60e47497f21f64e6d79420dc4c56c1907df22akschulz 4845a60e47497f21f64e6d79420dc4c56c1907df22akschulz String table = uri.getLastPathSegment(); 4855a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(table == null){ 4865a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Table missing in URI"); 4875a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 4885a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(selection != null) { 4895a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("selection shall not be used, ContentValues " + 4905a60e47497f21f64e6d79420dc4c56c1907df22akschulz "shall contain the data"); 4915a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 4925a60e47497f21f64e6d79420dc4c56c1907df22akschulz 4935a60e47497f21f64e6d79420dc4c56c1907df22akschulz long callingId = Binder.clearCallingIdentity(); 4945a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D)Log.w(TAG, "update(): uri=" + uri.toString() + " - getLastPathSegment() = " + 4955a60e47497f21f64e6d79420dc4c56c1907df22akschulz uri.getLastPathSegment()); 4965a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 4975a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(table.equals(BluetoothMapContract.TABLE_ACCOUNT)) { 4985a60e47497f21f64e6d79420dc4c56c1907df22akschulz String accountId = values.getAsString(BluetoothMapContract.AccountColumns._ID); 4995a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(accountId == null) { 5005a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Account ID missing in update values!"); 5015a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5025a60e47497f21f64e6d79420dc4c56c1907df22akschulz Integer exposeFlag = values.getAsInteger( 5035a60e47497f21f64e6d79420dc4c56c1907df22akschulz BluetoothMapContract.AccountColumns.FLAG_EXPOSE); 5045a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(exposeFlag == null){ 5055a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Expose flag missing in update values!"); 5065a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5075a60e47497f21f64e6d79420dc4c56c1907df22akschulz return updateAccount(accountId, exposeFlag); 5085a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(table.equals(BluetoothMapContract.TABLE_FOLDER)) { 5095a60e47497f21f64e6d79420dc4c56c1907df22akschulz return 0; // We do not support changing folders 5105a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(table.equals(BluetoothMapContract.TABLE_MESSAGE)) { 5115a60e47497f21f64e6d79420dc4c56c1907df22akschulz String accountId = getAccountId(uri); 5125a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(accountId == null) { 5135a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Account ID missing in update values!"); 5145a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5155a60e47497f21f64e6d79420dc4c56c1907df22akschulz Long messageId = values.getAsLong(BluetoothMapContract.MessageColumns._ID); 5165a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(messageId == null) { 5175a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Message ID missing in update values!"); 5185a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5195a60e47497f21f64e6d79420dc4c56c1907df22akschulz Long folderId = values.getAsLong(BluetoothMapContract.MessageColumns.FOLDER_ID); 5205a60e47497f21f64e6d79420dc4c56c1907df22akschulz Boolean flagRead = values.getAsBoolean( 5215a60e47497f21f64e6d79420dc4c56c1907df22akschulz BluetoothMapContract.MessageColumns.FLAG_READ); 5225a60e47497f21f64e6d79420dc4c56c1907df22akschulz return updateMessage(accountId, messageId, folderId, flagRead); 5235a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(table.equals(BluetoothMapContract.TABLE_CONVERSATION)) { 5245a60e47497f21f64e6d79420dc4c56c1907df22akschulz return 0; // We do not support changing conversation 5255a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(table.equals(BluetoothMapContract.TABLE_CONVOCONTACT)) { 5265a60e47497f21f64e6d79420dc4c56c1907df22akschulz return 0; // We do not support changing contacts 5275a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 5285a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D)Log.w(TAG, "Unknown table name: " + table); 5295a60e47497f21f64e6d79420dc4c56c1907df22akschulz return 0; 5305a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5315a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 5325a60e47497f21f64e6d79420dc4c56c1907df22akschulz Binder.restoreCallingIdentity(callingId); 5335a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5345a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5355a60e47497f21f64e6d79420dc4c56c1907df22akschulz 5365a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 5375a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Update an entry in the account table. Only the expose flag will be 5385a60e47497f21f64e6d79420dc4c56c1907df22akschulz * changed through this interface. 5395a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account to change. 5405a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param flagExpose the updated value. 5415a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the number of entries changed - 0 if account not found or value cannot be changed. 5425a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 5435a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected int updateAccount(String accountId, Integer flagExpose); 5445a60e47497f21f64e6d79420dc4c56c1907df22akschulz 5455a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 5465a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Update an entry in the message table. 5475a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId ID of the account to which the messageId relates 5485a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param messageId the ID of the message to update 5495a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param folderId the new folder ID value to set - ignore if null. 5505a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param flagRead the new flagRead value to set - ignore if null. 5515a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return 5525a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 5535a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected int updateMessage(String accountId, Long messageId, Long folderId, 5545a60e47497f21f64e6d79420dc4c56c1907df22akschulz Boolean flagRead); 5555a60e47497f21f64e6d79420dc4c56c1907df22akschulz 5565a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 5575a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Utility function to Creates a ContentValues object based on a modified valuesSet. 5585a60e47497f21f64e6d79420dc4c56c1907df22akschulz * To be used after changing the keys and optionally values of a valueSet obtained 5595a60e47497f21f64e6d79420dc4c56c1907df22akschulz * from a ContentValues object received in update(). 5605a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param valueSet the values as received in the contentProvider 5615a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param keyMap the key map <btKey, emailKey> 5625a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return a new ContentValues object with the keys replaced as specified in the 5635a60e47497f21f64e6d79420dc4c56c1907df22akschulz * keyMap 5645a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 5655a60e47497f21f64e6d79420dc4c56c1907df22akschulz protected ContentValues createContentValues(Set<Entry<String,Object>> valueSet, 5665a60e47497f21f64e6d79420dc4c56c1907df22akschulz Map<String, String> keyMap) { 5675a60e47497f21f64e6d79420dc4c56c1907df22akschulz ContentValues values = new ContentValues(valueSet.size()); 5685a60e47497f21f64e6d79420dc4c56c1907df22akschulz for(Entry<String,Object> ent : valueSet) { 5695a60e47497f21f64e6d79420dc4c56c1907df22akschulz String key = keyMap.get(ent.getKey()); // Convert the key name 5705a60e47497f21f64e6d79420dc4c56c1907df22akschulz Object value = ent.getValue(); 5715a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(value == null) { 5725a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.putNull(key); 5735a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof Boolean) { 5745a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (Boolean) value); 5755a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof Byte) { 5765a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (Byte) value); 5775a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof byte[]) { 5785a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (byte[]) value); 5795a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof Double) { 5805a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (Double) value); 5815a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof Float) { 5825a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (Float) value); 5835a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof Integer) { 5845a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (Integer) value); 5855a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof Long) { 5865a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (Long) value); 5875a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof Short) { 5885a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (Short) value); 5895a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if(ent.getValue() instanceof String) { 5905a60e47497f21f64e6d79420dc4c56c1907df22akschulz values.put(key, (String) value); 5915a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else { 5925a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Unknown data type in content value"); 5935a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5945a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5955a60e47497f21f64e6d79420dc4c56c1907df22akschulz return values; 5965a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5975a60e47497f21f64e6d79420dc4c56c1907df22akschulz 5985a60e47497f21f64e6d79420dc4c56c1907df22akschulz @Override 5995a60e47497f21f64e6d79420dc4c56c1907df22akschulz public Bundle call(String method, String arg, Bundle extras) { 6005a60e47497f21f64e6d79420dc4c56c1907df22akschulz long callingId = Binder.clearCallingIdentity(); 6015a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(D)Log.w(TAG, "call(): method=" + method + " arg=" + arg + "ThreadId: " 6025a60e47497f21f64e6d79420dc4c56c1907df22akschulz + Thread.currentThread().getId()); 6035a60e47497f21f64e6d79420dc4c56c1907df22akschulz int ret = -1; 6045a60e47497f21f64e6d79420dc4c56c1907df22akschulz try { 6055a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(method.equals(BluetoothMapContract.METHOD_UPDATE_FOLDER)) { 6065a60e47497f21f64e6d79420dc4c56c1907df22akschulz long accountId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_ACCOUNT_ID, -1); 6075a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(accountId == -1) { 6085a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.w(TAG, "No account ID in CALL"); 6095a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 6105a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6115a60e47497f21f64e6d79420dc4c56c1907df22akschulz long folderId = extras.getLong(BluetoothMapContract.EXTRA_UPDATE_FOLDER_ID, -1); 6125a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(folderId == -1) { 6135a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.w(TAG, "No folder ID in CALL"); 6145a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 6155a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6165a60e47497f21f64e6d79420dc4c56c1907df22akschulz ret = syncFolder(accountId, folderId); 6175a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if (method.equals(BluetoothMapContract.METHOD_SET_OWNER_STATUS)) { 6185a60e47497f21f64e6d79420dc4c56c1907df22akschulz int presenceState = extras.getInt(BluetoothMapContract.EXTRA_PRESENCE_STATE); 6195a60e47497f21f64e6d79420dc4c56c1907df22akschulz String presenceStatus = extras.getString( 6205a60e47497f21f64e6d79420dc4c56c1907df22akschulz BluetoothMapContract.EXTRA_PRESENCE_STATUS); 6215a60e47497f21f64e6d79420dc4c56c1907df22akschulz long lastActive = extras.getLong(BluetoothMapContract.EXTRA_LAST_ACTIVE); 6225a60e47497f21f64e6d79420dc4c56c1907df22akschulz int chatState = extras.getInt(BluetoothMapContract.EXTRA_CHAT_STATE); 6235a60e47497f21f64e6d79420dc4c56c1907df22akschulz String convoId = extras.getString(BluetoothMapContract.EXTRA_CONVERSATION_ID); 6245a60e47497f21f64e6d79420dc4c56c1907df22akschulz ret = setOwnerStatus(presenceState, presenceStatus, lastActive, chatState, convoId); 6255a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6265a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else if (method.equals(BluetoothMapContract.METHOD_SET_BLUETOOTH_STATE)) { 6275a60e47497f21f64e6d79420dc4c56c1907df22akschulz boolean bluetoothState = extras.getBoolean( 6285a60e47497f21f64e6d79420dc4c56c1907df22akschulz BluetoothMapContract.EXTRA_BLUETOOTH_STATE); 6295a60e47497f21f64e6d79420dc4c56c1907df22akschulz ret = setBluetoothStatus(bluetoothState); 6305a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6315a60e47497f21f64e6d79420dc4c56c1907df22akschulz } finally { 6325a60e47497f21f64e6d79420dc4c56c1907df22akschulz Binder.restoreCallingIdentity(callingId); 6335a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6345a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(ret == 0) { 6355a60e47497f21f64e6d79420dc4c56c1907df22akschulz return new Bundle(); 6365a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6375a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 6385a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6395a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6405a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 6415a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Trigger a sync of the specified folder. 6425a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param accountId the ID of the account that owns the folder 6435a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param folderId the ID of the folder. 6445a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return 0 at success 6455a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 6465a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected int syncFolder(long accountId, long folderId); 6475a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6485a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 6495a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Set the properties that should change presence or chat state of owner 6505a60e47497f21f64e6d79420dc4c56c1907df22akschulz * e.g. when the owner is active on a BT client device but not on the BT server device 6515a60e47497f21f64e6d79420dc4c56c1907df22akschulz * where the IM application is installed, it should still be possible to show an active status. 6525a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param presenceState should follow the contract specified values 6535a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param presenceStatus string the owners current status 6545a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param lastActive time stamp of the owners last activity 6555a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param chatState should follow the contract specified values 6565a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param convoId ID to the conversation to change 6575a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return 0 at success 6585a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 6595a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected int setOwnerStatus(int presenceState, String presenceStatus, 6605a60e47497f21f64e6d79420dc4c56c1907df22akschulz long lastActive, int chatState, String convoId); 6615a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6625a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 6635a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Notify the application of the Bluetooth state 6645a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param bluetoothState 'on' of 'off' 6655a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return 0 at success 6665a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 6675a60e47497f21f64e6d79420dc4c56c1907df22akschulz abstract protected int setBluetoothStatus(boolean bluetoothState); 6685a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6695a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6705a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6715a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 6725a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Need this to suppress warning in unit tests. 6735a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 6745a60e47497f21f64e6d79420dc4c56c1907df22akschulz @Override 6755a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void shutdown() { 6765a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Don't call super.shutdown(), which emits a warning... 6775a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6785a60e47497f21f64e6d79420dc4c56c1907df22akschulz 6795a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 6805a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Extract the BluetoothMapContract.AccountColumns._ID from the given URI. 6815a60e47497f21f64e6d79420dc4c56c1907df22akschulz */ 6825a60e47497f21f64e6d79420dc4c56c1907df22akschulz public static String getAccountId(Uri uri) { 6835a60e47497f21f64e6d79420dc4c56c1907df22akschulz final List<String> segments = uri.getPathSegments(); 6845a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (segments.size() < 1) { 6855a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("No AccountId pressent in URI: " + uri); 6865a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6875a60e47497f21f64e6d79420dc4c56c1907df22akschulz return segments.get(0); 6885a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 6895a60e47497f21f64e6d79420dc4c56c1907df22akschulz} 690