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