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