diff options
Diffstat (limited to 'src/com/android/providers/contacts/ContactsProvider2.java')
-rw-r--r-- | src/com/android/providers/contacts/ContactsProvider2.java | 246 |
1 files changed, 245 insertions, 1 deletions
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 40be58f..1e4a613 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -35,6 +35,7 @@ import android.content.OperationApplicationException; import android.content.SharedPreferences; import android.content.SyncAdapterType; import android.content.UriMatcher; +import android.content.pm.PackageManager; import android.content.res.AssetFileDescriptor; import android.content.res.Configuration; import android.database.CharArrayBuffer; @@ -49,7 +50,9 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteStatement; import android.net.Uri; +import android.net.Uri.Builder; import android.os.AsyncTask; +import android.os.Binder; import android.os.Bundle; import android.os.MemoryFile; import android.os.RemoteException; @@ -64,6 +67,7 @@ import android.provider.ContactsContract.AggregationExceptions; import android.provider.ContactsContract.ContactCounts; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.Directory; import android.provider.ContactsContract.DisplayNameSources; import android.provider.ContactsContract.FullNameStyle; import android.provider.ContactsContract.Groups; @@ -245,6 +249,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private static final int PROVIDER_STATUS = 16001; + private static final int DIRECTORIES = 17001; + private static final int DIRECTORIES_ID = 17002; + private static final String SELECTION_FAVORITES_GROUPS_BY_RAW_CONTACT_ID = RawContactsColumns.CONCRETE_ID + "=? AND " + GroupsColumns.CONCRETE_ACCOUNT_NAME @@ -410,6 +417,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private static final HashMap<String, String> sStatusUpdatesProjectionMap; /** Contains Live Folders columns */ private static final HashMap<String, String> sLiveFoldersProjectionMap; + /** Contains {@link Directory} columns */ + private static final HashMap<String, String> sDirectoryProjectionMap; // where clause to update the status_updates table private static final String WHERE_CLAUSE_FOR_STATUS_UPDATES_TABLE = @@ -537,6 +546,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun LIVE_FOLDERS_CONTACTS_FAVORITES); matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS); + + matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES); + matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID); } static { @@ -987,12 +999,33 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // for contacts without a photo // sLiveFoldersProjectionMap.put(LiveFolders.ICON_BITMAP, // Photos.DATA + " AS " + LiveFolders.ICON_BITMAP); + + sDirectoryProjectionMap = new HashMap<String, String>(); + sDirectoryProjectionMap.put(Directory._ID, Directory._ID); + sDirectoryProjectionMap.put(Directory.PACKAGE_NAME, Directory.PACKAGE_NAME); + sDirectoryProjectionMap.put(Directory.TYPE_RESOURCE_ID, Directory.TYPE_RESOURCE_ID); + sDirectoryProjectionMap.put(Directory.DISPLAY_NAME, Directory.DISPLAY_NAME); + sDirectoryProjectionMap.put(Directory.DIRECTORY_AUTHORITY, Directory.DIRECTORY_AUTHORITY); + sDirectoryProjectionMap.put(Directory.ACCOUNT_TYPE, Directory.ACCOUNT_TYPE); + sDirectoryProjectionMap.put(Directory.ACCOUNT_NAME, Directory.ACCOUNT_NAME); + sDirectoryProjectionMap.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT); } private static void addProjection(HashMap<String, String> map, String toField, String fromField) { map.put(toField, fromField + " AS " + toField); } + private static class DirectoryInfo { + String authority; + String accountName; + String accountType; + } + + /** + * Cached information about contact directories. + */ + private HashMap<String, DirectoryInfo> mDirectoryCache; + /** * Handles inserts and update for a specific Data type. */ @@ -2509,6 +2542,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun break; } + case DIRECTORIES: { + id = insertDirectory(uri, values); + break; + } + default: mSyncToNetwork = true; return mLegacyApiSupport.insert(uri, values); @@ -2586,6 +2624,23 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return mAccount; } + private long insertDirectory(Uri uri, ContentValues values) { + String packageName = values.getAsString(Directory.PACKAGE_NAME); + if (packageName == null) { + throw new IllegalArgumentException(mDbHelper.exceptionMessage( + "The Directory.PACKAGE_NAME field is required", uri)); + } + + if (!verifyCallingPackage(packageName)) { + throw new IllegalArgumentException(mDbHelper.exceptionMessage( + "The supplied package name " + packageName + + " does not match the name of the calling package", uri)); + } + + mDirectoryCache = null; + return mDb.insert(Tables.DIRECTORIES, null, values); + } + /** * Inserts an item in the contacts table * @@ -3508,6 +3563,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return deleteStatusUpdates(selection, selectionArgs); } + case DIRECTORIES_ID: { + return deleteDirectory(uri); + } + default: { mSyncToNetwork = true; return mLegacyApiSupport.delete(uri, selection, selectionArgs); @@ -3515,6 +3574,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } } + private int deleteDirectory(Uri uri) { + mDirectoryCache = null; + verifyCallingPackageForDirectory(uri); + mSelectionArgs1[0] = String.valueOf(ContentUris.parseId(uri)); + return mDb.delete(Tables.DIRECTORIES, Directory._ID + "=?", mSelectionArgs1); + } + public int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) { mGroupIdCache.clear(); final long groupMembershipMimetypeId = mDbHelper @@ -3743,6 +3809,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun break; } + case DIRECTORIES_ID: { + count = updateDirectory(uri, values, selection, selectionArgs); + break; + } + default: { mSyncToNetwork = true; return mLegacyApiSupport.update(uri, values, selection, selectionArgs); @@ -4159,6 +4230,21 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return 1; } + private int updateDirectory(Uri uri, ContentValues values, String selection, + String[] selectionArgs) { + + verifyCallingPackageForDirectory(uri); + + mDirectoryCache = null; + + mValues.clear(); + mValues.putAll(values); + mValues.remove(Directory.PACKAGE_NAME); + + mSelectionArgs1[0] = String.valueOf(ContentUris.parseId(uri)); + return mDb.update(Tables.DIRECTORIES, mValues, Directory._ID + "=?", mSelectionArgs1); + } + public void onAccountsUpdated(Account[] accounts) { // TODO : Check the unit test. boolean accountsChanged = false; @@ -4212,6 +4298,11 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun "DELETE FROM " + Tables.ACCOUNTS + " WHERE " + RawContacts.ACCOUNT_NAME + "=?" + " AND " + RawContacts.ACCOUNT_TYPE + "=?", params); + mDb.execSQL( + "DELETE FROM " + Tables.DIRECTORIES + + " WHERE " + Directory.ACCOUNT_NAME + "=?" + + " AND " + Directory.ACCOUNT_TYPE + "=?", params); + mDirectoryCache = null; } // Find all aggregated contacts that used to contain the raw contacts @@ -4252,6 +4343,22 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mAccountWritability.clear(); } + public void onPackageUninstalled(String packageName) { + mDb.beginTransaction(); + try { + mSelectionArgs1[0] = packageName; + int count = + mDb.delete(Tables.DIRECTORIES, Directory.PACKAGE_NAME + "=?", mSelectionArgs1); + if (count != 0) { + Log.i(TAG, "Removed contact directories for package " + packageName); + } + mDb.setTransactionSuccessful(); + } finally { + mDirectoryCache = null; + mDb.endTransaction(); + } + } + /** * Finds all distinct accounts present in the specified table. */ @@ -4297,6 +4404,76 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY); + if (directory == null || directory.equals("0")) { + return queryLocal(uri, projection, selection, selectionArgs, sortOrder, false); + } else if (directory.equals("1")) { + return queryLocal(uri, projection, selection, selectionArgs, sortOrder, true); + } + + DirectoryInfo directoryInfo = getDirectoryAuthority(directory); + if (directoryInfo == null) { + throw new IllegalArgumentException( + mDbHelper.exceptionMessage("Invalid directory ID", uri)); + } + + Builder builder = new Uri.Builder(); + builder.scheme(ContentResolver.SCHEME_CONTENT); + builder.authority(directoryInfo.authority); + builder.encodedPath(uri.getEncodedPath()); + if (directoryInfo.accountName != null) { + builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName); + } + if (directoryInfo.accountType != null) { + builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType); + } + Uri directoryUri = builder.build(); + return getContext().getContentResolver().query(directoryUri, projection, selection, + selectionArgs, sortOrder); + } + + private static final class DirectoryQuery { + public static final String[] COLUMNS = new String[] { + Directory._ID, + Directory.DIRECTORY_AUTHORITY, + Directory.ACCOUNT_NAME, + Directory.ACCOUNT_TYPE + }; + + public static final int DIRECTORY_ID = 0; + public static final int AUTHORITY = 1; + public static final int ACCOUNT_NAME = 2; + public static final int ACCOUNT_TYPE = 3; + } + + /** + * Reads and caches directory information for the database. + */ + private DirectoryInfo getDirectoryAuthority(String directoryId) { + if (mDirectoryCache == null) { + mDirectoryCache = new HashMap<String, DirectoryInfo>(); + Cursor cursor = mDb.query(Tables.DIRECTORIES, + DirectoryQuery.COLUMNS, + null, null, null, null, null); + try { + while (cursor.moveToNext()) { + DirectoryInfo info = new DirectoryInfo(); + String id = cursor.getString(DirectoryQuery.DIRECTORY_ID); + info.authority = cursor.getString(DirectoryQuery.AUTHORITY); + info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME); + info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE); + mDirectoryCache.put(id, info); + } + } finally { + cursor.close(); + } + } + + return mDirectoryCache.get(directoryId); + } + + public Cursor queryLocal(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder, boolean hiddenOnly) { if (VERBOSE_LOGGING) { Log.v(TAG, "query: " + uri); } @@ -4317,6 +4494,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun case CONTACTS: { setTablesAndProjectionMapForContacts(qb, uri, projection); + if (hiddenOnly) { + qb.appendWhere(Contacts.IN_VISIBLE_GROUP + "=0"); + } break; } @@ -4437,6 +4617,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun filterParam = uri.getLastPathSegment(); } setTablesAndProjectionMapForContactsWithSnippet(qb, uri, projection, filterParam); + if (hiddenOnly) { + qb.appendWhere(Contacts.IN_VISIBLE_GROUP + "=0"); + } break; } @@ -4856,6 +5039,21 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return queryProviderStatus(uri, projection); } + case DIRECTORIES : { + qb.setTables(Tables.DIRECTORIES); + qb.setProjectionMap(sDirectoryProjectionMap); + break; + } + + case DIRECTORIES_ID : { + long directoryId = ContentUris.parseId(uri); + qb.setTables(Tables.DIRECTORIES); + qb.setProjectionMap(sDirectoryProjectionMap); + selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(directoryId)); + qb.appendWhere(Directory._ID + "=?"); + break; + } + default: return mLegacyApiSupport.query(uri, projection, selection, selectionArgs, sortOrder, limit); @@ -5792,7 +5990,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return SearchManager.SUGGEST_MIME_TYPE; case SEARCH_SHORTCUT: return SearchManager.SHORTCUT_MIME_TYPE; - + case DIRECTORIES: + return Directory.CONTENT_TYPE; + case DIRECTORIES_ID: + return Directory.CONTENT_ITEM_TYPE; default: return mLegacyApiSupport.getType(uri); } @@ -6081,6 +6282,49 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return writable; } + private void verifyCallingPackageForDirectory(Uri uri) { + String packageName = null; + mSelectionArgs1[0] = String.valueOf(ContentUris.parseId(uri)); + Cursor cursor = mDb.query(Tables.DIRECTORIES, + new String[] { Directory.PACKAGE_NAME }, Directory._ID + "=?", + mSelectionArgs1, null, null, null); + try { + if (cursor.moveToFirst()) { + packageName = cursor.getString(0); + } + } finally { + cursor.close(); + } + + if (packageName == null) { + throw new IllegalArgumentException(mDbHelper.exceptionMessage( + "Directory not found", uri)); + } + + if (!verifyCallingPackage(packageName)) { + throw new IllegalArgumentException(mDbHelper.exceptionMessage( + "The supplied package name " + packageName + + " does not match the name of the calling package", uri)); + } + } + + /** + * Returns true iff the package is one of the packages owned by the caller. + */ + private boolean verifyCallingPackage(String packageName) { + final PackageManager pm = getContext().getPackageManager(); + final int callingUid = Binder.getCallingUid(); + final String[] callerPackages = pm.getPackagesForUid(callingUid); + if (callerPackages != null) { + for (int i = 0; i < callerPackages.length; i++) { + if (packageName.equals(callerPackages[i])) { + return true; + } + } + } + return false; + } + /* package */ static boolean readBooleanQueryParameter(Uri uri, String parameter, boolean defaultValue) { |