172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov/* 272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * 472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * you may not use this file except in compliance with the License. 672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * You may obtain a copy of the License at 772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * 872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * 1072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 1172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 1272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * See the License for the specific language governing permissions and 1472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * limitations under the License 1572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 1672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 1772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovpackage com.android.providers.contacts; 1872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 1972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.ContentValues; 2072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.Context; 2172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageInfo; 2272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageManager; 2372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageManager.NameNotFoundException; 2472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.ProviderInfo; 25e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.content.res.Resources; 26e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.content.res.Resources.NotFoundException; 2772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.database.Cursor; 2872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.database.sqlite.SQLiteDatabase; 2972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.net.Uri; 3072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.os.Bundle; 3172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.os.SystemClock; 32e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.provider.ContactsContract; 3372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.provider.ContactsContract.Directory; 34e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.text.TextUtils; 3572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.util.Log; 3672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 3738210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.DbProperties; 3838210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns; 3938210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.Tables; 4038210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.google.android.collect.Lists; 4138210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.google.android.collect.Sets; 4238210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.google.common.annotations.VisibleForTesting; 4338210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki 4472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport java.util.ArrayList; 4572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport java.util.List; 46ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onukiimport java.util.Set; 4772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 4872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov/** 4972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Manages the contents of the {@link Directory} table. 5072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 51bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikovpublic class ContactDirectoryManager { 5272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 5372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private static final String TAG = "ContactDirectoryManager"; 54ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private static final boolean DEBUG = false; // DON'T SUBMIT WITH TRUE 5572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 563d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov public static final String CONTACT_DIRECTORY_META_DATA = "android.content.ContactDirectory"; 5772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 58a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki public static class DirectoryInfo { 5972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov long id; 6072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String packageName; 6172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String authority; 6272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String accountName; 6372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String accountType; 6472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov String displayName; 6572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int typeResourceId; 6672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int exportSupport = Directory.EXPORT_SUPPORT_NONE; 6772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int shortcutSupport = Directory.SHORTCUT_SUPPORT_NONE; 68994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov int photoSupport = Directory.PHOTO_SUPPORT_NONE; 69ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @Override 70ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki public String toString() { 71ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return "DirectoryInfo:" 72ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + "id=" + id 73ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " packageName=" + accountType 74ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " authority=" + authority 75ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " accountName=***" 76ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " accountType=" + accountType; 77ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 7872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 7972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 8072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private final static class DirectoryQuery { 8172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final String[] PROJECTION = { 8272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.ACCOUNT_NAME, 8372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.ACCOUNT_TYPE, 8472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.DISPLAY_NAME, 8572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.TYPE_RESOURCE_ID, 8672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.EXPORT_SUPPORT, 8772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.SHORTCUT_SUPPORT, 88994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov Directory.PHOTO_SUPPORT, 8972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov }; 9072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 9172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int ACCOUNT_NAME = 0; 9272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int ACCOUNT_TYPE = 1; 9372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int DISPLAY_NAME = 2; 9472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int TYPE_RESOURCE_ID = 3; 9572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int EXPORT_SUPPORT = 4; 9672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public static final int SHORTCUT_SUPPORT = 5; 97994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov public static final int PHOTO_SUPPORT = 6; 9872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 9972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 10072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private final ContactsProvider2 mContactsProvider; 101ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private final Context mContext; 102ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private final PackageManager mPackageManager; 10372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 10472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public ContactDirectoryManager(ContactsProvider2 contactsProvider) { 105ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki mContactsProvider = contactsProvider; 106ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki mContext = contactsProvider.getContext(); 107ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki mPackageManager = mContext.getPackageManager(); 10872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 10972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 110e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov public ContactsDatabaseHelper getDbHelper() { 111e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return (ContactsDatabaseHelper) mContactsProvider.getDatabaseHelper(); 112e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 113e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 11472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 11572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Scans all packages owned by the specified calling UID looking for contact 11672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * directory providers. 11772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 11872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public void scanPackagesByUid(int callingUid) { 119ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final String[] callerPackages = mPackageManager.getPackagesForUid(callingUid); 12072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (callerPackages != null) { 12172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (int i = 0; i < callerPackages.length; i++) { 12272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov onPackageChanged(callerPackages[i]); 12372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 12472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 12572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 12672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 12772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 128e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * Scans through existing directories to see if the cached resource IDs still 129e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * match their original resource names. If not - plays it safe by refreshing all directories. 130e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * 131e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * @return true if all resource IDs were found valid 132e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov */ 133e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov private boolean areTypeResourceIdsValid() { 134e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov SQLiteDatabase db = getDbHelper().getReadableDatabase(); 135e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 136e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov Cursor cursor = db.query(Tables.DIRECTORIES, 137e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov new String[] { Directory.TYPE_RESOURCE_ID, Directory.PACKAGE_NAME, 138e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov DirectoryColumns.TYPE_RESOURCE_NAME }, null, null, null, null, null); 139e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov try { 140e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov while (cursor.moveToNext()) { 141e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov int resourceId = cursor.getInt(0); 142e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov if (resourceId != 0) { 143e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov String packageName = cursor.getString(1); 144e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov String storedResourceName = cursor.getString(2); 145ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki String resourceName = getResourceNameById(packageName, resourceId); 146e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov if (!TextUtils.equals(storedResourceName, resourceName)) { 147e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return false; 148e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 149e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 150e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 151e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } finally { 152e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov cursor.close(); 153e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 154e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 155e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return true; 156e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 157e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 158e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov /** 159e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * Given a resource ID, returns the corresponding resource name or null if the package name / 160e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov * resource ID combination is invalid. 161e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov */ 162ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki private String getResourceNameById(String packageName, int resourceId) { 163e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov try { 164ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Resources resources = mPackageManager.getResourcesForApplication(packageName); 165e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return resources.getResourceName(resourceId); 166e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } catch (NameNotFoundException e) { 167e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return null; 168e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } catch (NotFoundException e) { 169e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov return null; 170e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 171e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 172e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 173e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov /** 174afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov * Scans all packages for directory content providers. 17572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 176bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov public void scanAllPackages(boolean rescan) { 177bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov if (rescan || !areTypeResourceIdsValid()) { 1789ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki getDbHelper().setProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "0"); 179bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov } 180bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov 181bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov scanAllPackagesIfNeeded(); 182bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov } 183bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov 18472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private void scanAllPackagesIfNeeded() { 1859ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki String scanComplete = getDbHelper().getProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "0"); 18672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!"0".equals(scanComplete)) { 18772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov return; 18872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 18972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 190565b62f354d8b6aadc760092a7dbf483f8bbbe17Makoto Onuki final long start = SystemClock.elapsedRealtime(); 19172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int count = scanAllPackages(); 1929ba8463dd030e5e26a4f99dfe2a6ad52b2410d73Makoto Onuki getDbHelper().setProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "1"); 193565b62f354d8b6aadc760092a7dbf483f8bbbe17Makoto Onuki final long end = SystemClock.elapsedRealtime(); 19472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.i(TAG, "Discovered " + count + " contact directories in " + (end - start) + "ms"); 19572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 19672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // Announce the change to listeners of the contacts authority 19772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov mContactsProvider.notifyChange(false); 19872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 19972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 200ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @VisibleForTesting 201ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki static boolean isDirectoryProvider(ProviderInfo provider) { 202ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Bundle metaData = provider.metaData; 203ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (metaData == null) return false; 204ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 205ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Object trueFalse = metaData.get(CONTACT_DIRECTORY_META_DATA); 206ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return trueFalse != null && Boolean.TRUE.equals(trueFalse); 207ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 208ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 209ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki /** 210ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki * @return List of packages that contain a directory provider. 211ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki */ 212ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @VisibleForTesting 213ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki static Set<String> getDirectoryProviderPackages(PackageManager pm) { 214ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final Set<String> ret = Sets.newHashSet(); 215ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 216ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // Note to 3rd party developers: 217ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // queryContentProviders() is a public API but this method doesn't officially support 218ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // the GET_META_DATA flag. Don't use it in your app. 219ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final List<ProviderInfo> providers = pm.queryContentProviders(null, 0, 220ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki PackageManager.GET_META_DATA); 221ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (providers == null) { 222ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return ret; 223ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 224ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki for (ProviderInfo provider : providers) { 225ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (isDirectoryProvider(provider)) { 226ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki ret.add(provider.packageName); 227ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 228ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 229ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (DEBUG) { 230ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Log.d(TAG, "Found " + ret.size() + " directory provider packages"); 231ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 232ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 233ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki return ret; 234ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 235ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 236ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki @VisibleForTesting 23772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int scanAllPackages() { 238e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov SQLiteDatabase db = getDbHelper().getWritableDatabase(); 239e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov insertDefaultDirectory(db); 240e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov insertLocalInvisibleDirectory(db); 241e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 24272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int count = 0; 243ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 244ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // Prepare query strings for removing stale rows which don't correspond to existing 245ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // directories. 246ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki StringBuilder deleteWhereBuilder = new StringBuilder(); 247ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki ArrayList<String> deleteWhereArgs = new ArrayList<String>(); 248ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append("NOT (" + Directory._ID + "=? OR " + Directory._ID + "=?"); 249ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(String.valueOf(Directory.DEFAULT)); 250ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(String.valueOf(Directory.LOCAL_INVISIBLE)); 251ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final String wherePart = "(" + Directory.PACKAGE_NAME + "=? AND " 252ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + Directory.DIRECTORY_AUTHORITY + "=? AND " 253ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + Directory.ACCOUNT_NAME + "=? AND " 254ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + Directory.ACCOUNT_TYPE + "=?)"; 255ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 256ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki for (String packageName : getDirectoryProviderPackages(mPackageManager)) { 257ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (DEBUG) Log.d(TAG, "package=" + packageName); 258ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 259a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki // getDirectoryProviderPackages() shouldn't return the contacts provider package 260a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki // because it doesn't have CONTACT_DIRECTORY_META_DATA, but just to make sure... 261a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki if (mContext.getPackageName().equals(packageName)) { 262a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki Log.w(TAG, " skipping self"); 263a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki continue; 264a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki } 265a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki 266ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki final PackageInfo packageInfo; 267ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki try { 268ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki packageInfo = mPackageManager.getPackageInfo(packageName, 269ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 270ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (packageInfo == null) continue; // Just in case... 271ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } catch (NameNotFoundException nnfe) { 272ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki continue; // Application just removed? 27372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 27437f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa 275ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki List<DirectoryInfo> directories = updateDirectoriesForPackage(packageInfo, true); 276ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (directories != null && !directories.isEmpty()) { 277ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki count += directories.size(); 278ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 279ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki // We shouldn't delete rows for existing directories. 280ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki for (DirectoryInfo info : directories) { 281ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (DEBUG) Log.d(TAG, " directory=" + info); 282ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append(" OR "); 283ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append(wherePart); 284ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.packageName); 285ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.authority); 286ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.accountName); 287ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.add(info.accountType); 288ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 289ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki } 29072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 291ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 292ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereBuilder.append(")"); // Close "NOT (" 293ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 294ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki int deletedRows = db.delete(Tables.DIRECTORIES, deleteWhereBuilder.toString(), 295ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki deleteWhereArgs.toArray(new String[0])); 296ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki Log.i(TAG, "deleted " + deletedRows 297ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki + " stale rows which don't have any relevant directory"); 29872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov return count; 29972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 30072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 301e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov private void insertDefaultDirectory(SQLiteDatabase db) { 302e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov ContentValues values = new ContentValues(); 303e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory._ID, Directory.DEFAULT); 304e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PACKAGE_NAME, mContext.getApplicationInfo().packageName); 305e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY); 306e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.TYPE_RESOURCE_ID, R.string.default_directory); 307e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(DirectoryColumns.TYPE_RESOURCE_NAME, 308e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov mContext.getResources().getResourceName(R.string.default_directory)); 309e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE); 310e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL); 311e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL); 312e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov db.replace(Tables.DIRECTORIES, null, values); 313e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 314e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 315e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov private void insertLocalInvisibleDirectory(SQLiteDatabase db) { 316e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov ContentValues values = new ContentValues(); 317e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory._ID, Directory.LOCAL_INVISIBLE); 318e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PACKAGE_NAME, mContext.getApplicationInfo().packageName); 319e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY); 320e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.TYPE_RESOURCE_ID, R.string.local_invisible_directory); 321e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(DirectoryColumns.TYPE_RESOURCE_NAME, 322e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov mContext.getResources().getResourceName(R.string.local_invisible_directory)); 323e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE); 324e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL); 325e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL); 326e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov db.replace(Tables.DIRECTORIES, null, values); 327e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 328e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 32972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 33072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Scans the specified package for content directories. The package may have 33172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * already been removed, so packageName does not necessarily correspond to 33272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * an installed package. 33372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 33472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov public void onPackageChanged(String packageName) { 33572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov PackageInfo packageInfo = null; 33672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 33772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 338ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki packageInfo = mPackageManager.getPackageInfo(packageName, 33972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 34072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } catch (NameNotFoundException e) { 34172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // The package got removed 34272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov packageInfo = new PackageInfo(); 34372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov packageInfo.packageName = packageName; 34472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 34572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 346a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki if (mContext.getPackageName().equals(packageInfo.packageName)) { 347a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki if (DEBUG) Log.d(TAG, "Ignoring onPackageChanged for self"); 348a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki return; 349a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki } 35072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov updateDirectoriesForPackage(packageInfo, false); 351e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 35272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 353ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki 35472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 35572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Scans the specified package for content directories and updates the {@link Directory} 35672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * table accordingly. 35772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 35837f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa private List<DirectoryInfo> updateDirectoriesForPackage( 35937f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa PackageInfo packageInfo, boolean initialScan) { 360a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki if (DEBUG) { 361a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki Log.d(TAG, "updateDirectoriesForPackage packageName=" + packageInfo.packageName 362a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki + " initialScan=" + initialScan); 363a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki } 364a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki 36572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ArrayList<DirectoryInfo> directories = Lists.newArrayList(); 36672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 36772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ProviderInfo[] providers = packageInfo.providers; 36872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (providers != null) { 36972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (ProviderInfo provider : providers) { 370ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki if (isDirectoryProvider(provider)) { 371ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki queryDirectoriesForAuthority(directories, provider); 37272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 37372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 37472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 37572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 37672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (directories.size() == 0 && initialScan) { 37737f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa return null; 37872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 37972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 380e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov SQLiteDatabase db = getDbHelper().getWritableDatabase(); 38172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.beginTransaction(); 38272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 38372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov updateDirectories(db, directories); 38472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // Clear out directories that are no longer present 38572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov StringBuilder sb = new StringBuilder(Directory.PACKAGE_NAME + "=?"); 38672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!directories.isEmpty()) { 38772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.append(" AND " + Directory._ID + " NOT IN("); 38872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (DirectoryInfo info: directories) { 38972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.append(info.id).append(","); 39072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 39172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.setLength(sb.length() - 1); // Remove the extra comma 39272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov sb.append(")"); 39372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 394a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki final int numDeleted = db.delete(Tables.DIRECTORIES, sb.toString(), 395a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki new String[] { packageInfo.packageName }); 396a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki if (DEBUG) { 397a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki Log.d(TAG, " deleted " + numDeleted + " stale rows"); 398a2d163097ec6630101c701723c170b5974262eb5Makoto Onuki } 39972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.setTransactionSuccessful(); 40072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } finally { 40172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.endTransaction(); 40272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 40372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 40472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov mContactsProvider.resetDirectoryCache(); 40537f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa return directories; 40672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 40772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 40872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 40972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Sends a {@link Directory#CONTENT_URI} request to a specific contact directory 41072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * provider and appends all discovered directories to the directoryInfo list. 41172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 41272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov protected void queryDirectoriesForAuthority( 41372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ArrayList<DirectoryInfo> directoryInfo, ProviderInfo provider) { 41472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Uri uri = new Uri.Builder().scheme("content") 41572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov .authority(provider.authority).appendPath("directories").build(); 41672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Cursor cursor = null; 41772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 41872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov cursor = mContext.getContentResolver().query( 41972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov uri, DirectoryQuery.PROJECTION, null, null, null); 42072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (cursor == null) { 42172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.i(TAG, providerDescription(provider) + " returned a NULL cursor."); 42272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } else { 42372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov while (cursor.moveToNext()) { 42472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov DirectoryInfo info = new DirectoryInfo(); 42572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.packageName = provider.packageName; 42672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.authority = provider.authority; 42772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME); 42872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE); 42972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME); 43072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.TYPE_RESOURCE_ID)) { 43172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID); 43272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 43372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.EXPORT_SUPPORT)) { 43472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT); 43572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov switch (exportSupport) { 43672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.EXPORT_SUPPORT_NONE: 43772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY: 43872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.EXPORT_SUPPORT_ANY_ACCOUNT: 43972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.exportSupport = exportSupport; 44072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov break; 44172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov default: 44272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.e(TAG, providerDescription(provider) 44372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov + " - invalid export support flag: " + exportSupport); 44472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 44572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 44672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.SHORTCUT_SUPPORT)) { 44772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov int shortcutSupport = cursor.getInt(DirectoryQuery.SHORTCUT_SUPPORT); 44872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov switch (shortcutSupport) { 44972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.SHORTCUT_SUPPORT_NONE: 45072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY: 45172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov case Directory.SHORTCUT_SUPPORT_FULL: 45272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.shortcutSupport = shortcutSupport; 45372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov break; 45472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov default: 45572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.e(TAG, providerDescription(provider) 45672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov + " - invalid shortcut support flag: " + shortcutSupport); 45772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 45872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 459994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov if (!cursor.isNull(DirectoryQuery.PHOTO_SUPPORT)) { 460994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT); 461994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov switch (photoSupport) { 462994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_NONE: 463994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY: 464994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY: 465994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov case Directory.PHOTO_SUPPORT_FULL: 466994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov info.photoSupport = photoSupport; 467994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov break; 468994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov default: 469994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov Log.e(TAG, providerDescription(provider) 470994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov + " - invalid photo support flag: " + photoSupport); 471994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov } 472994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov } 47372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov directoryInfo.add(info); 47472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 47572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 47672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } catch (Throwable t) { 47772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Log.e(TAG, providerDescription(provider) + " exception", t); 47872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } finally { 47972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (cursor != null) { 48072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov cursor.close(); 48172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 48272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 48372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 48472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 48572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov /** 48672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Updates the directories tables in the database to match the info received 48772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * from directory providers. 48872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */ 48972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov private void updateDirectories(SQLiteDatabase db, ArrayList<DirectoryInfo> directoryInfo) { 49072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // Insert or replace existing directories. 49172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov // This happens so infrequently that we can use a less-then-optimal one-a-time approach 49272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov for (DirectoryInfo info : directoryInfo) { 49372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov ContentValues values = new ContentValues(); 49472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.PACKAGE_NAME, info.packageName); 49572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.DIRECTORY_AUTHORITY, info.authority); 49672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.ACCOUNT_NAME, info.accountName); 49772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.ACCOUNT_TYPE, info.accountType); 49872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.TYPE_RESOURCE_ID, info.typeResourceId); 49972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.DISPLAY_NAME, info.displayName); 50072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.EXPORT_SUPPORT, info.exportSupport); 50172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov values.put(Directory.SHORTCUT_SUPPORT, info.shortcutSupport); 502994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov values.put(Directory.PHOTO_SUPPORT, info.photoSupport); 50372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 504e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov if (info.typeResourceId != 0) { 505ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki String resourceName = getResourceNameById(info.packageName, info.typeResourceId); 506e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov values.put(DirectoryColumns.TYPE_RESOURCE_NAME, resourceName); 507e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov } 508e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov 50972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Cursor cursor = db.query(Tables.DIRECTORIES, new String[] { Directory._ID }, 51072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov Directory.PACKAGE_NAME + "=? AND " + Directory.DIRECTORY_AUTHORITY + "=? AND " 51172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov + Directory.ACCOUNT_NAME + "=? AND " + Directory.ACCOUNT_TYPE + "=?", 51272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov new String[] { 51372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.packageName, info.authority, info.accountName, info.accountType }, 51472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov null, null, null); 51572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov try { 51672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov long id; 51772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov if (cursor.moveToFirst()) { 51872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov id = cursor.getLong(0); 51972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov db.update(Tables.DIRECTORIES, values, Directory._ID + "=?", 52072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov new String[] { String.valueOf(id) }); 52172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } else { 52272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov id = db.insert(Tables.DIRECTORIES, null, values); 52372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 52472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov info.id = id; 52572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } finally { 52672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov cursor.close(); 52772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 52872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 52972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 53072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov 53172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov protected String providerDescription(ProviderInfo provider) { 53272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov return "Directory provider " + provider.packageName + "(" + provider.authority + ")"; 53372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov } 53472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov} 535