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
19e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns;
2072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
2172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport com.google.android.collect.Lists;
22ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onukiimport com.google.android.collect.Sets;
23ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onukiimport com.google.common.annotations.VisibleForTesting;
2472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
2572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.ContentValues;
2672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.Context;
2772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageInfo;
2872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageManager;
2972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.PackageManager.NameNotFoundException;
3072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.content.pm.ProviderInfo;
31e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.content.res.Resources;
32e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.content.res.Resources.NotFoundException;
3372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.database.Cursor;
3472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.database.sqlite.SQLiteDatabase;
3572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.net.Uri;
3672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.os.Bundle;
3772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.os.SystemClock;
38e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.provider.ContactsContract;
3972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.provider.ContactsContract.Directory;
40e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikovimport android.text.TextUtils;
4172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport android.util.Log;
4272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
4372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport java.util.ArrayList;
4472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikovimport java.util.List;
45ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onukiimport java.util.Set;
4672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
4772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov/**
4872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov * Manages the contents of the {@link Directory} table.
4972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov */
5043368a3f9e05a979e454e278d6a0e8475f08923dDave Santoro// TODO: Determine whether directories need to be aware of data sets under the account.
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 PROPERTY_DIRECTORY_SCAN_COMPLETE = "directoryScanComplete";
573d67ff829e8acb0f650f155c3c0d377c0f46507aDmitri Plotnikov    public static final String CONTACT_DIRECTORY_META_DATA = "android.content.ContactDirectory";
5872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
5972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public class DirectoryInfo {
6072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        long id;
6172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        String packageName;
6272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        String authority;
6372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        String accountName;
6472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        String accountType;
6572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        String displayName;
6672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        int typeResourceId;
6772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        int exportSupport = Directory.EXPORT_SUPPORT_NONE;
6872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        int shortcutSupport = Directory.SHORTCUT_SUPPORT_NONE;
69994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov        int photoSupport = Directory.PHOTO_SUPPORT_NONE;
70ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        @Override
71ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        public String toString() {
72ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            return "DirectoryInfo:"
73ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    + "id=" + id
74ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    + " packageName=" + accountType
75ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    + " authority=" + authority
76ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    + " accountName=***"
77ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    + " accountType=" + accountType;
78ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        }
7972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
8072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
8172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private final static class DirectoryQuery {
8272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        public static final String[] PROJECTION = {
8372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Directory.ACCOUNT_NAME,
8472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Directory.ACCOUNT_TYPE,
8572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Directory.DISPLAY_NAME,
8672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Directory.TYPE_RESOURCE_ID,
8772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Directory.EXPORT_SUPPORT,
8872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Directory.SHORTCUT_SUPPORT,
89994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov            Directory.PHOTO_SUPPORT,
9072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        };
9172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
9272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        public static final int ACCOUNT_NAME = 0;
9372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        public static final int ACCOUNT_TYPE = 1;
9472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        public static final int DISPLAY_NAME = 2;
9572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        public static final int TYPE_RESOURCE_ID = 3;
9672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        public static final int EXPORT_SUPPORT = 4;
9772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        public static final int SHORTCUT_SUPPORT = 5;
98994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov        public static final int PHOTO_SUPPORT = 6;
9972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
10072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
10172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private final ContactsProvider2 mContactsProvider;
102ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    private final Context mContext;
103ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    private final PackageManager mPackageManager;
10472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
10572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public ContactDirectoryManager(ContactsProvider2 contactsProvider) {
106ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        mContactsProvider = contactsProvider;
107ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        mContext = contactsProvider.getContext();
108ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        mPackageManager = mContext.getPackageManager();
10972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
11072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
111e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    public ContactsDatabaseHelper getDbHelper() {
112e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        return (ContactsDatabaseHelper) mContactsProvider.getDatabaseHelper();
113e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    }
114e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
11572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /**
11672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * Scans all packages owned by the specified calling UID looking for contact
11772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * directory providers.
11872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     */
11972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void scanPackagesByUid(int callingUid) {
120ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        final String[] callerPackages = mPackageManager.getPackagesForUid(callingUid);
12172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        if (callerPackages != null) {
12272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            for (int i = 0; i < callerPackages.length; i++) {
12372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                onPackageChanged(callerPackages[i]);
12472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            }
12572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
12672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
12772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
12872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /**
129e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     * Scans through existing directories to see if the cached resource IDs still
130e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     * match their original resource names.  If not - plays it safe by refreshing all directories.
131e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     *
132e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     * @return true if all resource IDs were found valid
133e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     */
134e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    private boolean areTypeResourceIdsValid() {
135e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        SQLiteDatabase db = getDbHelper().getReadableDatabase();
136e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
137e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        Cursor cursor = db.query(Tables.DIRECTORIES,
138e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                new String[] { Directory.TYPE_RESOURCE_ID, Directory.PACKAGE_NAME,
139e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                        DirectoryColumns.TYPE_RESOURCE_NAME }, null, null, null, null, null);
140e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        try {
141e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            while (cursor.moveToNext()) {
142e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                int resourceId = cursor.getInt(0);
143e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                if (resourceId != 0) {
144e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                    String packageName = cursor.getString(1);
145e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                    String storedResourceName = cursor.getString(2);
146ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    String resourceName = getResourceNameById(packageName, resourceId);
147e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                    if (!TextUtils.equals(storedResourceName, resourceName)) {
148e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                        return false;
149e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                    }
150e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                }
151e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            }
152e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        } finally {
153e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            cursor.close();
154e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        }
155e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
156e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        return true;
157e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    }
158e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
159e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    /**
160e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     * Given a resource ID, returns the corresponding resource name or null if the package name /
161e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     * resource ID combination is invalid.
162e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov     */
163ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    private String getResourceNameById(String packageName, int resourceId) {
164e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        try {
165ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            Resources resources = mPackageManager.getResourcesForApplication(packageName);
166e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            return resources.getResourceName(resourceId);
167e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        } catch (NameNotFoundException e) {
168e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            return null;
169e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        } catch (NotFoundException e) {
170e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            return null;
171e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        }
172e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    }
173e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
174e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    /**
175afb84050536a4472c13efc0e996d31132d254605Dmitri Plotnikov     * Scans all packages for directory content providers.
17672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     */
177bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    public void scanAllPackages(boolean rescan) {
178bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        if (rescan || !areTypeResourceIdsValid()) {
179bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov            getDbHelper().setProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "0");
180bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        }
181bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
182bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov        scanAllPackagesIfNeeded();
183bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov    }
184bf732767b4d4d7104e4723bda7d3b0eb0f909997Dmitri Plotnikov
18572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private void scanAllPackagesIfNeeded() {
186e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        String scanComplete = getDbHelper().getProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "0");
18772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        if (!"0".equals(scanComplete)) {
18872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            return;
18972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
19072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
19172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        long start = SystemClock.currentThreadTimeMillis();
19272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        int count = scanAllPackages();
193e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        getDbHelper().setProperty(PROPERTY_DIRECTORY_SCAN_COMPLETE, "1");
19472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        long end = SystemClock.currentThreadTimeMillis();
19572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        Log.i(TAG, "Discovered " + count + " contact directories in " + (end - start) + "ms");
19672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
19772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        // Announce the change to listeners of the contacts authority
19872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactsProvider.notifyChange(false);
19972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
20072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
201ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    @VisibleForTesting
202ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    static boolean isDirectoryProvider(ProviderInfo provider) {
203ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        Bundle metaData = provider.metaData;
204ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        if (metaData == null) return false;
205ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
206ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        Object trueFalse = metaData.get(CONTACT_DIRECTORY_META_DATA);
207ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        return trueFalse != null && Boolean.TRUE.equals(trueFalse);
208ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    }
209ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
210ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    /**
211ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki     * @return List of packages that contain a directory provider.
212ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki     */
213ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    @VisibleForTesting
214ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    static Set<String> getDirectoryProviderPackages(PackageManager pm) {
215ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        final Set<String> ret = Sets.newHashSet();
216ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
217ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        // Note to 3rd party developers:
218ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        // queryContentProviders() is a public API but this method doesn't officially support
219ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        // the GET_META_DATA flag.  Don't use it in your app.
220ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        final List<ProviderInfo> providers = pm.queryContentProviders(null, 0,
221ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                PackageManager.GET_META_DATA);
222ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        if (providers == null) {
223ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            return ret;
224ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        }
225ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        for (ProviderInfo provider : providers) {
226ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            if (isDirectoryProvider(provider)) {
227ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                ret.add(provider.packageName);
228ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            }
229ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        }
230ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        if (DEBUG) {
231ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            Log.d(TAG, "Found " + ret.size() + " directory provider packages");
232ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        }
233ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
234ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        return ret;
235ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    }
236ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
237ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki    @VisibleForTesting
23872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    int scanAllPackages() {
239e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        SQLiteDatabase db = getDbHelper().getWritableDatabase();
240e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        insertDefaultDirectory(db);
241e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        insertLocalInvisibleDirectory(db);
242e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
24372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        int count = 0;
244ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
245ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        // Prepare query strings for removing stale rows which don't correspond to existing
246ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        // directories.
247ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        StringBuilder deleteWhereBuilder = new StringBuilder();
248ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        ArrayList<String> deleteWhereArgs = new ArrayList<String>();
249ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        deleteWhereBuilder.append("NOT (" + Directory._ID + "=? OR " + Directory._ID + "=?");
250ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        deleteWhereArgs.add(String.valueOf(Directory.DEFAULT));
251ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        deleteWhereArgs.add(String.valueOf(Directory.LOCAL_INVISIBLE));
252ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        final String wherePart = "(" + Directory.PACKAGE_NAME + "=? AND "
253ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                + Directory.DIRECTORY_AUTHORITY + "=? AND "
254ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                + Directory.ACCOUNT_NAME + "=? AND "
255ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                + Directory.ACCOUNT_TYPE + "=?)";
256ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
257ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        for (String packageName : getDirectoryProviderPackages(mPackageManager)) {
258ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            if (DEBUG) Log.d(TAG, "package=" + packageName);
259ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
260ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            final PackageInfo packageInfo;
261ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            try {
262ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                packageInfo = mPackageManager.getPackageInfo(packageName,
263ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                        PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA);
264ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                if (packageInfo == null) continue;  // Just in case...
265ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            } catch (NameNotFoundException nnfe) {
266ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                continue; // Application just removed?
26772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            }
26837f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa
269ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            List<DirectoryInfo> directories = updateDirectoriesForPackage(packageInfo, true);
270ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            if (directories != null && !directories.isEmpty()) {
271ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                count += directories.size();
272ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
273ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                // We shouldn't delete rows for existing directories.
274ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                for (DirectoryInfo info : directories) {
275ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    if (DEBUG) Log.d(TAG, "  directory=" + info);
276ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    deleteWhereBuilder.append(" OR ");
277ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    deleteWhereBuilder.append(wherePart);
278ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    deleteWhereArgs.add(info.packageName);
279ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    deleteWhereArgs.add(info.authority);
280ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    deleteWhereArgs.add(info.accountName);
281ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    deleteWhereArgs.add(info.accountType);
282ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                }
283ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            }
28472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
285ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
286ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        deleteWhereBuilder.append(")");  // Close "NOT ("
287ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
288ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        int deletedRows = db.delete(Tables.DIRECTORIES, deleteWhereBuilder.toString(),
289ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                deleteWhereArgs.toArray(new String[0]));
290ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki        Log.i(TAG, "deleted " + deletedRows
291ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                + " stale rows which don't have any relevant directory");
29272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return count;
29372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
29472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
295e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    private void insertDefaultDirectory(SQLiteDatabase db) {
296e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        ContentValues values = new ContentValues();
297e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory._ID, Directory.DEFAULT);
298e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.PACKAGE_NAME, mContext.getApplicationInfo().packageName);
299e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY);
300e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.TYPE_RESOURCE_ID, R.string.default_directory);
301e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(DirectoryColumns.TYPE_RESOURCE_NAME,
302e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                mContext.getResources().getResourceName(R.string.default_directory));
303e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE);
304e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL);
305e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL);
306e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        db.replace(Tables.DIRECTORIES, null, values);
307e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    }
308e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
309e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    private void insertLocalInvisibleDirectory(SQLiteDatabase db) {
310e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        ContentValues values = new ContentValues();
311e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory._ID, Directory.LOCAL_INVISIBLE);
312e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.PACKAGE_NAME, mContext.getApplicationInfo().packageName);
313e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY);
314e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.TYPE_RESOURCE_ID, R.string.local_invisible_directory);
315e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(DirectoryColumns.TYPE_RESOURCE_NAME,
316e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                mContext.getResources().getResourceName(R.string.local_invisible_directory));
317e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE);
318e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL);
319e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL);
320e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        db.replace(Tables.DIRECTORIES, null, values);
321e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    }
322e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
32372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /**
32472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * Scans the specified package for content directories.  The package may have
32572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * already been removed, so packageName does not necessarily correspond to
32672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * an installed package.
32772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     */
32872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    public void onPackageChanged(String packageName) {
32972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        PackageInfo packageInfo = null;
33072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
33172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        try {
332ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki            packageInfo = mPackageManager.getPackageInfo(packageName,
33372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA);
33472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        } catch (NameNotFoundException e) {
33572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            // The package got removed
33672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            packageInfo = new PackageInfo();
33772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            packageInfo.packageName = packageName;
33872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
33972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
34072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        updateDirectoriesForPackage(packageInfo, false);
341e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov    }
34272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
343ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki
34472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /**
34572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * Scans the specified package for content directories and updates the {@link Directory}
34672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * table accordingly.
34772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     */
34837f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa    private List<DirectoryInfo> updateDirectoriesForPackage(
34937f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa            PackageInfo packageInfo, boolean initialScan) {
35072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        ArrayList<DirectoryInfo> directories = Lists.newArrayList();
35172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
35272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        ProviderInfo[] providers = packageInfo.providers;
35372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        if (providers != null) {
35472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            for (ProviderInfo provider : providers) {
355ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                if (isDirectoryProvider(provider)) {
356ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                    queryDirectoriesForAuthority(directories, provider);
35772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                }
35872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            }
35972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
36072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
36172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        if (directories.size() == 0 && initialScan) {
36237f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa            return null;
36372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
36472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
365e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov        SQLiteDatabase db = getDbHelper().getWritableDatabase();
36672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        db.beginTransaction();
36772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        try {
36872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            updateDirectories(db, directories);
36972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            // Clear out directories that are no longer present
37072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            StringBuilder sb = new StringBuilder(Directory.PACKAGE_NAME + "=?");
37172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            if (!directories.isEmpty()) {
37272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                sb.append(" AND " + Directory._ID + " NOT IN(");
37372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                for (DirectoryInfo info: directories) {
37472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    sb.append(info.id).append(",");
37572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                }
37672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                sb.setLength(sb.length() - 1);  // Remove the extra comma
37772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                sb.append(")");
37872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            }
37972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            db.delete(Tables.DIRECTORIES, sb.toString(), new String[] { packageInfo.packageName });
38072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            db.setTransactionSuccessful();
38172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        } finally {
38272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            db.endTransaction();
38372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
38472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
38572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        mContactsProvider.resetDirectoryCache();
38637f0bdbb0a99b12abcc319f49a2caa5ab648add0Daisuke Miyakawa        return directories;
38772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
38872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
38972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /**
39072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * Sends a {@link Directory#CONTENT_URI} request to a specific contact directory
39172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * provider and appends all discovered directories to the directoryInfo list.
39272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     */
39372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    protected void queryDirectoriesForAuthority(
39472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            ArrayList<DirectoryInfo> directoryInfo, ProviderInfo provider) {
39572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        Uri uri = new Uri.Builder().scheme("content")
39672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                .authority(provider.authority).appendPath("directories").build();
39772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        Cursor cursor = null;
39872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        try {
39972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            cursor = mContext.getContentResolver().query(
40072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    uri, DirectoryQuery.PROJECTION, null, null, null);
40172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            if (cursor == null) {
40272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                Log.i(TAG, providerDescription(provider) + " returned a NULL cursor.");
40372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            } else {
40472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                while (cursor.moveToNext()) {
40572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    DirectoryInfo info = new DirectoryInfo();
40672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    info.packageName = provider.packageName;
40772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    info.authority = provider.authority;
40872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
40972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
41072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    info.displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
41172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    if (!cursor.isNull(DirectoryQuery.TYPE_RESOURCE_ID)) {
41272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                        info.typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
41372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    }
41472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    if (!cursor.isNull(DirectoryQuery.EXPORT_SUPPORT)) {
41572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                        int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
41672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                        switch (exportSupport) {
41772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            case Directory.EXPORT_SUPPORT_NONE:
41872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY:
41972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            case Directory.EXPORT_SUPPORT_ANY_ACCOUNT:
42072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                info.exportSupport = exportSupport;
42172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                break;
42272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            default:
42372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                Log.e(TAG, providerDescription(provider)
42472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                        + " - invalid export support flag: " + exportSupport);
42572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                        }
42672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    }
42772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    if (!cursor.isNull(DirectoryQuery.SHORTCUT_SUPPORT)) {
42872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                        int shortcutSupport = cursor.getInt(DirectoryQuery.SHORTCUT_SUPPORT);
42972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                        switch (shortcutSupport) {
43072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            case Directory.SHORTCUT_SUPPORT_NONE:
43172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            case Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY:
43272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            case Directory.SHORTCUT_SUPPORT_FULL:
43372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                info.shortcutSupport = shortcutSupport;
43472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                break;
43572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            default:
43672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                Log.e(TAG, providerDescription(provider)
43772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                                        + " - invalid shortcut support flag: " + shortcutSupport);
43872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                        }
43972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    }
440994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                    if (!cursor.isNull(DirectoryQuery.PHOTO_SUPPORT)) {
441994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                        int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT);
442994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                        switch (photoSupport) {
443994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                            case Directory.PHOTO_SUPPORT_NONE:
444994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                            case Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY:
445994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                            case Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY:
446994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                            case Directory.PHOTO_SUPPORT_FULL:
447994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                                info.photoSupport = photoSupport;
448994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                                break;
449994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                            default:
450994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                                Log.e(TAG, providerDescription(provider)
451994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                                        + " - invalid photo support flag: " + photoSupport);
452994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                        }
453994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov                    }
45472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    directoryInfo.add(info);
45572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                }
45672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            }
45772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        } catch (Throwable t) {
45872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Log.e(TAG, providerDescription(provider) + " exception", t);
45972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        } finally {
46072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            if (cursor != null) {
46172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                cursor.close();
46272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            }
46372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
46472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
46572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
46672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    /**
46772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * Updates the directories tables in the database to match the info received
46872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     * from directory providers.
46972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov     */
47072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    private void updateDirectories(SQLiteDatabase db, ArrayList<DirectoryInfo> directoryInfo) {
47172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        // Insert or replace existing directories.
47272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        // This happens so infrequently that we can use a less-then-optimal one-a-time approach
47372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        for (DirectoryInfo info : directoryInfo) {
47472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            ContentValues values = new ContentValues();
47572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.PACKAGE_NAME, info.packageName);
47672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.DIRECTORY_AUTHORITY, info.authority);
47772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.ACCOUNT_NAME, info.accountName);
47872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.ACCOUNT_TYPE, info.accountType);
47972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.TYPE_RESOURCE_ID, info.typeResourceId);
48072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.DISPLAY_NAME, info.displayName);
48172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.EXPORT_SUPPORT, info.exportSupport);
48272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            values.put(Directory.SHORTCUT_SUPPORT, info.shortcutSupport);
483994c32a995a85937faf59e37a9fc88b526470d7dDmitri Plotnikov            values.put(Directory.PHOTO_SUPPORT, info.photoSupport);
48472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
485e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            if (info.typeResourceId != 0) {
486ac6f0b8a92fccb3611a7841d0f04e4ac24a58c91Makoto Onuki                String resourceName = getResourceNameById(info.packageName, info.typeResourceId);
487e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov                values.put(DirectoryColumns.TYPE_RESOURCE_NAME, resourceName);
488e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov            }
489e0e24418cba10a5184e2966aaa32d5458fa6a387Dmitri Plotnikov
49072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            Cursor cursor = db.query(Tables.DIRECTORIES, new String[] { Directory._ID },
49172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    Directory.PACKAGE_NAME + "=? AND " + Directory.DIRECTORY_AUTHORITY + "=? AND "
49272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            + Directory.ACCOUNT_NAME + "=? AND " + Directory.ACCOUNT_TYPE + "=?",
49372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    new String[] {
49472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            info.packageName, info.authority, info.accountName, info.accountType },
49572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    null, null, null);
49672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            try {
49772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                long id;
49872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                if (cursor.moveToFirst()) {
49972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    id = cursor.getLong(0);
50072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    db.update(Tables.DIRECTORIES, values, Directory._ID + "=?",
50172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                            new String[] { String.valueOf(id) });
50272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                } else {
50372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                    id = db.insert(Tables.DIRECTORIES, null, values);
50472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                }
50572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                info.id = id;
50672e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            } finally {
50772e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov                cursor.close();
50872e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov            }
50972e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        }
51072e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
51172e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov
51272e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    protected String providerDescription(ProviderInfo provider) {
51372e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov        return "Directory provider " + provider.packageName + "(" + provider.authority + ")";
51472e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov    }
51572e3003a810fb4793a1513d17a40f8ab83d7d0afDmitri Plotnikov}
516