aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2021-08-14 06:30:58 +0000
committerXin Li <delphij@google.com>2021-08-14 06:30:58 +0000
commit0ba8ccd088e7e132297e5b56dfa46e0ad66e8caf (patch)
tree14abe8bdae813b7333ad9a9d2aa271146f2cfd5e
parentc01031b4dd7043ac0ad3bbde41054ed182117a45 (diff)
parent586fee1e34818160e2cef368cae1ca17acbacba7 (diff)
downloadContactsProvider-temp_sam_202323961.tar.gz
Merge sc-dev-plus-aosp-without-vendor@7634622temp_sam_202323961
Merged-In: I73a2d8b17abcc9f78d055634687e29c5928172a8 Change-Id: I23959c7186eec403c91b0431837a8705e7d07b05
-rw-r--r--Android.bp1
-rw-r--r--AndroidManifest.xml27
-rw-r--r--res/values-hi/strings.xml2
-rw-r--r--res/values-te/strings.xml4
-rw-r--r--src/com/android/providers/contacts/AccountWithDataSet.java23
-rw-r--r--src/com/android/providers/contacts/CallComposerLocationProvider.java194
-rw-r--r--src/com/android/providers/contacts/CallLogDatabaseHelper.java16
-rw-r--r--src/com/android/providers/contacts/CallLogProvider.java407
-rw-r--r--src/com/android/providers/contacts/ContactDirectoryManager.java3
-rw-r--r--src/com/android/providers/contacts/ContactMetadataProvider.java443
-rw-r--r--src/com/android/providers/contacts/ContactsDatabaseHelper.java260
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java448
-rw-r--r--src/com/android/providers/contacts/DataRowHandler.java12
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java5
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForEmail.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForIdentity.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForNickname.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForOrganization.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForPhoto.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForStructuredName.java4
-rw-r--r--src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java4
-rw-r--r--src/com/android/providers/contacts/DbModifierWithNotification.java32
-rw-r--r--src/com/android/providers/contacts/MetadataEntryParser.java296
-rw-r--r--src/com/android/providers/contacts/ProfileProvider.java4
-rw-r--r--src/com/android/providers/contacts/SearchIndexManager.java6
-rw-r--r--src/com/android/providers/contacts/TransactionContext.java28
-rw-r--r--src/com/android/providers/contacts/VoicemailContentProvider.java14
-rw-r--r--src/com/android/providers/contacts/VoicemailPermissions.java9
-rw-r--r--src/com/android/providers/contacts/util/LogFields.java144
-rw-r--r--src/com/android/providers/contacts/util/LogUtils.java97
-rw-r--r--tests/assets/test1/testFileDeviceContactMetadataJSON.txt69
-rw-r--r--tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java4
-rw-r--r--tests/src/com/android/providers/contacts/CallLogProviderTest.java5
-rw-r--r--tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java586
-rw-r--r--tests/src/com/android/providers/contacts/ContactMetadataProviderTestable.java23
-rw-r--r--tests/src/com/android/providers/contacts/ContactsActor.java5
-rw-r--r--tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java23
-rw-r--r--tests/src/com/android/providers/contacts/ContactsProvider2Test.java529
-rw-r--r--tests/src/com/android/providers/contacts/MetadataEntryParserTest.java304
-rw-r--r--tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java9
42 files changed, 1341 insertions, 2727 deletions
diff --git a/Android.bp b/Android.bp
index bd6e2498..4b345e61 100644
--- a/Android.bp
+++ b/Android.bp
@@ -19,6 +19,7 @@ license {
android_app {
name: "ContactsProvider",
+ defaults: ["platform_app_defaults"],
// Only compile source java files in this apk.
srcs: [
"src/**/*.java",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9136f5da..2b101bef 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -21,12 +21,18 @@
<uses-permission android:name="android.permission.USE_RESERVED_DISK" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
+ <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<permission
android:name="android.permission.SEND_CALL_LOG_CHANGE"
android:label="Broadcast that a change happened to the call log."
android:protectionLevel="signature|system"/>
+ <permission
+ android:name="android.contacts.permission.MANAGE_SIM_ACCOUNTS"
+ android:label="Change known SIM accounts in ContactsProvider."
+ android:protectionLevel="signature"/>
+
<application android:process="android.process.acore"
android:label="@string/app_label"
android:allowBackup="false"
@@ -62,13 +68,24 @@
android:writePermission="android.permission.WRITE_CALL_LOG">
</provider>
+ <!-- Separate provider for the locations stored in call provider.
+ Uses a different db in order to prevent SQL injection attacks from bypassing
+ location permission requirements -->
+ <provider android:name="CallComposerLocationProvider"
+ android:authorities="call_composer_locations"
+ android:syncable="false" android:multiprocess="false"
+ android:exported="true"
+ android:readPermission="android.permission.READ_CALL_LOG"
+ android:writePermission="android.permission.WRITE_CALL_LOG">
+ </provider>
+
<provider android:name="ShadowCallLogProvider"
android:authorities="call_log_shadow"
android:syncable="false" android:multiprocess="false"
android:exported="true"
android:directBootAware="true"
- android:readPermission="android.permission.MANAGE_USERS"
- android:writePermission="android.permission.MANAGE_USERS">
+ android:readPermission="android.permission.INTERACT_ACROSS_USERS"
+ android:writePermission="android.permission.INTERACT_ACROSS_USERS">
</provider>
<!-- Note: While this provider does not declare a permission explicitly, it enforces that
@@ -80,12 +97,6 @@
android:exported="true">
</provider>
- <provider android:name="ContactMetadataProvider"
- android:authorities="com.android.contacts.metadata"
- android:multiprocess="false"
- android:exported="true">
- </provider>
-
<!-- Handles database upgrades after OTAs, then disables itself -->
<receiver android:name="ContactsUpgradeReceiver"
android:exported="true">
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 8ce3b7d0..478e5ce5 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -19,7 +19,7 @@
<string name="sharedUserLabel" msgid="8024311725474286801">"Android Core Apps"</string>
<string name="app_label" msgid="3389954322874982620">"संपर्क मेमोरी"</string>
<string name="provider_label" msgid="6012150850819899907">"संपर्क"</string>
- <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"संपर्क अपग्रेड के लिए ज़्यादा स्टोरेज की आवश्यकता होती है."</string>
+ <string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"संपर्क अपग्रेड के लिए ज़्यादा मेमोरी की आवश्यकता होती है."</string>
<string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"संपर्कों के लिए मेमोरी अपग्रेड करना"</string>
<string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"अपग्रेड पूरा करने के लिए टैप करें."</string>
<string name="default_directory" msgid="93961630309570294">"संपर्क"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 1073f199..bb60c01f 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -18,11 +18,11 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="sharedUserLabel" msgid="8024311725474286801">"Android ప్రధాన యాప్‌లు"</string>
<string name="app_label" msgid="3389954322874982620">"పరిచయాల నిల్వ"</string>
- <string name="provider_label" msgid="6012150850819899907">"కాంటాక్ట్‌లు"</string>
+ <string name="provider_label" msgid="6012150850819899907">"పరిచయాలు"</string>
<string name="upgrade_out_of_memory_notification_ticker" msgid="7638747231223520477">"పరిచయాల అప్‌గ్రేడ్‌కు మరింత మెమరీ అవసరం."</string>
<string name="upgrade_out_of_memory_notification_title" msgid="8888171924684998531">"పరిచయాల కోసం నిల్వను అప్‌గ్రేడ్ చేస్తోంది"</string>
<string name="upgrade_out_of_memory_notification_text" msgid="2581831842693151968">"అప్‌గ్రేడ్‌ను పూర్తి చేయడానికి నొక్కండి."</string>
- <string name="default_directory" msgid="93961630309570294">"కాంటాక్ట్‌లు"</string>
+ <string name="default_directory" msgid="93961630309570294">"పరిచయాలు"</string>
<string name="local_invisible_directory" msgid="705244318477396120">"ఇతరం"</string>
<string name="voicemail_from_column" msgid="435732568832121444">"దీని నుండి వాయిస్ మెయిల్ "</string>
<string name="debug_dump_title" msgid="4916885724165570279">"పరిచయాల డేటాబేస్‌ను కాపీ చేయి"</string>
diff --git a/src/com/android/providers/contacts/AccountWithDataSet.java b/src/com/android/providers/contacts/AccountWithDataSet.java
index bfae1128..e1f633ea 100644
--- a/src/com/android/providers/contacts/AccountWithDataSet.java
+++ b/src/com/android/providers/contacts/AccountWithDataSet.java
@@ -17,10 +17,14 @@
package com.android.providers.contacts;
import android.accounts.Account;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.SimAccount;
import android.text.TextUtils;
import com.google.common.base.Objects;
+import java.util.List;
+
/**
* Account information that includes the data set, if any.
*/
@@ -105,4 +109,23 @@ public class AccountWithDataSet {
}
return false;
}
+
+ /**
+ * @return {@code true} if there is a {@link SimAccount} with a matching account name and type
+ * in the passed list.
+ * The list should be obtained from {@link ContactsDatabaseHelper#getAllSimAccounts()} so it
+ * will already have valid SIM accounts so only name and type need to be compared.
+ */
+ public boolean inSimAccounts(List<SimAccount> simAccountList) {
+ // Note we don't want to create a new SimAccount object from this instance, as this method
+ // does not need to know about the SIM slot or the EF type. It only needs to know whether
+ // the name and type match since the passed list will only contain valid SIM accounts.
+ for (SimAccount simAccount : simAccountList) {
+ if (Objects.equal(simAccount.getAccountName(), getAccountName())
+ && Objects.equal(simAccount.getAccountType(), getAccountType())) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/android/providers/contacts/CallComposerLocationProvider.java b/src/com/android/providers/contacts/CallComposerLocationProvider.java
new file mode 100644
index 00000000..568a1899
--- /dev/null
+++ b/src/com/android/providers/contacts/CallComposerLocationProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts;
+
+import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Process;
+import android.provider.CallLog;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+
+import com.android.providers.contacts.util.SelectionBuilder;
+
+import java.util.Objects;
+
+public class CallComposerLocationProvider extends ContentProvider {
+ private static final String TAG = CallComposerLocationProvider.class.getSimpleName();
+ private static final String DB_NAME = "call_composer_locations.db";
+ private static final String TABLE_NAME = "locations";
+ private static final int VERSION = 1;
+
+ private static final int LOCATION = 1;
+ private static final int LOCATION_ID = 2;
+
+ private static class OpenHelper extends SQLiteOpenHelper {
+ public OpenHelper(@Nullable Context context, @Nullable String name,
+ @Nullable SQLiteDatabase.CursorFactory factory, int version) {
+ super(context, name, factory, version);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_NAME+ " (" +
+ CallLog.Locations._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ CallLog.Locations.LATITUDE + " REAL, " +
+ CallLog.Locations.LONGITUDE + " REAL" +
+ ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // Nothing to do here, still on version 1.
+ }
+ }
+
+ private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ static {
+ sURIMatcher.addURI(CallLog.Locations.AUTHORITY, "", LOCATION);
+ sURIMatcher.addURI(CallLog.Locations.AUTHORITY, "/#", LOCATION_ID);
+ }
+
+ private OpenHelper mOpenHelper;
+
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new OpenHelper(getContext(), DB_NAME, null, VERSION);
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
+ @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+ enforceAccessRestrictions();
+ final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(TABLE_NAME);
+ qb.setStrict(true);
+ qb.setStrictGrammar(true);
+ final int match = sURIMatcher.match(uri);
+
+ final SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
+ switch (match) {
+ case LOCATION_ID: {
+ selectionBuilder.addClause(getEqualityClause(CallLog.Locations._ID,
+ parseLocationIdFromUri(uri)));
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Provided URI is not supported for query.");
+ }
+
+ final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ return qb.query(db, projection, selectionBuilder.build(), selectionArgs, null,
+ null, sortOrder, null);
+ }
+
+ @Nullable
+ @Override
+ public String getType(@NonNull Uri uri) {
+ final int match = sURIMatcher.match(uri);
+ switch (match) {
+ case LOCATION_ID:
+ return CallLog.Locations.CONTENT_ITEM_TYPE;
+ case LOCATION:
+ return CallLog.Locations.CONTENT_TYPE;
+ default:
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ enforceAccessRestrictions();
+ long id = mOpenHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
+ return ContentUris.withAppendedId(CallLog.Locations.CONTENT_URI, id);
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ enforceAccessRestrictions();
+ final int match = sURIMatcher.match(uri);
+ switch (match) {
+ case LOCATION_ID:
+ long id = parseLocationIdFromUri(uri);
+ return mOpenHelper.getWritableDatabase().delete(TABLE_NAME,
+ CallLog.Locations._ID + " = ?", new String[] {String.valueOf(id)});
+ case LOCATION:
+ Log.w(TAG, "Deleting entire location table!");
+ return mOpenHelper.getWritableDatabase().delete(TABLE_NAME, "1", null);
+ default:
+ throw new IllegalArgumentException("delete() on the locations"
+ + " does not support the uri " + uri.toString());
+ }
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ enforceAccessRestrictions();
+ throw new UnsupportedOperationException(
+ "Call composer location db does not support updates");
+ }
+
+ private long parseLocationIdFromUri(Uri uri) {
+ try {
+ return Long.parseLong(uri.getPathSegments().get(0));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid location id in uri: " + uri, e);
+ }
+ }
+
+ private void enforceAccessRestrictions() {
+ int uid = Binder.getCallingUid();
+ if (uid == Process.SYSTEM_UID || uid == Process.myUid() || uid == Process.PHONE_UID) {
+ return;
+ }
+ String defaultDialerPackageName = getContext().getSystemService(TelecomManager.class)
+ .getDefaultDialerPackage();
+ if (TextUtils.isEmpty(defaultDialerPackageName)) {
+ throw new SecurityException("Access to call composer locations is only allowed for the"
+ + " default dialer, but the default dialer is unset");
+ }
+ String[] callingPackageCandidates = getContext().getPackageManager().getPackagesForUid(uid);
+ for (String packageCandidate : callingPackageCandidates) {
+ if (Objects.equals(packageCandidate, defaultDialerPackageName)) {
+ return;
+ }
+ }
+ throw new SecurityException("Access to call composer locations is only allowed for the "
+ + "default dialer: " + defaultDialerPackageName);
+ }
+}
diff --git a/src/com/android/providers/contacts/CallLogDatabaseHelper.java b/src/com/android/providers/contacts/CallLogDatabaseHelper.java
index 06d3291b..22f1cad4 100644
--- a/src/com/android/providers/contacts/CallLogDatabaseHelper.java
+++ b/src/com/android/providers/contacts/CallLogDatabaseHelper.java
@@ -39,7 +39,7 @@ import com.android.providers.contacts.util.PropertyUtils;
public class CallLogDatabaseHelper {
private static final String TAG = "CallLogDatabaseHelper";
- private static final int DATABASE_VERSION = 9;
+ private static final int DATABASE_VERSION = 10;
private static final boolean DEBUG = false; // DON'T SUBMIT WITH TRUE
@@ -153,6 +153,10 @@ public class CallLogDatabaseHelper {
Calls.CALL_SCREENING_APP_NAME + " TEXT," +
Calls.BLOCK_REASON + " INTEGER NOT NULL DEFAULT 0," +
Calls.MISSED_REASON + " INTEGER NOT NULL DEFAULT 0," +
+ Calls.PRIORITY + " INTEGER NOT NULL DEFAULT 0," +
+ Calls.SUBJECT + " TEXT," +
+ Calls.LOCATION + " TEXT," +
+ Calls.COMPOSER_PHOTO_URI + " TEXT," +
Voicemails._DATA + " TEXT," +
Voicemails.HAS_CONTENT + " INTEGER," +
@@ -225,6 +229,10 @@ public class CallLogDatabaseHelper {
if (oldVersion < 9) {
upgradeToVersion9(db);
}
+
+ if (oldVersion < 10) {
+ upgradeToVersion10(db);
+ }
}
}
@@ -459,6 +467,12 @@ public class CallLogDatabaseHelper {
db.execSQL("ALTER TABLE calls ADD missed_reason INTEGER NOT NULL DEFAULT 0");
}
+ private void upgradeToVersion10(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE calls ADD priority INTEGER NOT NULL DEFAULT 0");
+ db.execSQL("ALTER TABLE calls ADD subject TEXT");
+ db.execSQL("ALTER TABLE calls ADD location TEXT");
+ db.execSQL("ALTER TABLE calls ADD composer_photo_uri TEXT");
+ }
/**
* Perform the migration from the contacts2.db (of the latest version) to the current calllog/
* voicemail status tables.
diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java
index 9fa651cb..c832e9b4 100644
--- a/src/com/android/providers/contacts/CallLogProvider.java
+++ b/src/com/android/providers/contacts/CallLogProvider.java
@@ -20,6 +20,8 @@ import static com.android.providers.contacts.util.DbQueryUtils.checkForSupported
import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
import static com.android.providers.contacts.util.DbQueryUtils.getInequalityClause;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
@@ -36,6 +38,10 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
+import android.os.StatFs;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.CallLog;
@@ -43,6 +49,7 @@ import android.provider.CallLog.Calls;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -55,11 +62,25 @@ import com.android.providers.contacts.util.SelectionBuilder;
import com.android.providers.contacts.util.UserUtils;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.UUID;
import java.util.concurrent.CountDownLatch;
+import java.util.stream.Collectors;
/**
* Call log content provider.
@@ -81,18 +102,57 @@ public class CallLogProvider extends ContentProvider {
private static final String EXCLUDE_HIDDEN_SELECTION = getEqualityClause(
Calls.PHONE_ACCOUNT_HIDDEN, 0);
+ private static final String CALL_COMPOSER_PICTURE_DIRECTORY_NAME = "call_composer_pics";
+ private static final String CALL_COMPOSER_ALL_USERS_DIRECTORY_NAME = "all_users";
+
+ // Constants to be used with ContentProvider#call in order to sync call composer pics between
+ // users. Defined here because they're for internal use only.
+ /**
+ * Method name used to get a list of {@link Uri}s for call composer pictures inserted for all
+ * users after a certain date
+ */
+ private static final String GET_CALL_COMPOSER_IMAGE_URIS =
+ "com.android.providers.contacts.GET_CALL_COMPOSER_IMAGE_URIS";
+
+ /**
+ * Long-valued extra containing the date to filter by expressed as milliseconds after the epoch.
+ */
+ private static final String EXTRA_SINCE_DATE =
+ "com.android.providers.contacts.extras.SINCE_DATE";
+
+ /**
+ * Boolean-valued extra indicating whether to read from the shadow portion of the calllog
+ * (i.e. device-encrypted storage rather than credential-encrypted)
+ */
+ private static final String EXTRA_IS_SHADOW =
+ "com.android.providers.contacts.extras.IS_SHADOW";
+
+ /**
+ * Boolean-valued extra indicating whether to return Uris only for those images that are
+ * supposed to be inserted for all users.
+ */
+ private static final String EXTRA_ALL_USERS_ONLY =
+ "com.android.providers.contacts.extras.ALL_USERS_ONLY";
+
+ private static final String EXTRA_RESULT_URIS =
+ "com.android.provider.contacts.extras.EXTRA_RESULT_URIS";
+
@VisibleForTesting
static final String[] CALL_LOG_SYNC_PROJECTION = new String[] {
- Calls.NUMBER,
- Calls.NUMBER_PRESENTATION,
- Calls.TYPE,
- Calls.FEATURES,
- Calls.DATE,
- Calls.DURATION,
- Calls.DATA_USAGE,
- Calls.PHONE_ACCOUNT_COMPONENT_NAME,
- Calls.PHONE_ACCOUNT_ID,
- Calls.ADD_FOR_ALL_USERS
+ Calls.NUMBER,
+ Calls.NUMBER_PRESENTATION,
+ Calls.TYPE,
+ Calls.FEATURES,
+ Calls.DATE,
+ Calls.DURATION,
+ Calls.DATA_USAGE,
+ Calls.PHONE_ACCOUNT_COMPONENT_NAME,
+ Calls.PHONE_ACCOUNT_ID,
+ Calls.PRIORITY,
+ Calls.SUBJECT,
+ Calls.COMPOSER_PHOTO_URI,
+ // Location is deliberately omitted
+ Calls.ADD_FOR_ALL_USERS
};
static final String[] MINIMAL_PROJECTION = new String[] { Calls._ID };
@@ -103,6 +163,10 @@ public class CallLogProvider extends ContentProvider {
private static final int CALLS_FILTER = 3;
+ private static final int CALL_COMPOSER_NEW_PICTURE = 4;
+
+ private static final int CALL_COMPOSER_PICTURE = 5;
+
private static final String UNHIDE_BY_PHONE_ACCOUNT_QUERY =
"UPDATE " + Tables.CALLS + " SET " + Calls.PHONE_ACCOUNT_HIDDEN + "=0 WHERE " +
Calls.PHONE_ACCOUNT_COMPONENT_NAME + "=? AND " + Calls.PHONE_ACCOUNT_ID + "=?;";
@@ -116,12 +180,20 @@ public class CallLogProvider extends ContentProvider {
sURIMatcher.addURI(CallLog.AUTHORITY, "calls", CALLS);
sURIMatcher.addURI(CallLog.AUTHORITY, "calls/#", CALLS_ID);
sURIMatcher.addURI(CallLog.AUTHORITY, "calls/filter/*", CALLS_FILTER);
+ sURIMatcher.addURI(CallLog.AUTHORITY, CallLog.CALL_COMPOSER_SEGMENT,
+ CALL_COMPOSER_NEW_PICTURE);
+ sURIMatcher.addURI(CallLog.AUTHORITY, CallLog.CALL_COMPOSER_SEGMENT + "/*",
+ CALL_COMPOSER_PICTURE);
- // Shadow provider only supports "/calls".
+ // Shadow provider only supports "/calls" and "/call_composer".
sURIMatcher.addURI(CallLog.SHADOW_AUTHORITY, "calls", CALLS);
+ sURIMatcher.addURI(CallLog.SHADOW_AUTHORITY, CallLog.CALL_COMPOSER_SEGMENT,
+ CALL_COMPOSER_NEW_PICTURE);
+ sURIMatcher.addURI(CallLog.SHADOW_AUTHORITY, CallLog.CALL_COMPOSER_SEGMENT + "/*",
+ CALL_COMPOSER_PICTURE);
}
- private static final ArrayMap<String, String> sCallsProjectionMap;
+ public static final ArrayMap<String, String> sCallsProjectionMap;
static {
// Calls projection map
@@ -163,6 +235,10 @@ public class CallLogProvider extends ContentProvider {
sCallsProjectionMap.put(Calls.CALL_SCREENING_APP_NAME, Calls.CALL_SCREENING_APP_NAME);
sCallsProjectionMap.put(Calls.BLOCK_REASON, Calls.BLOCK_REASON);
sCallsProjectionMap.put(Calls.MISSED_REASON, Calls.MISSED_REASON);
+ sCallsProjectionMap.put(Calls.PRIORITY, Calls.PRIORITY);
+ sCallsProjectionMap.put(Calls.COMPOSER_PHOTO_URI, Calls.COMPOSER_PHOTO_URI);
+ sCallsProjectionMap.put(Calls.SUBJECT, Calls.SUBJECT);
+ sCallsProjectionMap.put(Calls.LOCATION, Calls.LOCATION);
}
private static final String ALLOWED_PACKAGE_FOR_TESTING = "com.android.providers.contacts";
@@ -443,6 +519,11 @@ public class CallLogProvider extends ContentProvider {
return Calls.CONTENT_ITEM_TYPE;
case CALLS_FILTER:
return Calls.CONTENT_TYPE;
+ case CALL_COMPOSER_NEW_PICTURE:
+ return null; // No type for newly created files
+ case CALL_COMPOSER_PICTURE:
+ // We don't know the exact image format, so this is as specific as we can be.
+ return "application/octet-stream";
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
@@ -494,6 +575,30 @@ public class CallLogProvider extends ContentProvider {
" CUID=" + Binder.getCallingUid());
}
waitForAccess(mReadAccessLatch);
+ int match = sURIMatcher.match(uri);
+ switch (match) {
+ case CALL_COMPOSER_PICTURE: {
+ String fileName = uri.getLastPathSegment();
+ try {
+ return allocateNewCallComposerPicture(values,
+ CallLog.SHADOW_AUTHORITY.equals(uri.getAuthority()),
+ fileName);
+ } catch (IOException e) {
+ throw new ParcelableException(e);
+ }
+ }
+ case CALL_COMPOSER_NEW_PICTURE: {
+ try {
+ return allocateNewCallComposerPicture(values,
+ CallLog.SHADOW_AUTHORITY.equals(uri.getAuthority()));
+ } catch (IOException e) {
+ throw new ParcelableException(e);
+ }
+ }
+ default:
+ // Fall through and execute the rest of the method for ordinary call log insertions.
+ }
+
checkForSupportedColumns(sCallsProjectionMap, values);
// Inserting a voicemail record through call_log requires the voicemail
// permission and also requires the additional voicemail param set.
@@ -518,6 +623,170 @@ public class CallLogProvider extends ContentProvider {
return null;
}
+ @Override
+ public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
+ throws FileNotFoundException {
+ int match = sURIMatcher.match(uri);
+ if (match != CALL_COMPOSER_PICTURE) {
+ throw new UnsupportedOperationException("The call log provider only supports opening"
+ + " call composer pictures.");
+ }
+ int modeInt;
+ switch (mode) {
+ case "r":
+ modeInt = ParcelFileDescriptor.MODE_READ_ONLY;
+ break;
+ case "w":
+ modeInt = ParcelFileDescriptor.MODE_WRITE_ONLY;
+ break;
+ default:
+ throw new UnsupportedOperationException("The call log does not support opening"
+ + " a call composer picture with mode " + mode);
+ }
+
+ try {
+ Path callComposerDir = getCallComposerPictureDirectory(getContext(), uri);
+ Path pictureFile = callComposerDir.resolve(uri.getLastPathSegment());
+ if (Files.notExists(pictureFile)) {
+ throw new FileNotFoundException(uri.toString()
+ + " does not correspond to a valid file.");
+ }
+ return ParcelFileDescriptor.open(pictureFile.toFile(), modeInt);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while opening call composer file: " + e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+ Log.i(TAG, "Fetching list of Uris to sync");
+ if (!UserHandle.isSameApp(android.os.Process.myUid(), Binder.getCallingUid())) {
+ throw new SecurityException("call() functionality reserved"
+ + " for internal use by the call log.");
+ }
+ if (!GET_CALL_COMPOSER_IMAGE_URIS.equals(method)) {
+ throw new UnsupportedOperationException("Invalid method passed to call(): " + method);
+ }
+ if (!extras.containsKey(EXTRA_SINCE_DATE)) {
+ throw new IllegalArgumentException("SINCE_DATE required");
+ }
+ if (!extras.containsKey(EXTRA_IS_SHADOW)) {
+ throw new IllegalArgumentException("IS_SHADOW required");
+ }
+ if (!extras.containsKey(EXTRA_ALL_USERS_ONLY)) {
+ throw new IllegalArgumentException("ALL_USERS_ONLY required");
+ }
+ boolean isShadow = extras.getBoolean(EXTRA_IS_SHADOW);
+ boolean allUsers = extras.getBoolean(EXTRA_ALL_USERS_ONLY);
+ long sinceDate = extras.getLong(EXTRA_SINCE_DATE);
+
+ try {
+ Path queryDir = allUsers
+ ? getCallComposerAllUsersPictureDirectory(getContext(), isShadow)
+ : getCallComposerPictureDirectory(getContext(), isShadow);
+ List<Path> newestPics = new ArrayList<>();
+ try (DirectoryStream<Path> dirStream =
+ Files.newDirectoryStream(queryDir, entry -> {
+ if (Files.isDirectory(entry)) {
+ return false;
+ }
+ FileTime createdAt =
+ (FileTime) Files.getAttribute(entry, "creationTime");
+ return createdAt.toMillis() > sinceDate;
+ })) {
+ dirStream.forEach(newestPics::add);
+ }
+ List<Uri> fileUris = newestPics.stream().map((path) -> {
+ String fileName = path.getFileName().toString();
+ // We don't need to worry about if it's for all users -- anything that's for
+ // all users is also stored in the regular location.
+ Uri base = isShadow ? CallLog.SHADOW_CALL_COMPOSER_PICTURE_URI
+ : CallLog.CALL_COMPOSER_PICTURE_URI;
+ return base.buildUpon().appendPath(fileName).build();
+ }).collect(Collectors.toList());
+ Bundle result = new Bundle();
+ result.putParcelableList(EXTRA_RESULT_URIS, fileUris);
+ Log.i(TAG, "Will sync following Uris:" + fileUris);
+ return result;
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while trying to fetch URI list: " + e);
+ return null;
+ }
+ }
+
+ private static @NonNull Path getCallComposerPictureDirectory(Context context, Uri uri)
+ throws IOException {
+ boolean isShadow = CallLog.SHADOW_AUTHORITY.equals(uri.getAuthority());
+ return getCallComposerPictureDirectory(context, isShadow);
+ }
+
+ private static @NonNull Path getCallComposerPictureDirectory(Context context, boolean isShadow)
+ throws IOException {
+ if (isShadow) {
+ context = context.createDeviceProtectedStorageContext();
+ }
+ Path path = context.getFilesDir().toPath().resolve(CALL_COMPOSER_PICTURE_DIRECTORY_NAME);
+ if (!Files.isDirectory(path)) {
+ Files.createDirectory(path);
+ }
+ return path;
+ }
+
+ private static @NonNull Path getCallComposerAllUsersPictureDirectory(
+ Context context, boolean isShadow) throws IOException {
+ Path pathToCallComposerDir = getCallComposerPictureDirectory(context, isShadow);
+ Path path = pathToCallComposerDir.resolve(CALL_COMPOSER_ALL_USERS_DIRECTORY_NAME);
+ if (!Files.isDirectory(path)) {
+ Files.createDirectory(path);
+ }
+ return path;
+ }
+
+ private Uri allocateNewCallComposerPicture(ContentValues values, boolean isShadow)
+ throws IOException {
+ return allocateNewCallComposerPicture(values, isShadow, UUID.randomUUID().toString());
+ }
+
+ private Uri allocateNewCallComposerPicture(ContentValues values,
+ boolean isShadow, String fileName) throws IOException {
+ Uri baseUri = isShadow ?
+ CallLog.CALL_COMPOSER_PICTURE_URI.buildUpon()
+ .authority(CallLog.SHADOW_AUTHORITY).build()
+ : CallLog.CALL_COMPOSER_PICTURE_URI;
+
+ boolean forAllUsers = values.containsKey(Calls.ADD_FOR_ALL_USERS)
+ && (values.getAsInteger(Calls.ADD_FOR_ALL_USERS) == 1);
+ Path pathToCallComposerDir = getCallComposerPictureDirectory(getContext(), isShadow);
+
+ if (new StatFs(pathToCallComposerDir.toString()).getAvailableBytes()
+ < TelephonyManager.getMaximumCallComposerPictureSize()) {
+ return null;
+ }
+ Path pathToFile = pathToCallComposerDir.resolve(fileName);
+ Files.createFile(pathToFile);
+
+ if (forAllUsers) {
+ // Create a symlink in a subdirectory for copying later.
+ Path allUsersDir = getCallComposerAllUsersPictureDirectory(getContext(), isShadow);
+ Files.createSymbolicLink(allUsersDir.resolve(fileName), pathToFile);
+ }
+ return baseUri.buildUpon().appendPath(fileName).build();
+ }
+
+ private int deleteCallComposerPicture(Uri uri) {
+ try {
+ Path pathToCallComposerDir = getCallComposerPictureDirectory(getContext(), uri);
+ String fileName = uri.getLastPathSegment();
+ boolean successfulDelete =
+ Files.deleteIfExists(pathToCallComposerDir.resolve(fileName));
+ return successfulDelete ? 1 : 0;
+ } catch (IOException e) {
+ Log.e(TAG, "IOException encountered deleting the call composer pics dir " + e);
+ return 0;
+ }
+ }
+
private int updateInternal(Uri uri, ContentValues values,
String selection, String[] selectionArgs) {
if (VERBOSE_LOGGING) {
@@ -537,19 +806,8 @@ public class CallLogProvider extends ContentProvider {
SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, false /*isQuery*/);
-
- final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(Tables.CALLS);
- qb.setProjectionMap(sCallsProjectionMap);
- qb.setStrict(true);
- // If the caller doesn't have READ_VOICEMAIL, make sure they can't
- // do any SQL shenanigans to get access to the voicemails. If the caller does have the
- // READ_VOICEMAIL permission, then they have sufficient permissions to access any data in
- // the database, so the strict check is unnecessary.
- if (!mVoicemailPermissions.callerHasReadAccess(getCallingPackage())) {
- qb.setStrictGrammar(true);
- }
-
+ boolean hasReadVoicemailPermission = mVoicemailPermissions.callerHasReadAccess(
+ getCallingPackage());
final SQLiteDatabase db = mDbHelper.getWritableDatabase();
final int matchedUriId = sURIMatcher.match(uri);
switch (matchedUriId) {
@@ -564,11 +822,8 @@ public class CallLogProvider extends ContentProvider {
throw new UnsupportedOperationException("Cannot update URL: " + uri);
}
- int rowsUpdated = qb.update(db, values, selectionBuilder.build(), selectionArgs);
- if (rowsUpdated > 0) {
- DbModifierWithNotification.notifyCallLogChange(getContext());
- }
- return rowsUpdated;
+ return createDatabaseModifier(db, hasReadVoicemailPermission).update(uri, Tables.CALLS,
+ values, selectionBuilder.build(), selectionArgs);
}
private int deleteInternal(Uri uri, String selection, String[] selectionArgs) {
@@ -583,29 +838,18 @@ public class CallLogProvider extends ContentProvider {
SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
checkVoicemailPermissionAndAddRestriction(uri, selectionBuilder, false /*isQuery*/);
- final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(Tables.CALLS);
- qb.setProjectionMap(sCallsProjectionMap);
- qb.setStrict(true);
- // If the caller doesn't have READ_VOICEMAIL, make sure they can't
- // do any SQL shenanigans to get access to the voicemails. If the caller does have the
- // READ_VOICEMAIL permission, then they have sufficient permissions to access any data in
- // the database, so the strict check is unnecessary.
- if (!mVoicemailPermissions.callerHasReadAccess(getCallingPackage())) {
- qb.setStrictGrammar(true);
- }
-
+ boolean hasReadVoicemailPermission =
+ mVoicemailPermissions.callerHasReadAccess(getCallingPackage());
final SQLiteDatabase db = mDbHelper.getWritableDatabase();
final int matchedUriId = sURIMatcher.match(uri);
switch (matchedUriId) {
case CALLS:
- // TODO: Special case - We may want to forward the delete request on user 0 to the
- // shadow provider too.
- int deletedCount = qb.delete(db, selectionBuilder.build(), selectionArgs);
- if (deletedCount > 0) {
- DbModifierWithNotification.notifyCallLogChange(getContext());
- }
- return deletedCount;
+ return createDatabaseModifier(db, hasReadVoicemailPermission).delete(Tables.CALLS,
+ selectionBuilder.build(), selectionArgs);
+ case CALL_COMPOSER_PICTURE:
+ // TODO(hallliu): implement deletion of file when the corresponding calllog entry
+ // gets deleted as well.
+ return deleteCallComposerPicture(uri);
default:
throw new UnsupportedOperationException("Cannot delete that URL: " + uri);
}
@@ -619,8 +863,9 @@ public class CallLogProvider extends ContentProvider {
* Returns a {@link DatabaseModifier} that takes care of sending necessary notifications
* after the operation is performed.
*/
- private DatabaseModifier createDatabaseModifier(SQLiteDatabase db) {
- return new DbModifierWithNotification(Tables.CALLS, db, getContext());
+ private DatabaseModifier createDatabaseModifier(SQLiteDatabase db, boolean hasReadVoicemail) {
+ return new DbModifierWithNotification(Tables.CALLS, db, null, hasReadVoicemail,
+ getContext());
}
/**
@@ -762,8 +1007,68 @@ public class CallLogProvider extends ContentProvider {
// delete all entries in shadow.
cr.delete(uri, Calls.DATE + "<= ?", new String[] {String.valueOf(newestTimeStamp)});
}
+
+ try {
+ syncCallComposerPics(sourceUserId, sourceIsShadow, forAllUsersOnly, lastSyncTime);
+ } catch (Exception e) {
+ // Catch any exceptions to make sure we don't bring down the entire process if something
+ // goes wrong
+ StringWriter w = new StringWriter();
+ PrintWriter pw = new PrintWriter(w);
+ e.printStackTrace(pw);
+ Log.e(TAG, "Caught exception syncing call composer pics: " + e
+ + "\n" + pw.toString());
+ }
}
+ private void syncCallComposerPics(int sourceUserId, boolean sourceIsShadow,
+ boolean forAllUsersOnly, long lastSyncTime) {
+ Log.i(TAG, "Syncing call composer pics -- source user=" + sourceUserId + ","
+ + " isShadow=" + sourceIsShadow + ", forAllUser=" + forAllUsersOnly);
+ ContentResolver contentResolver = getContext().getContentResolver();
+ Bundle args = new Bundle();
+ args.putLong(EXTRA_SINCE_DATE, lastSyncTime);
+ args.putBoolean(EXTRA_ALL_USERS_ONLY, forAllUsersOnly);
+ args.putBoolean(EXTRA_IS_SHADOW, sourceIsShadow);
+ Uri queryUri = ContentProvider.maybeAddUserId(
+ sourceIsShadow
+ ? CallLog.SHADOW_CALL_COMPOSER_PICTURE_URI
+ : CallLog.CALL_COMPOSER_PICTURE_URI,
+ sourceUserId);
+ Bundle result = contentResolver.call(queryUri, GET_CALL_COMPOSER_IMAGE_URIS, null, args);
+ if (result == null || !result.containsKey(EXTRA_RESULT_URIS)) {
+ Log.e(TAG, "Failed to sync call composer pics -- invalid return from call()");
+ return;
+ }
+ List<Uri> urisToCopy = result.getParcelableArrayList(EXTRA_RESULT_URIS);
+ Log.i(TAG, "Syncing call composer pics -- got " + urisToCopy);
+ for (Uri uri : urisToCopy) {
+ try {
+ Uri uriWithUser = ContentProvider.maybeAddUserId(uri, sourceUserId);
+ Path newFilePath = getCallComposerPictureDirectory(getContext(), false)
+ .resolve(uri.getLastPathSegment());
+ try (ParcelFileDescriptor remoteFile = contentResolver.openFile(uriWithUser,
+ "r", null);
+ OutputStream localOut =
+ Files.newOutputStream(newFilePath, StandardOpenOption.CREATE_NEW)) {
+ FileInputStream input = new FileInputStream(remoteFile.getFileDescriptor());
+ byte[] buffer = new byte[1 << 14]; // 16kb
+ while (true) {
+ int numRead = input.read(buffer);
+ if (numRead < 0) {
+ break;
+ }
+ localOut.write(buffer, 0, numRead);
+ }
+ }
+ contentResolver.delete(uriWithUser, null);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while syncing call composer pics: " + e);
+ // Keep going and get as many as we can.
+ }
+ }
+
+ }
/**
* Un-hides any hidden call log entries that are associated with the specified handle.
*
diff --git a/src/com/android/providers/contacts/ContactDirectoryManager.java b/src/com/android/providers/contacts/ContactDirectoryManager.java
index 8a1c88bb..09e3ff37 100644
--- a/src/com/android/providers/contacts/ContactDirectoryManager.java
+++ b/src/com/android/providers/contacts/ContactDirectoryManager.java
@@ -245,8 +245,7 @@ public class ContactDirectoryManager {
Log.i(TAG, "Discovered " + count + " contact directories in " + (end - start) + "ms");
// Announce the change to listeners of the contacts authority
- mContactsProvider.notifyChange(/* syncToNetwork =*/false,
- /* syncToMetadataNetwork =*/false);
+ mContactsProvider.notifyChange(/* syncToNetwork =*/false);
// We schedule a rescan if update(DIRECTORIES) is called while we're scanning all packages.
if (mDirectoriesForceUpdated) {
diff --git a/src/com/android/providers/contacts/ContactMetadataProvider.java b/src/com/android/providers/contacts/ContactMetadataProvider.java
deleted file mode 100644
index 3cf7df2c..00000000
--- a/src/com/android/providers/contacts/ContactMetadataProvider.java
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.providers.contacts;
-
-import android.content.ContentProvider;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.IContentProvider;
-import android.content.OperationApplicationException;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.Binder;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.MetadataSync;
-import android.provider.ContactsContract.MetadataSyncState;
-import android.text.TextUtils;
-import android.util.Log;
-import com.android.common.content.ProjectionMap;
-import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncStateColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.ContactsDatabaseHelper.Views;
-import com.android.providers.contacts.MetadataEntryParser.MetadataEntry;
-import com.android.providers.contacts.util.SelectionBuilder;
-import com.android.providers.contacts.util.UserUtils;
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Map;
-
-import static com.android.providers.contacts.ContactsProvider2.getLimit;
-import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause;
-
-/**
- * Simple content provider to handle directing contact metadata specific calls.
- */
-public class ContactMetadataProvider extends ContentProvider {
- private static final String TAG = "ContactMetadata";
- private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
- private static final int METADATA_SYNC = 1;
- private static final int METADATA_SYNC_ID = 2;
- private static final int SYNC_STATE = 3;
-
- private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- static {
- sURIMatcher.addURI(MetadataSync.METADATA_AUTHORITY, "metadata_sync", METADATA_SYNC);
- sURIMatcher.addURI(MetadataSync.METADATA_AUTHORITY, "metadata_sync/#", METADATA_SYNC_ID);
- sURIMatcher.addURI(MetadataSync.METADATA_AUTHORITY, "metadata_sync_state", SYNC_STATE);
- }
-
- private static final Map<String, String> sMetadataProjectionMap = ProjectionMap.builder()
- .add(MetadataSync._ID)
- .add(MetadataSync.RAW_CONTACT_BACKUP_ID)
- .add(MetadataSync.ACCOUNT_TYPE)
- .add(MetadataSync.ACCOUNT_NAME)
- .add(MetadataSync.DATA_SET)
- .add(MetadataSync.DATA)
- .add(MetadataSync.DELETED)
- .build();
-
- private static final Map<String, String> sSyncStateProjectionMap =ProjectionMap.builder()
- .add(MetadataSyncState._ID)
- .add(MetadataSyncState.ACCOUNT_TYPE)
- .add(MetadataSyncState.ACCOUNT_NAME)
- .add(MetadataSyncState.DATA_SET)
- .add(MetadataSyncState.STATE)
- .build();
-
- private ContactsDatabaseHelper mDbHelper;
- private ContactsProvider2 mContactsProvider;
-
- private String mAllowedPackage;
-
- @Override
- public boolean onCreate() {
- final Context context = getContext();
- mDbHelper = getDatabaseHelper(context);
- final IContentProvider iContentProvider = context.getContentResolver().acquireProvider(
- ContactsContract.AUTHORITY);
- final ContentProvider provider = ContentProvider.coerceToLocalContentProvider(
- iContentProvider);
- mContactsProvider = (ContactsProvider2) provider;
-
- mAllowedPackage = getContext().getResources().getString(R.string.metadata_sync_pacakge);
- return true;
- }
-
- protected ContactsDatabaseHelper getDatabaseHelper(final Context context) {
- return ContactsDatabaseHelper.getInstance(context);
- }
-
- @VisibleForTesting
- protected void setDatabaseHelper(final ContactsDatabaseHelper helper) {
- mDbHelper = helper;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
-
- ensureCaller();
-
- if (VERBOSE_LOGGING) {
- Log.v(TAG, "query: uri=" + uri + " projection=" + Arrays.toString(projection) +
- " selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
- " order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
- " User=" + UserUtils.getCurrentUserHandle(getContext()));
- }
- final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- String limit = getLimit(uri);
-
- final SelectionBuilder selectionBuilder = new SelectionBuilder(selection);
-
- final int match = sURIMatcher.match(uri);
- switch (match) {
- case METADATA_SYNC:
- setTablesAndProjectionMapForMetadata(qb);
- break;
-
- case METADATA_SYNC_ID: {
- setTablesAndProjectionMapForMetadata(qb);
- selectionBuilder.addClause(getEqualityClause(MetadataSync._ID,
- ContentUris.parseId(uri)));
- break;
- }
-
- case SYNC_STATE:
- setTablesAndProjectionMapForSyncState(qb);
- break;
- default:
- throw new IllegalArgumentException("Unknown URL " + uri);
- }
-
- final SQLiteDatabase db = mDbHelper.getReadableDatabase();
- return qb.query(db, projection, selectionBuilder.build(), selectionArgs, null,
- null, sortOrder, limit);
- }
-
- @Override
- public String getType(Uri uri) {
- int match = sURIMatcher.match(uri);
- switch (match) {
- case METADATA_SYNC:
- return MetadataSync.CONTENT_TYPE;
- case METADATA_SYNC_ID:
- return MetadataSync.CONTENT_ITEM_TYPE;
- case SYNC_STATE:
- return MetadataSyncState.CONTENT_TYPE;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri);
- }
- }
-
- @Override
- /**
- * Insert or update if the raw is already existing.
- */
- public Uri insert(Uri uri, ContentValues values) {
-
- ensureCaller();
-
- final SQLiteDatabase db = mDbHelper.getWritableDatabase();
- db.beginTransactionNonExclusive();
- try {
- final int matchedUriId = sURIMatcher.match(uri);
- switch (matchedUriId) {
- case METADATA_SYNC:
- // Insert the new entry, and also parse the data column to update related
- // tables.
- final long metadataSyncId = updateOrInsertDataToMetadataSync(db, uri, values);
- db.setTransactionSuccessful();
- return ContentUris.withAppendedId(uri, metadataSyncId);
- case SYNC_STATE:
- replaceAccountInfoByAccountId(uri, values);
- final Long syncStateId = db.replace(
- Tables.METADATA_SYNC_STATE, MetadataSyncColumns.ACCOUNT_ID, values);
- db.setTransactionSuccessful();
- return ContentUris.withAppendedId(uri, syncStateId);
- default:
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Calling contact metadata insert on an unknown/invalid URI", uri));
- }
- } finally {
- db.endTransaction();
- }
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
-
- ensureCaller();
-
- final SQLiteDatabase db = mDbHelper.getWritableDatabase();
- db.beginTransactionNonExclusive();
- try {
- final int matchedUriId = sURIMatcher.match(uri);
- int numDeletes = 0;
- switch (matchedUriId) {
- case METADATA_SYNC:
- Cursor c = db.query(Views.METADATA_SYNC, new String[]{MetadataSync._ID},
- selection, selectionArgs, null, null, null);
- try {
- while (c.moveToNext()) {
- final long contactMetadataId = c.getLong(0);
- numDeletes += db.delete(Tables.METADATA_SYNC,
- MetadataSync._ID + "=" + contactMetadataId, null);
- }
- } finally {
- c.close();
- }
- db.setTransactionSuccessful();
- return numDeletes;
- case SYNC_STATE:
- c = db.query(Views.METADATA_SYNC_STATE, new String[]{MetadataSyncState._ID},
- selection, selectionArgs, null, null, null);
- try {
- while (c.moveToNext()) {
- final long stateId = c.getLong(0);
- numDeletes += db.delete(Tables.METADATA_SYNC_STATE,
- MetadataSyncState._ID + "=" + stateId, null);
- }
- } finally {
- c.close();
- }
- db.setTransactionSuccessful();
- return numDeletes;
- default:
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Calling contact metadata delete on an unknown/invalid URI", uri));
- }
- } finally {
- db.endTransaction();
- }
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-
- ensureCaller();
-
- final SQLiteDatabase db = mDbHelper.getWritableDatabase();
- db.beginTransactionNonExclusive();
- try {
- final int matchedUriId = sURIMatcher.match(uri);
- switch (matchedUriId) {
- // Do not support update metadata sync by update() method. Please use insert().
- case SYNC_STATE:
- // Only support update by account.
- final Long accountId = replaceAccountInfoByAccountId(uri, values);
- if (accountId == null) {
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Invalid identifier is found for accountId", uri));
- }
- values.put(MetadataSyncColumns.ACCOUNT_ID, accountId);
- // Insert a new row if it doesn't exist.
- db.replace(Tables.METADATA_SYNC_STATE, null, values);
- db.setTransactionSuccessful();
- return 1;
- default:
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Calling contact metadata update on an unknown/invalid URI", uri));
- }
- } finally {
- db.endTransaction();
- }
- }
-
- @Override
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
- throws OperationApplicationException {
-
- ensureCaller();
-
- if (VERBOSE_LOGGING) {
- Log.v(TAG, "applyBatch: " + operations.size() + " ops");
- }
- final SQLiteDatabase db = mDbHelper.getWritableDatabase();
- db.beginTransactionNonExclusive();
- try {
- ContentProviderResult[] results = super.applyBatch(operations);
- db.setTransactionSuccessful();
- return results;
- } finally {
- db.endTransaction();
- }
- }
-
- @Override
- public int bulkInsert(Uri uri, ContentValues[] values) {
-
- ensureCaller();
-
- if (VERBOSE_LOGGING) {
- Log.v(TAG, "bulkInsert: " + values.length + " inserts");
- }
- final SQLiteDatabase db = mDbHelper.getWritableDatabase();
- db.beginTransactionNonExclusive();
- try {
- final int numValues = super.bulkInsert(uri, values);
- db.setTransactionSuccessful();
- return numValues;
- } finally {
- db.endTransaction();
- }
- }
-
- private void setTablesAndProjectionMapForMetadata(SQLiteQueryBuilder qb){
- qb.setTables(Views.METADATA_SYNC);
- qb.setProjectionMap(sMetadataProjectionMap);
- qb.setStrict(true);
- }
-
- private void setTablesAndProjectionMapForSyncState(SQLiteQueryBuilder qb){
- qb.setTables(Views.METADATA_SYNC_STATE);
- qb.setProjectionMap(sSyncStateProjectionMap);
- qb.setStrict(true);
- }
-
- /**
- * Insert or update a non-deleted entry to MetadataSync table, and also parse the data column
- * to update related tables for the raw contact.
- * Returns new upserted metadataSyncId.
- */
- private long updateOrInsertDataToMetadataSync(SQLiteDatabase db, Uri uri, ContentValues values) {
- final int matchUri = sURIMatcher.match(uri);
- if (matchUri != METADATA_SYNC) {
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Calling contact metadata insert or update on an unknown/invalid URI", uri));
- }
-
- // Don't insert or update a deleted metadata.
- Integer deleted = values.getAsInteger(MetadataSync.DELETED);
- if (deleted != null && deleted != 0) {
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Cannot insert or update deleted metadata:" + values.toString(), uri));
- }
-
- // Check if data column is empty or null.
- final String data = values.getAsString(MetadataSync.DATA);
- if (TextUtils.isEmpty(data)) {
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Data column cannot be empty.", uri));
- }
-
- // Update or insert for backupId and account info.
- final Long accountId = replaceAccountInfoByAccountId(uri, values);
- final String rawContactBackupId = values.getAsString(
- MetadataSync.RAW_CONTACT_BACKUP_ID);
- // TODO (tingtingw): Consider a corner case: if there's raw with the same accountId and
- // backupId, but deleted=1, (Deleted should be synced up to server and hard-deleted, but
- // may be delayed.) In this case, should we not override it with delete=0? or should this
- // be prevented by sync adapter side?.
- deleted = 0; // Only insert or update non-deleted metadata
- if (accountId == null) {
- // Do nothing, just return.
- return 0;
- }
- if (rawContactBackupId == null) {
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Invalid identifier is found: accountId=" + accountId + "; " +
- "rawContactBackupId=" + rawContactBackupId, uri));
- }
-
- // Update if it exists, otherwise insert.
- final long metadataSyncId = mDbHelper.upsertMetadataSync(
- rawContactBackupId, accountId, data, deleted);
- if (metadataSyncId <= 0) {
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Metadata upsertion failed. Values= " + values.toString(), uri));
- }
-
- // Parse the data column and update other tables.
- // Data field will never be empty or null, since contacts prefs and usage stats
- // have default values.
- final MetadataEntry metadataEntry = MetadataEntryParser.parseDataToMetaDataEntry(data);
- mContactsProvider.updateFromMetaDataEntry(db, metadataEntry);
-
- return metadataSyncId;
- }
-
- /**
- * Replace account_type, account_name and data_set with account_id. If a valid account_id
- * cannot be found for this combination, return null.
- */
- private Long replaceAccountInfoByAccountId(Uri uri, ContentValues values) {
- String accountName = values.getAsString(MetadataSync.ACCOUNT_NAME);
- String accountType = values.getAsString(MetadataSync.ACCOUNT_TYPE);
- String dataSet = values.getAsString(MetadataSync.DATA_SET);
- final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType);
- if (partialUri) {
- // Throw when either account is incomplete.
- throw new IllegalArgumentException(mDbHelper.exceptionMessage(
- "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE", uri));
- }
-
- final AccountWithDataSet account = AccountWithDataSet.get(
- accountName, accountType, dataSet);
-
- final Long id = mDbHelper.getAccountIdOrNull(account);
- if (id == null) {
- return null;
- }
-
- values.put(MetadataSyncColumns.ACCOUNT_ID, id);
- // Only remove the account information once the account ID is extracted (since these
- // fields are actually used by resolveAccountWithDataSet to extract the relevant ID).
- values.remove(MetadataSync.ACCOUNT_NAME);
- values.remove(MetadataSync.ACCOUNT_TYPE);
- values.remove(MetadataSync.DATA_SET);
-
- return id;
- }
-
- @VisibleForTesting
- void ensureCaller() {
- final String caller = getCallingPackage();
- if (mAllowedPackage.equals(caller)) {
- return; // Okay.
- }
- throw new SecurityException("Caller " + caller + " can't access ContactMetadataProvider");
- }
-}
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 2e5cdacf..7f4188d2 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -16,12 +16,6 @@
package com.android.providers.contacts;
-import com.android.internal.R.bool;
-import com.android.providers.contacts.sqlite.DatabaseAnalyzer;
-import com.android.providers.contacts.sqlite.SqlChecker;
-import com.android.providers.contacts.sqlite.SqlChecker.InvalidSqlException;
-import com.android.providers.contacts.util.PropertyUtils;
-
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -73,14 +67,13 @@ import android.provider.ContactsContract.DisplayNameSources;
import android.provider.ContactsContract.DisplayPhoto;
import android.provider.ContactsContract.FullNameStyle;
import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.MetadataSync;
-import android.provider.ContactsContract.MetadataSyncState;
import android.provider.ContactsContract.PhoneticNameStyle;
import android.provider.ContactsContract.PhotoFiles;
import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.ProviderStatus;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
+import android.provider.ContactsContract.SimAccount;
import android.provider.ContactsContract.StatusUpdates;
import android.provider.ContactsContract.StreamItemPhotos;
import android.provider.ContactsContract.StreamItems;
@@ -98,17 +91,23 @@ import android.util.Log;
import android.util.Slog;
import com.android.common.content.SyncStateContentProviderHelper;
+import com.android.internal.R.bool;
import com.android.internal.annotations.VisibleForTesting;
import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
import com.android.providers.contacts.database.ContactsTableUtil;
import com.android.providers.contacts.database.DeletedContactsTableUtil;
import com.android.providers.contacts.database.MoreDatabaseUtils;
+import com.android.providers.contacts.sqlite.DatabaseAnalyzer;
+import com.android.providers.contacts.sqlite.SqlChecker;
+import com.android.providers.contacts.sqlite.SqlChecker.InvalidSqlException;
import com.android.providers.contacts.util.NeededForTesting;
+import com.android.providers.contacts.util.PropertyUtils;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -143,9 +142,10 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
* 1200-1299 O
* 1300-1399 P
* 1400-1499 Q
+ * 1500-1599 S
* </pre>
*/
- static final int DATABASE_VERSION = 1400;
+ static final int DATABASE_VERSION = 1501;
private static final int MINIMUM_SUPPORTED_VERSION = 700;
@VisibleForTesting
@@ -189,8 +189,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
public static final String DIRECTORIES = "directories";
public static final String DEFAULT_DIRECTORY = "default_directory";
public static final String SEARCH_INDEX = "search_index";
- public static final String METADATA_SYNC = "metadata_sync";
- public static final String METADATA_SYNC_STATE = "metadata_sync_state";
public static final String PRE_AUTHORIZED_URIS = "pre_authorized_uris";
// This list of tables contains auto-incremented sequences.
@@ -313,15 +311,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
+ " JOIN " + Tables.ACCOUNTS + " ON ("
+ AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
+ ")";
-
- public static final String RAW_CONTACTS_JOIN_METADATA_SYNC = Tables.RAW_CONTACTS
- + " JOIN " + Tables.METADATA_SYNC + " ON ("
- + RawContactsColumns.CONCRETE_BACKUP_ID + "="
- + MetadataSyncColumns.CONCRETE_BACKUP_ID
- + " AND "
- + RawContactsColumns.CONCRETE_ACCOUNT_ID + "="
- + MetadataSyncColumns.CONCRETE_ACCOUNT_ID
- + ")";
}
public interface Joins {
@@ -712,6 +701,8 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
String ACCOUNT_NAME = RawContacts.ACCOUNT_NAME;
String ACCOUNT_TYPE = RawContacts.ACCOUNT_TYPE;
String DATA_SET = RawContacts.DATA_SET;
+ String SIM_SLOT_INDEX = "sim_slot_index";
+ String SIM_EF_TYPE = "sim_ef_type";
String CONCRETE_ACCOUNT_NAME = Tables.ACCOUNTS + "." + ACCOUNT_NAME;
String CONCRETE_ACCOUNT_TYPE = Tables.ACCOUNTS + "." + ACCOUNT_TYPE;
@@ -770,22 +761,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
public static final int USAGE_TYPE_INT_SHORT_TEXT = 2;
}
- public interface MetadataSyncColumns {
- static final String CONCRETE_ID = Tables.METADATA_SYNC + "._id";
- static final String ACCOUNT_ID = "account_id";
- static final String CONCRETE_BACKUP_ID = Tables.METADATA_SYNC + "." +
- MetadataSync.RAW_CONTACT_BACKUP_ID;
- static final String CONCRETE_ACCOUNT_ID = Tables.METADATA_SYNC + "." + ACCOUNT_ID;
- static final String CONCRETE_DELETED = Tables.METADATA_SYNC + "." +
- MetadataSync.DELETED;
- }
-
- public interface MetadataSyncStateColumns {
- static final String CONCRETE_ID = Tables.METADATA_SYNC_STATE + "._id";
- static final String ACCOUNT_ID = "account_id";
- static final String CONCRETE_ACCOUNT_ID = Tables.METADATA_SYNC_STATE + "." + ACCOUNT_ID;
- }
-
private interface EmailQuery {
public static final String TABLE = Tables.DATA;
@@ -1247,8 +1222,10 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
AccountsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
AccountsColumns.ACCOUNT_NAME + " TEXT, " +
AccountsColumns.ACCOUNT_TYPE + " TEXT, " +
- AccountsColumns.DATA_SET + " TEXT" +
- ");");
+ AccountsColumns.DATA_SET + " TEXT, " +
+ AccountsColumns.SIM_SLOT_INDEX + " INTEGER, " +
+ AccountsColumns.SIM_EF_TYPE + " INTEGER" +
+ ");");
// Note, there are two sets of the usage stat columns: LR_* and RAW_*.
// RAW_* contain the real values, which clients can't access. The column names start
@@ -1622,34 +1599,11 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
DataUsageStatColumns.USAGE_TYPE_INT +
");");
- db.execSQL("CREATE TABLE IF NOT EXISTS "
- + Tables.METADATA_SYNC + " (" +
- MetadataSync._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
- MetadataSync.RAW_CONTACT_BACKUP_ID + " TEXT NOT NULL," +
- MetadataSyncColumns.ACCOUNT_ID + " INTEGER NOT NULL," +
- MetadataSync.DATA + " TEXT," +
- MetadataSync.DELETED + " INTEGER NOT NULL DEFAULT 0);");
-
- db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS metadata_sync_index ON " +
- Tables.METADATA_SYNC + " (" +
- MetadataSync.RAW_CONTACT_BACKUP_ID + ", " +
- MetadataSyncColumns.ACCOUNT_ID +");");
-
db.execSQL("CREATE TABLE " + Tables.PRE_AUTHORIZED_URIS + " ("+
PreAuthorizedUris._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
PreAuthorizedUris.URI + " STRING NOT NULL, " +
PreAuthorizedUris.EXPIRATION + " INTEGER NOT NULL DEFAULT 0);");
- db.execSQL("CREATE TABLE IF NOT EXISTS "
- + Tables.METADATA_SYNC_STATE + " (" +
- MetadataSyncState._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
- MetadataSyncStateColumns.ACCOUNT_ID + " INTEGER NOT NULL," +
- MetadataSyncState.STATE + " BLOB);");
-
- db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS metadata_sync_state_index ON " +
- Tables.METADATA_SYNC_STATE + " (" +
- MetadataSyncColumns.ACCOUNT_ID +");");
-
// When adding new tables, be sure to also add size-estimates in updateSqliteStats
createContactsViews(db);
createGroupsView(db);
@@ -2242,34 +2196,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
+ RawContactsColumns.CONCRETE_CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + ")";
db.execSQL("CREATE VIEW " + Views.STREAM_ITEMS + " AS " + streamItemSelect);
-
- String metadataSyncSelect = "SELECT " +
- MetadataSyncColumns.CONCRETE_ID + ", " +
- MetadataSync.RAW_CONTACT_BACKUP_ID + ", " +
- AccountsColumns.ACCOUNT_NAME + ", " +
- AccountsColumns.ACCOUNT_TYPE + ", " +
- AccountsColumns.DATA_SET + ", " +
- MetadataSync.DATA + ", " +
- MetadataSync.DELETED +
- " FROM " + Tables.METADATA_SYNC
- + " JOIN " + Tables.ACCOUNTS + " ON ("
- + MetadataSyncColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
- + ")";
-
- db.execSQL("CREATE VIEW " + Views.METADATA_SYNC + " AS " + metadataSyncSelect);
-
- String metadataSyncStateSelect = "SELECT " +
- MetadataSyncStateColumns.CONCRETE_ID + ", " +
- AccountsColumns.ACCOUNT_NAME + ", " +
- AccountsColumns.ACCOUNT_TYPE + ", " +
- AccountsColumns.DATA_SET + ", " +
- MetadataSyncState.STATE +
- " FROM " + Tables.METADATA_SYNC_STATE
- + " JOIN " + Tables.ACCOUNTS + " ON ("
- + MetadataSyncStateColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
- + ")";
-
- db.execSQL("CREATE VIEW " + Views.METADATA_SYNC_STATE + " AS " + metadataSyncStateSelect);
}
private static String buildDisplayPhotoUriAlias(String contactIdColumn, String alias) {
@@ -2655,6 +2581,19 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
oldVersion = 1400;
}
+ if (isUpgradeRequired(oldVersion, newVersion, 1500)) {
+ db.execSQL("DROP TABLE IF EXISTS metadata_sync;");
+ db.execSQL("DROP TABLE IF EXISTS metadata_sync_state;");
+ upgradeViewsAndTriggers = true;
+ oldVersion = 1500;
+ }
+
+ if (isUpgradeRequired(oldVersion, newVersion, 1501)) {
+ upgradeToVersion1501(db);
+ upgradeViewsAndTriggers = true;
+ oldVersion = 1501;
+ }
+
// We extracted "calls" and "voicemail_status" at this point, but we can't remove them here
// yet, until CallLogDatabaseHelper moves the data.
@@ -3317,30 +3256,19 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
}
/**
- * Add new metadata_sync table to cache the meta data on raw contacts level from server before
- * they are merged into other CP2 tables. The data column is the blob column containing all
- * the backed up metadata for this raw_contact. This table should only be used by metadata
- * sync adapter.
+ * Used to add new metadata_sync table to cache the meta data on raw contacts level from server
+ * before they are merged into other CP2 tables. The table is not used any more.
*/
public void upgradeToVersion1104(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS metadata_sync;");
- db.execSQL("CREATE TABLE metadata_sync (" +
- "_id INTEGER PRIMARY KEY AUTOINCREMENT, raw_contact_backup_id TEXT NOT NULL, " +
- "account_id INTEGER NOT NULL, data TEXT, deleted INTEGER NOT NULL DEFAULT 0);");
- db.execSQL("CREATE UNIQUE INDEX metadata_sync_index ON metadata_sync (" +
- "raw_contact_backup_id, account_id);");
}
/**
- * Add new metadata_sync_state table to store the metadata sync state for a set of accounts.
+ * Used to add new metadata_sync_state table to store the metadata sync state for a set of
+ * accounts. The table is not used any more.
*/
public void upgradeToVersion1105(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS metadata_sync_state;");
- db.execSQL("CREATE TABLE metadata_sync_state (" +
- "_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
- "account_id INTEGER NOT NULL, state BLOB);");
- db.execSQL("CREATE UNIQUE INDEX metadata_sync_state_index ON metadata_sync_state (" +
- "account_id);");
}
public void upgradeToVersion1106(SQLiteDatabase db) {
@@ -3439,6 +3367,14 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
}
}
+ private void upgradeToVersion1501(SQLiteDatabase db) {
+ try {
+ db.execSQL("ALTER TABLE accounts ADD sim_slot_index INTEGER;");
+ db.execSQL("ALTER TABLE accounts ADD sim_ef_type INTEGER;");
+ } catch (SQLException ignore) {
+ }
+ }
+
/**
* This method is only used in upgradeToVersion1101 method, and should not be used in other
* places now. Because data15 is not used to generate hash_id for photo, and the new generating
@@ -3693,9 +3629,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
updateIndexStats(db, Tables.DATA_USAGE_STAT,
"data_usage_stat_index", "20 2 1");
- updateIndexStats(db, Tables.METADATA_SYNC,
- "metadata_sync_index", "10000 1 1");
-
// Tiny tables
updateIndexStats(db, Tables.AGGREGATION_EXCEPTIONS,
null, "10");
@@ -3716,9 +3649,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
updateIndexStats(db, "properties",
"sqlite_autoindex_properties_1", "4 1");
- updateIndexStats(db, Tables.METADATA_SYNC_STATE,
- "metadata_sync_state_index", "2 1 1");
-
// Search index
updateIndexStats(db, "search_index_docsize",
null, "9000");
@@ -3980,6 +3910,37 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
}
/**
+ * Gets all SIM accounts in the accounts table.
+ */
+ public List<SimAccount> getAllSimAccounts() {
+ final List<SimAccount> result = new ArrayList<>();
+ final Cursor c = getReadableDatabase().rawQuery(
+ "SELECT DISTINCT " + AccountsColumns._ID + ","
+ + AccountsColumns.ACCOUNT_NAME + ","
+ + AccountsColumns.ACCOUNT_TYPE + ","
+ + AccountsColumns.SIM_SLOT_INDEX + ","
+ + AccountsColumns.SIM_EF_TYPE + " FROM " + Tables.ACCOUNTS, null);
+ try {
+ while (c.moveToNext()) {
+ if (c.isNull(3) || c.isNull(4)) {
+ // Invalid slot index or ef type
+ continue;
+ }
+ final int simSlot = c.getInt(3);
+ final int efType = c.getInt(4);
+ if (simSlot < 0 || !SimAccount.getValidEfTypes().contains(efType)) {
+ // Invalid slot index or ef type
+ continue;
+ }
+ result.add(new SimAccount(c.getString(1), c.getString(2), simSlot, efType));
+ }
+ } finally {
+ c.close();
+ }
+ return result;
+ }
+
+ /**
* @return ID of the specified account, or null if the account doesn't exist.
*/
public Long getAccountIdOrNull(AccountWithDataSet accountWithDataSet) {
@@ -4042,6 +4003,69 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
}
/**
+ * This method will create a record in the accounts table.
+ *
+ * This must be used in a transaction, so there's no need for synchronization.
+ *
+ * @param simSlot Sim slot index of the account. Must be 0 or greater
+ * @param efType EF type of the account. Must be a value contained in {@link
+ * SimAccount#getValidEfTypes()}
+ * @throws IllegalArgumentException if the account name/type pair is already within the table.
+ * SIM accounts should have distinct names and types.
+ * And if simSlot is negative, or efType is not in {@link
+ * SimAccount#getValidEfTypes()}
+ */
+ public long createSimAccountIdInTransaction(AccountWithDataSet accountWithDataSet,
+ int simSlot, int efType) {
+ if (simSlot < 0) {
+ throw new IllegalArgumentException("Sim slot is negative");
+ }
+ if (!SimAccount.getValidEfTypes().contains(efType)) {
+ throw new IllegalArgumentException("Invalid EF type");
+ }
+ if (accountWithDataSet == null || TextUtils.isEmpty(accountWithDataSet.getAccountName())
+ || TextUtils.isEmpty(accountWithDataSet.getAccountType())) {
+ throw new IllegalArgumentException("Account is null or the name/type is empty");
+ }
+
+ Long id = getAccountIdOrNull(accountWithDataSet);
+ if (id != null) {
+ throw new IllegalArgumentException("Account already exists in the table");
+ }
+ final SQLiteStatement insert = getWritableDatabase().compileStatement(
+ "INSERT INTO " + Tables.ACCOUNTS +
+ " (" + AccountsColumns.ACCOUNT_NAME + ", " +
+ AccountsColumns.ACCOUNT_TYPE + ", " +
+ AccountsColumns.DATA_SET + ", " +
+ AccountsColumns.SIM_SLOT_INDEX + ", " +
+ AccountsColumns.SIM_EF_TYPE + ") VALUES (?, ?, ?, ?, ?)");
+ try {
+ DatabaseUtils.bindObjectToProgram(insert, 1, accountWithDataSet.getAccountName());
+ DatabaseUtils.bindObjectToProgram(insert, 2, accountWithDataSet.getAccountType());
+ DatabaseUtils.bindObjectToProgram(insert, 3, accountWithDataSet.getDataSet());
+ DatabaseUtils.bindObjectToProgram(insert, 4, simSlot);
+ DatabaseUtils.bindObjectToProgram(insert, 5, efType);
+ id = insert.executeInsert();
+ } finally {
+ insert.close();
+ }
+
+ return id;
+ }
+
+ /**
+ * Deletes all rows in the accounts table with the given sim slot index
+ *
+ * @param simSlot Sim slot to remove accounts
+ * @return how many rows were deleted
+ */
+ public int removeSimAccounts(int simSlot) {
+ final SQLiteDatabase db = getWritableDatabase();
+ return db.delete(Tables.ACCOUNTS, AccountsColumns.SIM_SLOT_INDEX + "=?",
+ new String[]{String.valueOf(simSlot)});
+ }
+
+ /**
* Update {@link Contacts#IN_VISIBLE_GROUP} for all contacts.
*/
public void updateAllVisible() {
@@ -4989,22 +5013,6 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
new String[] {String.valueOf(contactId)});
}
- public long upsertMetadataSync(String backupId, Long accountId, String data, Integer deleted) {
- final SQLiteStatement metadataSyncInsert = getWritableDatabase().compileStatement(
- "INSERT OR REPLACE INTO " + Tables.METADATA_SYNC + "("
- + MetadataSync.RAW_CONTACT_BACKUP_ID + ", "
- + MetadataSyncColumns.ACCOUNT_ID + ", "
- + MetadataSync.DATA + ","
- + MetadataSync.DELETED + ")" +
- " VALUES (?,?,?,?)");
- metadataSyncInsert.bindString(1, backupId);
- metadataSyncInsert.bindLong(2, accountId);
- data = (data == null) ? "" : data;
- metadataSyncInsert.bindString(3, data);
- metadataSyncInsert.bindLong(4, deleted);
- return metadataSyncInsert.executeInsert();
- }
-
public static void notifyProviderStatusChange(Context context) {
context.getContentResolver().notifyChange(ProviderStatus.CONTENT_URI,
/* observer= */ null, /* syncToNetwork= */ false);
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index aad51c72..0c6e8192 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -16,9 +16,9 @@
package com.android.providers.contacts;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -34,6 +34,7 @@ import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.IContentService;
+import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.SyncAdapterType;
@@ -59,8 +60,11 @@ import android.net.Uri;
import android.net.Uri.Builder;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.RemoteException;
@@ -94,7 +98,6 @@ import android.provider.ContactsContract.DeletedContacts;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.DisplayPhoto;
import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.MetadataSync;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.PhotoFiles;
import android.provider.ContactsContract.PinnedPositions;
@@ -104,6 +107,8 @@ import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
import android.provider.ContactsContract.SearchSnippets;
import android.provider.ContactsContract.Settings;
+import android.provider.ContactsContract.SimAccount;
+import android.provider.ContactsContract.SimContacts;
import android.provider.ContactsContract.StatusUpdates;
import android.provider.ContactsContract.StreamItemPhotos;
import android.provider.ContactsContract.StreamItems;
@@ -121,7 +126,6 @@ import android.util.Log;
import com.android.common.content.ProjectionMap;
import com.android.common.content.SyncStateContentProviderHelper;
import com.android.common.io.MoreCloseables;
-import com.android.i18n.phonenumbers.Phonenumber;
import com.android.internal.util.ArrayUtils;
import com.android.providers.contacts.ContactLookupKey.LookupKeySegment;
import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
@@ -135,8 +139,6 @@ import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumn
import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Joins;
-import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncStateColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
@@ -153,11 +155,6 @@ import com.android.providers.contacts.ContactsDatabaseHelper.StreamItemsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import com.android.providers.contacts.ContactsDatabaseHelper.ViewGroupsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Views;
-import com.android.providers.contacts.MetadataEntryParser.AggregationData;
-import com.android.providers.contacts.MetadataEntryParser.FieldData;
-import com.android.providers.contacts.MetadataEntryParser.MetadataEntry;
-import com.android.providers.contacts.MetadataEntryParser.RawContactInfo;
-import com.android.providers.contacts.MetadataEntryParser.UsageStats;
import com.android.providers.contacts.SearchIndexManager.FtsQueryBuilder;
import com.android.providers.contacts.aggregation.AbstractContactAggregator;
import com.android.providers.contacts.aggregation.AbstractContactAggregator.AggregationSuggestionParameter;
@@ -173,6 +170,8 @@ import com.android.providers.contacts.enterprise.EnterprisePolicyGuard;
import com.android.providers.contacts.util.Clock;
import com.android.providers.contacts.util.ContactsPermissions;
import com.android.providers.contacts.util.DbQueryUtils;
+import com.android.providers.contacts.util.LogFields;
+import com.android.providers.contacts.util.LogUtils;
import com.android.providers.contacts.util.NeededForTesting;
import com.android.providers.contacts.util.UserUtils;
import com.android.vcard.VCardComposer;
@@ -219,6 +218,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
private static final String READ_PERMISSION = "android.permission.READ_CONTACTS";
private static final String WRITE_PERMISSION = "android.permission.WRITE_CONTACTS";
+ private static final String MANAGE_SIM_ACCOUNTS_PERMISSION =
+ "android.contacts.permission.MANAGE_SIM_ACCOUNTS";
/* package */ static final String PHONEBOOK_COLLATOR_NAME = "PHONEBOOK";
@@ -259,6 +260,9 @@ public class ContactsProvider2 extends AbstractContactsProvider
/** Limit for the maximum number of social stream items to store under a raw contact. */
private static final int MAX_STREAM_ITEMS_PER_RAW_CONTACT = 5;
+ /** Rate limit (in milliseconds) for notify change. Do it as most once every 5 seconds. */
+ private static final int NOTIFY_CHANGE_RATE_LIMIT = 5 * 1000;
+
/** Rate limit (in milliseconds) for photo cleanup. Do it at most once per day. */
private static final int PHOTO_CLEANUP_RATE_LIMIT = 24 * 60 * 60 * 1000;
@@ -1453,13 +1457,14 @@ public class ContactsProvider2 extends AbstractContactsProvider
private boolean mVisibleTouched = false;
private boolean mSyncToNetwork;
- private boolean mSyncToMetadataNetWork;
private LocaleSet mCurrentLocales;
private int mContactsAccountCount;
private ContactsTaskScheduler mTaskScheduler;
+ private long mLastNotifyChange = 0;
+
private long mLastPhotoCleanup = 0;
private FastScrollingIndexCache mFastScrollingIndexCache;
@@ -1469,9 +1474,6 @@ public class ContactsProvider2 extends AbstractContactsProvider
private int mFastScrollingIndexCacheMissCount;
private long mTotalTimeFastScrollingIndexGenerate;
- // MetadataSync flag.
- private boolean mMetadataSyncEnabled;
-
// Enterprise members
private EnterprisePolicyGuard mEnterprisePolicyGuard;
@@ -1481,6 +1483,13 @@ public class ContactsProvider2 extends AbstractContactsProvider
Log.v(TAG, "onCreate user="
+ android.os.Process.myUserHandle().getIdentifier());
}
+ if (Build.IS_DEBUGGABLE) {
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
+ .detectLeakedSqlLiteObjects() // for SqlLiteCursor
+ .detectLeakedClosableObjects() // for any Cursor
+ .penaltyLog()
+ .build());
+ }
if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
Log.d(Constants.PERFORMANCE_TAG, "ContactsProvider2.onCreate start");
@@ -1515,9 +1524,6 @@ public class ContactsProvider2 extends AbstractContactsProvider
mFastScrollingIndexCache = FastScrollingIndexCache.getInstance(getContext());
- mMetadataSyncEnabled = android.provider.Settings.Global.getInt(
- getContext().getContentResolver(), Global.CONTACT_METADATA_SYNC_ENABLED, 0) == 1;
-
mContactsHelper = getDatabaseHelper();
mDbHelper.set(mContactsHelper);
@@ -1971,8 +1977,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
ContentValues updateValues = new ContentValues();
updateValues.putNull(Photo.PHOTO_FILE_ID);
updateData(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
- updateValues, null, null, /* callerIsSyncAdapter =*/false,
- /* callerIsMetadataSyncAdapter =*/false);
+ updateValues, null, null, /* callerIsSyncAdapter =*/false);
}
if (photoFileIdToStreamItemPhotoId.containsKey(missingPhotoId)) {
// For missing photos that were in stream item photos, just delete the
@@ -2171,49 +2176,106 @@ public class ContactsProvider2 extends AbstractContactsProvider
@Override
public Uri insert(Uri uri, ContentValues values) {
- waitForAccess(mWriteAccessLatch);
+ LogFields.Builder logBuilder = LogFields.Builder.aLogFields()
+ .setApiType(LogUtils.ApiType.INSERT)
+ .setUriType(sUriMatcher.match(uri))
+ .setCallerIsSyncAdapter(readBooleanQueryParameter(
+ uri, ContactsContract.CALLER_IS_SYNCADAPTER, false))
+ .setStartNanos(SystemClock.elapsedRealtimeNanos());
+ Uri resultUri = null;
- mContactsHelper.validateContentValues(getCallingPackage(), values);
+ try {
+ waitForAccess(mWriteAccessLatch);
- if (mapsToProfileDbWithInsertedValues(uri, values)) {
- switchToProfileMode();
- return mProfileProvider.insert(uri, values);
+ mContactsHelper.validateContentValues(getCallingPackage(), values);
+
+ if (mapsToProfileDbWithInsertedValues(uri, values)) {
+ switchToProfileMode();
+ resultUri = mProfileProvider.insert(uri, values);
+ return resultUri;
+ }
+ switchToContactMode();
+ resultUri = super.insert(uri, values);
+ return resultUri;
+ } catch (Exception e) {
+ logBuilder.setException(e);
+ throw e;
+ } finally {
+ LogUtils.log(
+ logBuilder.setResultUri(resultUri).setResultCount(resultUri == null ? 0 : 1)
+ .build());
}
- switchToContactMode();
- return super.insert(uri, values);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- waitForAccess(mWriteAccessLatch);
+ LogFields.Builder logBuilder = LogFields.Builder.aLogFields()
+ .setApiType(LogUtils.ApiType.UPDATE)
+ .setUriType(sUriMatcher.match(uri))
+ .setCallerIsSyncAdapter(readBooleanQueryParameter(
+ uri, ContactsContract.CALLER_IS_SYNCADAPTER, false))
+ .setStartNanos(SystemClock.elapsedRealtimeNanos());
+ int updates = 0;
- mContactsHelper.validateContentValues(getCallingPackage(), values);
- mContactsHelper.validateSql(getCallingPackage(), selection);
+ try {
+ waitForAccess(mWriteAccessLatch);
- if (mapsToProfileDb(uri)) {
- switchToProfileMode();
- return mProfileProvider.update(uri, values, selection, selectionArgs);
+ mContactsHelper.validateContentValues(getCallingPackage(), values);
+ mContactsHelper.validateSql(getCallingPackage(), selection);
+
+ if (mapsToProfileDb(uri)) {
+ switchToProfileMode();
+ updates = mProfileProvider.update(uri, values, selection, selectionArgs);
+ return updates;
+ }
+ switchToContactMode();
+ updates = super.update(uri, values, selection, selectionArgs);
+ return updates;
+ } catch (Exception e) {
+ logBuilder.setException(e);
+ throw e;
+ } finally {
+ LogUtils.log(logBuilder.setResultCount(updates).build());
}
- switchToContactMode();
- return super.update(uri, values, selection, selectionArgs);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
- waitForAccess(mWriteAccessLatch);
+ LogFields.Builder logBuilder = LogFields.Builder.aLogFields()
+ .setApiType(LogUtils.ApiType.DELETE)
+ .setUriType(sUriMatcher.match(uri))
+ .setCallerIsSyncAdapter(readBooleanQueryParameter(
+ uri, ContactsContract.CALLER_IS_SYNCADAPTER, false))
+ .setStartNanos(SystemClock.elapsedRealtimeNanos());
+ int deletes = 0;
- mContactsHelper.validateSql(getCallingPackage(), selection);
+ try {
+ waitForAccess(mWriteAccessLatch);
- if (mapsToProfileDb(uri)) {
- switchToProfileMode();
- return mProfileProvider.delete(uri, selection, selectionArgs);
+ mContactsHelper.validateSql(getCallingPackage(), selection);
+
+ if (mapsToProfileDb(uri)) {
+ switchToProfileMode();
+ deletes = mProfileProvider.delete(uri, selection, selectionArgs);
+ return deletes;
+ }
+ switchToContactMode();
+ deletes = super.delete(uri, selection, selectionArgs);
+ return deletes;
+ } catch (Exception e) {
+ logBuilder.setException(e);
+ throw e;
+ } finally {
+ LogUtils.log(logBuilder.setResultCount(deletes).build());
}
- switchToContactMode();
- return super.delete(uri, selection, selectionArgs);
}
@Override
public Bundle call(String method, String arg, Bundle extras) {
+ LogFields.Builder logBuilder =
+ LogFields.Builder.aLogFields()
+ .setApiType(LogUtils.ApiType.CALL)
+ .setStartNanos(SystemClock.elapsedRealtimeNanos());
waitForAccess(mReadAccessLatch);
switchToContactMode();
if (Authorization.AUTHORIZATION_METHOD.equals(method)) {
@@ -2236,6 +2298,95 @@ public class ContactsProvider2 extends AbstractContactsProvider
}
undemoteContact(mDbHelper.get().getWritableDatabase(), id);
return null;
+ } else if (SimContacts.ADD_SIM_ACCOUNT_METHOD.equals(method)) {
+ ContactsPermissions.enforceCallingOrSelfPermission(getContext(),
+ MANAGE_SIM_ACCOUNTS_PERMISSION);
+
+ final String accountName = extras.getString(SimContacts.KEY_ACCOUNT_NAME);
+ final String accountType = extras.getString(SimContacts.KEY_ACCOUNT_TYPE);
+ final int simSlot = extras.getInt(SimContacts.KEY_SIM_SLOT_INDEX, -1);
+ final int efType = extras.getInt(SimContacts.KEY_SIM_EF_TYPE, -1);
+ if (simSlot < 0) {
+ throw new IllegalArgumentException("Sim slot is negative");
+ }
+ if (!SimAccount.getValidEfTypes().contains(efType)) {
+ throw new IllegalArgumentException("Invalid EF type");
+ }
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ throw new IllegalArgumentException("Account name or type is empty");
+ }
+
+ long resultId = -1;
+ final Bundle response = new Bundle();
+ final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+ db.beginTransaction();
+ try {
+ resultId = mDbHelper.get().createSimAccountIdInTransaction(
+ AccountWithDataSet.get(accountName, accountType, null), simSlot, efType);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ logBuilder.setException(e);
+ throw e;
+ } finally {
+ LogUtils.log(
+ logBuilder
+ .setMethodCall(LogUtils.MethodCall.ADD_SIM_ACCOUNTS)
+ .setResultCount(resultId > -1 ? 1 : 0)
+ .build());
+ db.endTransaction();
+ }
+
+ getContext().sendBroadcast(new Intent(SimContacts.ACTION_SIM_ACCOUNTS_CHANGED));
+ return response;
+ } else if (SimContacts.REMOVE_SIM_ACCOUNT_METHOD.equals(method)) {
+ ContactsPermissions.enforceCallingOrSelfPermission(
+ getContext(), MANAGE_SIM_ACCOUNTS_PERMISSION);
+
+ final int simSlot = extras.getInt(SimContacts.KEY_SIM_SLOT_INDEX, -1);
+ if (simSlot < 0) {
+ throw new IllegalArgumentException("Sim slot is negative");
+ }
+
+ int removedCount = 0;
+ final Bundle response = new Bundle();
+ final SQLiteDatabase db = mDbHelper.get().getWritableDatabase();
+ db.beginTransaction();
+ try {
+ removedCount = mDbHelper.get().removeSimAccounts(simSlot);
+ scheduleBackgroundTask(BACKGROUND_TASK_UPDATE_ACCOUNTS);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ logBuilder.setException(e);
+ throw e;
+ } finally {
+ LogUtils.log(
+ logBuilder
+ .setMethodCall(LogUtils.MethodCall.REMOVE_SIM_ACCOUNTS)
+ .setResultCount(removedCount)
+ .build());
+ db.endTransaction();
+ }
+ getContext().sendBroadcast(new Intent(SimContacts.ACTION_SIM_ACCOUNTS_CHANGED));
+ return response;
+ } else if (SimContacts.QUERY_SIM_ACCOUNTS_METHOD.equals(method)) {
+ ContactsPermissions.enforceCallingOrSelfPermission(getContext(), READ_PERMISSION);
+ final Bundle response = new Bundle();
+ int accountsCount = 0;
+ try {
+ final List<SimAccount> simAccounts = mDbHelper.get().getAllSimAccounts();
+ response.putParcelableList(SimContacts.KEY_SIM_ACCOUNTS, simAccounts);
+ accountsCount = simAccounts.size();
+ return response;
+ } catch (Exception e) {
+ logBuilder.setException(e);
+ throw e;
+ } finally {
+ LogUtils.log(
+ logBuilder
+ .setMethodCall(LogUtils.MethodCall.GET_SIM_ACCOUNTS)
+ .setResultCount(accountsCount)
+ .build());
+ }
}
return null;
}
@@ -2455,11 +2606,6 @@ public class ContactsProvider2 extends AbstractContactsProvider
mTransactionContext.get().clearExceptSearchIndexUpdates();
}
- @VisibleForTesting
- void setMetadataSyncForTest(boolean enabled) {
- mMetadataSyncEnabled = enabled;
- }
-
/**
* Appends comma separated IDs.
* @param ids Should not be empty
@@ -2474,14 +2620,44 @@ public class ContactsProvider2 extends AbstractContactsProvider
@Override
protected void notifyChange() {
- notifyChange(mSyncToNetwork, mSyncToMetadataNetWork);
+ notifyChange(mSyncToNetwork);
mSyncToNetwork = false;
- mSyncToMetadataNetWork = false;
}
- protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetwork) {
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Runnable mChangeNotifier = () -> {
+ Log.v(TAG, "Scheduled notifyChange started.");
+ mLastNotifyChange = System.currentTimeMillis();
getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
+ false);
+ };
+
+ protected void notifyChange(boolean syncToNetwork) {
+ if (syncToNetwork) {
+ // Changes to sync to network won't be rate limited.
+ getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
syncToNetwork);
+ } else {
+ // Rate limit the changes which are not to sync to network.
+ long currentTimeMillis = System.currentTimeMillis();
+
+ mHandler.removeCallbacks(mChangeNotifier);
+ if (currentTimeMillis > mLastNotifyChange + NOTIFY_CHANGE_RATE_LIMIT) {
+ // Notify change immediately, since it has been a while since last notify.
+ mLastNotifyChange = currentTimeMillis;
+ getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
+ false);
+ } else {
+ // Schedule a delayed notification, to ensure the very last notifyChange will be
+ // executed.
+ // Delay is set to two-fold of rate limit, and the subsequent notifyChange called
+ // (if ever) between the (NOTIFY_CHANGE_RATE_LIMIT, 2 * NOTIFY_CHANGE_RATE_LIMIT)
+ // time window, will cancel this delayed notification.
+ // The delayed notification is only expected to run if notifyChange is not invoked
+ // between the above time window.
+ mHandler.postDelayed(mChangeNotifier, NOTIFY_CHANGE_RATE_LIMIT * 2);
+ }
+ }
}
protected void setProviderStatus(int status) {
@@ -3039,8 +3215,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
Uri dataUri = inProfileMode()
? Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)
: Data.CONTENT_URI;
- Cursor c = query(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
- selection, selectionArgs, null);
+ Cursor c = queryInternal(dataUri, DataRowHandler.DataDeleteQuery.COLUMNS,
+ selection, selectionArgs, null, null);
try {
while(c.moveToNext()) {
long rawContactId = c.getLong(DataRowHandler.DataDeleteQuery.RAW_CONTACT_ID);
@@ -3067,8 +3243,8 @@ public class ContactsProvider2 extends AbstractContactsProvider
// Note that the query will return data according to the access restrictions,
// so we don't need to worry about deleting data we don't have permission to read.
mSelectionArgs1[0] = String.valueOf(dataId);
- Cursor c = query(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS, Data._ID + "=?",
- mSelectionArgs1, null);
+ Cursor c = queryInternal(Data.CONTENT_URI, DataRowHandler.DataDeleteQuery.COLUMNS,
+ Data._ID + "=?", mSelectionArgs1, null, null);
try {
if (!c.moveToFirst()) {
@@ -3895,8 +4071,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
values.put(RawContactsColumns.AGGREGATION_NEEDED, 1);
values.putNull(RawContacts.CONTACT_ID);
values.put(RawContacts.DIRTY, 1);
- return updateRawContact(db, rawContactId, values, callerIsSyncAdapter,
- /* callerIsMetadataSyncAdapter =*/false);
+ return updateRawContact(db, rawContactId, values, callerIsSyncAdapter);
}
static int deleteDataUsage(SQLiteDatabase db) {
@@ -3998,8 +4173,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
String selectionWithId = (Data.RAW_CONTACT_ID + "=" + rawContactId + " ")
+ (selection == null ? "" : " AND " + selection);
- count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter,
- /* callerIsMetadataSyncAdapter =*/false);
+ count = updateData(uri, values, selectionWithId, selectionArgs, callerIsSyncAdapter);
break;
}
@@ -4007,8 +4181,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
case PROFILE_DATA: {
invalidateFastScrollingIndexCache();
count = updateData(uri, values, appendAccountToSelection(uri, selection),
- selectionArgs, callerIsSyncAdapter,
- /* callerIsMetadataSyncAdapter =*/false);
+ selectionArgs, callerIsSyncAdapter);
if (count > 0) {
mSyncToNetwork |= !callerIsSyncAdapter;
}
@@ -4021,8 +4194,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
case CALLABLES_ID:
case POSTALS_ID: {
invalidateFastScrollingIndexCache();
- count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter,
- /* callerIsMetadataSyncAdapter =*/false);
+ count = updateData(uri, values, selection, selectionArgs, callerIsSyncAdapter);
if (count > 0) {
mSyncToNetwork |= !callerIsSyncAdapter;
}
@@ -4075,8 +4247,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
}
case AGGREGATION_EXCEPTIONS: {
- count = updateAggregationException(db, values,
- /* callerIsMetadataSyncAdapter =*/false);
+ count = updateAggregationException(db, values);
invalidateFastScrollingIndexCache();
break;
}
@@ -4402,8 +4573,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
try {
while (cursor.moveToNext()) {
long rawContactId = cursor.getLong(0);
- updateRawContact(db, rawContactId, values, callerIsSyncAdapter,
- /* callerIsMetadataSyncAdapter =*/false);
+ updateRawContact(db, rawContactId, values, callerIsSyncAdapter);
count++;
}
} finally {
@@ -4436,7 +4606,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
}
private int updateRawContact(SQLiteDatabase db, long rawContactId, ContentValues values,
- boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ boolean callerIsSyncAdapter) {
final String selection = RawContactsColumns.CONCRETE_ID + " = ?";
mSelectionArgs1[0] = Long.toString(rawContactId);
@@ -4567,8 +4737,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
}
private int updateData(Uri uri, ContentValues inputValues, String selection,
- String[] selectionArgs, boolean callerIsSyncAdapter,
- boolean callerIsMetadataSyncAdapter) {
+ String[] selectionArgs, boolean callerIsSyncAdapter) {
final ContentValues values = new ContentValues(inputValues);
values.remove(Data._ID);
@@ -4594,7 +4763,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
selection, selectionArgs, null, -1 /* directory ID */, null);
try {
while(c.moveToNext()) {
- count += updateData(values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter);
+ count += updateData(values, c, callerIsSyncAdapter);
}
} finally {
c.close();
@@ -4610,8 +4779,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
}
}
- private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter,
- boolean callerIsMetadataSyncAdapter) {
+ private int updateData(ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
if (values.size() == 0) {
return 0;
}
@@ -4626,7 +4794,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
DataRowHandler rowHandler = getDataRowHandler(mimeType);
boolean updated =
rowHandler.update(db, mTransactionContext.get(), values, c,
- callerIsSyncAdapter, callerIsMetadataSyncAdapter);
+ callerIsSyncAdapter);
if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
scheduleBackgroundTask(BACKGROUND_TASK_CLEANUP_PHOTOS);
}
@@ -4744,8 +4912,7 @@ public class ContactsProvider2 extends AbstractContactsProvider
return rslt;
}
- private int updateAggregationException(SQLiteDatabase db, ContentValues values,
- boolean callerIsMetadataSyncAdapter) {
+ private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
Integer exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
Long rcId1 = values.getAsLong(AggregationExceptions.RAW_CONTACT_ID1);
Long rcId2 = values.getAsLong(AggregationExceptions.RAW_CONTACT_ID2);
@@ -4868,27 +5035,6 @@ public class ContactsProvider2 extends AbstractContactsProvider
return result;
}
- private long searchRawContactIdForRawContactInfo(SQLiteDatabase db,
- RawContactInfo rawContactInfo) {
- if (rawContactInfo == null) {
- return 0;
- }
- final String backupId = rawContactInfo.mBackupId;
- final String accountType = rawContactInfo.mAccountType;
- final String accountName = rawContactInfo.mAccountName;
- final String dataSet = rawContactInfo.mDataSet;
- ContentValues values = new ContentValues();
- values.put(AccountsColumns.ACCOUNT_TYPE, accountType);
- values.put(AccountsColumns.ACCOUNT_NAME, accountName);
- if (dataSet != null) {
- values.put(AccountsColumns.DATA_SET, dataSet);
- }
-
- final long accountId = replaceAccountInfoByAccountId(RawContacts.CONTENT_URI, values);
- final long rawContactId = queryRawContactId(db, backupId, accountId);
- return rawContactId;
- }
-
interface AggregationExceptionQuery {
String TABLE = Tables.AGGREGATION_EXCEPTIONS;
String[] COLUMNS = new String[] {
@@ -4925,80 +5071,6 @@ public class ContactsProvider2 extends AbstractContactsProvider
return aggregationRawContactIds;
}
- /**
- * Update RawContact, Data, DataUsageStats, AggregationException tables from MetadataEntry.
- */
- @NeededForTesting
- void updateFromMetaDataEntry(SQLiteDatabase db, MetadataEntry metadataEntry) {
- final RawContactInfo rawContactInfo = metadataEntry.mRawContactInfo;
- final long rawContactId = searchRawContactIdForRawContactInfo(db, rawContactInfo);
- if (rawContactId == 0) {
- return;
- }
-
- ContentValues rawContactValues = new ContentValues();
- rawContactValues.put(RawContacts.SEND_TO_VOICEMAIL, metadataEntry.mSendToVoicemail);
- rawContactValues.put(RawContacts.STARRED, metadataEntry.mStarred);
- rawContactValues.put(RawContacts.PINNED, metadataEntry.mPinned);
- updateRawContact(db, rawContactId, rawContactValues, /* callerIsSyncAdapter =*/true,
- /* callerIsMetadataSyncAdapter =*/true);
-
- // Update Data and DataUsageStats table.
- for (int i = 0; i < metadataEntry.mFieldDatas.size(); i++) {
- final FieldData fieldData = metadataEntry.mFieldDatas.get(i);
- final String dataHashId = fieldData.mDataHashId;
- final ArrayList<Long> dataIds = queryDataId(db, rawContactId, dataHashId);
-
- for (long dataId : dataIds) {
- // Update is_primary and is_super_primary.
- ContentValues dataValues = new ContentValues();
- dataValues.put(Data.IS_PRIMARY, fieldData.mIsPrimary ? 1 : 0);
- dataValues.put(Data.IS_SUPER_PRIMARY, fieldData.mIsSuperPrimary ? 1 : 0);
- updateData(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
- dataValues, null, null, /* callerIsSyncAdapter =*/true,
- /* callerIsMetadataSyncAdapter =*/true);
-
- }
- }
-
- // Update AggregationException table.
- final Set<Long> aggregationRawContactIdsInServer = new ArraySet<>();
- for (int i = 0; i < metadataEntry.mAggregationDatas.size(); i++) {
- final AggregationData aggregationData = metadataEntry.mAggregationDatas.get(i);
- final int typeInt = getAggregationType(aggregationData.mType, null);
- final RawContactInfo aggregationContact1 = aggregationData.mRawContactInfo1;
- final RawContactInfo aggregationContact2 = aggregationData.mRawContactInfo2;
- final long rawContactId1 = searchRawContactIdForRawContactInfo(db, aggregationContact1);
- final long rawContactId2 = searchRawContactIdForRawContactInfo(db, aggregationContact2);
- if (rawContactId1 == 0 || rawContactId2 == 0) {
- continue;
- }
- ContentValues values = new ContentValues();
- values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
- values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
- values.put(AggregationExceptions.TYPE, typeInt);
- updateAggregationException(db, values, /* callerIsMetadataSyncAdapter =*/true);
- if (rawContactId1 != rawContactId) {
- aggregationRawContactIdsInServer.add(rawContactId1);
- }
- if (rawContactId2 != rawContactId) {
- aggregationRawContactIdsInServer.add(rawContactId2);
- }
- }
-
- // Delete AggregationExceptions from CP2 if it doesn't exist in server side.
- Set<Long> aggregationRawContactIdsInLocal = queryAggregationRawContactIds(db, rawContactId);
- Set<Long> rawContactIdsToBeDeleted = com.google.common.collect.Sets.difference(
- aggregationRawContactIdsInLocal, aggregationRawContactIdsInServer);
- for (Long deleteRawContactId : rawContactIdsToBeDeleted) {
- ContentValues values = new ContentValues();
- values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
- values.put(AggregationExceptions.RAW_CONTACT_ID2, deleteRawContactId);
- values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_AUTOMATIC);
- updateAggregationException(db, values, /* callerIsMetadataSyncAdapter =*/true);
- }
- }
-
/** return serialized version of {@code accounts} */
@VisibleForTesting
static String accountsToString(Set<Account> accounts) {
@@ -5093,12 +5165,14 @@ public class ContactsProvider2 extends AbstractContactsProvider
// All accounts that are used in raw_contacts and/or groups.
final Set<AccountWithDataSet> knownAccountsWithDataSets
= dbHelper.getAllAccountsWithDataSets();
-
+ // All known SIM accounts
+ final List<SimAccount> simAccounts = getDatabaseHelper().getAllSimAccounts();
// Find the accounts that have been removed.
final List<AccountWithDataSet> accountsWithDataSetsToDelete = Lists.newArrayList();
for (AccountWithDataSet knownAccountWithDataSet : knownAccountsWithDataSets) {
if (knownAccountWithDataSet.isLocalAccount()
- || knownAccountWithDataSet.inSystemAccounts(systemAccounts)) {
+ || knownAccountWithDataSet.inSystemAccounts(systemAccounts)
+ || knownAccountWithDataSet.inSimAccounts(simAccounts)) {
continue;
}
accountsWithDataSetsToDelete.add(knownAccountWithDataSet);
@@ -5108,7 +5182,6 @@ public class ContactsProvider2 extends AbstractContactsProvider
for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
final Long accountIdOrNull = dbHelper.getAccountIdOrNull(accountWithDataSet);
- // getAccountIdOrNull() really shouldn't return null here, but just in case...
if (accountIdOrNull != null) {
final String accountId = Long.toString(accountIdOrNull);
final String[] accountIdParams =
@@ -5141,14 +5214,6 @@ public class ContactsProvider2 extends AbstractContactsProvider
" FROM " + Tables.RAW_CONTACTS +
" WHERE " + RawContactsColumns.ACCOUNT_ID + " = ?)",
accountIdParams);
- db.execSQL(
- "DELETE FROM " + Tables.METADATA_SYNC +
- " WHERE " + MetadataSyncColumns.ACCOUNT_ID + " = ?",
- accountIdParams);
- db.execSQL(
- "DELETE FROM " + Tables.METADATA_SYNC_STATE +
- " WHERE " + MetadataSyncStateColumns.ACCOUNT_ID + " = ?",
- accountIdParams);
// Delta API is only needed for regular contacts.
if (!inProfileMode()) {
@@ -5349,6 +5414,29 @@ public class ContactsProvider2 extends AbstractContactsProvider
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder, CancellationSignal cancellationSignal) {
+ LogFields.Builder logBuilder = LogFields.Builder.aLogFields()
+ .setApiType(LogUtils.ApiType.QUERY)
+ .setUriType(sUriMatcher.match(uri))
+ .setCallerIsSyncAdapter(readBooleanQueryParameter(
+ uri, ContactsContract.CALLER_IS_SYNCADAPTER, false))
+ .setStartNanos(SystemClock.elapsedRealtimeNanos());
+
+ Cursor cursor = null;
+ try {
+ cursor = queryInternal(uri, projection, selection, selectionArgs, sortOrder,
+ cancellationSignal);
+ return cursor;
+ } catch (Exception e) {
+ logBuilder.setException(e);
+ throw e;
+ } finally {
+ LogUtils.log(
+ logBuilder.setResultCount(cursor == null ? 0 : cursor.getCount()).build());
+ }
+ }
+
+ private Cursor queryInternal(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
if (VERBOSE_LOGGING) {
Log.v(TAG, "query: uri=" + uri + " projection=" + Arrays.toString(projection) +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
diff --git a/src/com/android/providers/contacts/DataRowHandler.java b/src/com/android/providers/contacts/DataRowHandler.java
index 235edfed..a82ce34f 100644
--- a/src/com/android/providers/contacts/DataRowHandler.java
+++ b/src/com/android/providers/contacts/DataRowHandler.java
@@ -121,7 +121,6 @@ public abstract class DataRowHandler {
if ((primary != null && primary != 0) || (superPrimary != null && superPrimary != 0)) {
final long mimeTypeId = getMimeTypeId();
mDbHelper.setIsPrimary(rawContactId, dataId, mimeTypeId);
- txContext.markRawContactMetadataDirty(rawContactId, /* isMetadataSyncAdapter =*/false);
// We also have to make sure that no other data item on this raw_contact is
// configured super primary
@@ -154,13 +153,11 @@ public abstract class DataRowHandler {
* @return true if update changed something
*/
public boolean update(SQLiteDatabase db, TransactionContext txContext,
- ContentValues values, Cursor c, boolean callerIsSyncAdapter,
- boolean callerIsMetadataSyncAdapter) {
+ ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
long dataId = c.getLong(DataUpdateQuery._ID);
long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
- handlePrimaryAndSuperPrimary(txContext, values, dataId, rawContactId,
- callerIsMetadataSyncAdapter);
+ handlePrimaryAndSuperPrimary(txContext, values, dataId, rawContactId);
handleHashIdForUpdate(values, dataId);
if (values.size() > 0) {
@@ -251,15 +248,13 @@ public abstract class DataRowHandler {
* configured correctly
*/
private void handlePrimaryAndSuperPrimary(TransactionContext txContext, ContentValues values,
- long dataId, long rawContactId, boolean callerIsMetadataSyncAdapter) {
+ long dataId, long rawContactId) {
final boolean hasPrimary = values.getAsInteger(Data.IS_PRIMARY) != null;
final boolean hasSuperPrimary = values.getAsInteger(Data.IS_SUPER_PRIMARY) != null;
// Nothing to do? Bail out early
if (!hasPrimary && !hasSuperPrimary) return;
- txContext.markRawContactMetadataDirty(rawContactId, callerIsMetadataSyncAdapter);
-
final long mimeTypeId = getMimeTypeId();
// Check if we want to clear values
@@ -325,7 +320,6 @@ public abstract class DataRowHandler {
db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
if (count != 0 && primary) {
fixPrimary(db, rawContactId);
- txContext.markRawContactMetadataDirty(rawContactId, /* isMetadataSyncAdapter =*/false);
}
if (hasSearchableData()) {
diff --git a/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java b/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java
index 5ae3a01f..063fcdb2 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForCommonDataKind.java
@@ -49,15 +49,14 @@ public class DataRowHandlerForCommonDataKind extends DataRowHandler {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
final long dataId = c.getLong(DataUpdateQuery._ID);
final ContentValues augmented = getAugmentedValues(db, dataId, values);
if (augmented == null) { // No change
return false;
}
enforceTypeAndLabel(augmented);
- return super.update(db, txContext, values, c, callerIsSyncAdapter,
- callerIsMetadataSyncAdapter);
+ return super.update(db, txContext, values, c, callerIsSyncAdapter);
}
/**
diff --git a/src/com/android/providers/contacts/DataRowHandlerForEmail.java b/src/com/android/providers/contacts/DataRowHandlerForEmail.java
index 3c7311f0..539c9596 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForEmail.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForEmail.java
@@ -50,8 +50,8 @@ public class DataRowHandlerForEmail extends DataRowHandlerForCommonDataKind {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
- if (!super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter)) {
+ Cursor c, boolean callerIsSyncAdapter) {
+ if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
return false;
}
diff --git a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
index b1c40497..3f310b18 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForGroupMembership.java
@@ -86,11 +86,11 @@ public class DataRowHandlerForGroupMembership extends DataRowHandler {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
resolveGroupSourceIdInValues(txContext, rawContactId, db, values, false);
- if (!super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter)) {
+ if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
return false;
}
boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
diff --git a/src/com/android/providers/contacts/DataRowHandlerForIdentity.java b/src/com/android/providers/contacts/DataRowHandlerForIdentity.java
index 4d4a33f6..32e9757e 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForIdentity.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForIdentity.java
@@ -46,9 +46,9 @@ public class DataRowHandlerForIdentity extends DataRowHandler {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
- super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter);
+ super.update(db, txContext, values, c, callerIsSyncAdapter);
// Identity affects aggregation.
final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
diff --git a/src/com/android/providers/contacts/DataRowHandlerForNickname.java b/src/com/android/providers/contacts/DataRowHandlerForNickname.java
index cc85c2b1..03b96a3a 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForNickname.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForNickname.java
@@ -52,11 +52,11 @@ public class DataRowHandlerForNickname extends DataRowHandlerForCommonDataKind {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
long dataId = c.getLong(DataUpdateQuery._ID);
long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
- if (!super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter)) {
+ if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
return false;
}
diff --git a/src/com/android/providers/contacts/DataRowHandlerForOrganization.java b/src/com/android/providers/contacts/DataRowHandlerForOrganization.java
index 5b69fe3c..66a3b1bd 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForOrganization.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForOrganization.java
@@ -51,8 +51,8 @@ public class DataRowHandlerForOrganization extends DataRowHandlerForCommonDataKi
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
- if (!super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter)) {
+ Cursor c, boolean callerIsSyncAdapter) {
+ if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
return false;
}
diff --git a/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java b/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
index 7bbac689..052252e1 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForPhoneNumber.java
@@ -58,10 +58,10 @@ public class DataRowHandlerForPhoneNumber extends DataRowHandlerForCommonDataKin
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
fillNormalizedNumber(values);
- if (!super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter)) {
+ if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
return false;
}
diff --git a/src/com/android/providers/contacts/DataRowHandlerForPhoto.java b/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
index 3d28b05c..532a8521 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForPhoto.java
@@ -76,7 +76,7 @@ public class DataRowHandlerForPhoto extends DataRowHandler {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
if (values.containsKey(SKIP_PROCESSING_KEY)) {
@@ -89,7 +89,7 @@ public class DataRowHandlerForPhoto extends DataRowHandler {
}
// Do the actual update.
- if (!super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter)) {
+ if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
return false;
}
diff --git a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
index b80f759d..044e9726 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
@@ -63,7 +63,7 @@ public class DataRowHandlerForStructuredName extends DataRowHandler {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
final long dataId = c.getLong(DataUpdateQuery._ID);
final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
@@ -74,7 +74,7 @@ public class DataRowHandlerForStructuredName extends DataRowHandler {
fixStructuredNameComponents(augmented, values);
- super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter);
+ super.update(db, txContext, values, c, callerIsSyncAdapter);
if (values.containsKey(StructuredName.DISPLAY_NAME)) {
augmented.putAll(values);
String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
diff --git a/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java b/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java
index 235bbd7c..7fc97b7a 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForStructuredPostal.java
@@ -60,7 +60,7 @@ public class DataRowHandlerForStructuredPostal extends DataRowHandler {
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
- Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
+ Cursor c, boolean callerIsSyncAdapter) {
final long dataId = c.getLong(DataUpdateQuery._ID);
final ContentValues augmented = getAugmentedValues(db, dataId, values);
if (augmented == null) { // No change
@@ -68,7 +68,7 @@ public class DataRowHandlerForStructuredPostal extends DataRowHandler {
}
fixStructuredPostalComponents(augmented, values);
- super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter);
+ super.update(db, txContext, values, c, callerIsSyncAdapter);
return true;
}
diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java
index 03ebd1f1..dc74c0ab 100644
--- a/src/com/android/providers/contacts/DbModifierWithNotification.java
+++ b/src/com/android/providers/contacts/DbModifierWithNotification.java
@@ -27,6 +27,7 @@ import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils.InsertHelper;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
import android.provider.CallLog.Calls;
@@ -65,6 +66,7 @@ public class DbModifierWithNotification implements DatabaseModifier {
Voicemails.DELETED + " == 0";
private final String mTableName;
private final SQLiteDatabase mDb;
+ private final boolean mHasReadVoicemailPermission;
private final InsertHelper mInsertHelper;
private final Context mContext;
private final Uri mBaseUri;
@@ -86,8 +88,14 @@ public class DbModifierWithNotification implements DatabaseModifier {
private DbModifierWithNotification(String tableName, SQLiteDatabase db,
InsertHelper insertHelper, Context context) {
+ this(tableName, db, insertHelper, true /* hasReadVoicemail */, context);
+ }
+
+ public DbModifierWithNotification(String tableName, SQLiteDatabase db,
+ InsertHelper insertHelper, boolean hasReadVoicemailPermission, Context context) {
mTableName = tableName;
mDb = db;
+ mHasReadVoicemailPermission = hasReadVoicemailPermission;
mInsertHelper = insertHelper;
mContext = context;
mBaseUri = mTableName.equals(Tables.VOICEMAIL_STATUS) ?
@@ -196,7 +204,16 @@ public class DbModifierWithNotification implements DatabaseModifier {
if (values.isEmpty()) {
return 0;
}
- int count = mDb.update(table, values, whereClause, whereArgs);
+
+ final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(mTableName);
+ qb.setProjectionMap(CallLogProvider.sCallsProjectionMap);
+ qb.setStrict(true);
+ if (!mHasReadVoicemailPermission) {
+ qb.setStrictGrammar(true);
+ }
+ int count = qb.update(mDb, values, whereClause, whereArgs);
+
if (count > 0 && isVoicemailContent || Tables.VOICEMAIL_STATUS.equals(table)) {
notifyVoicemailChange(mBaseUri, packagesModified);
}
@@ -269,14 +286,23 @@ public class DbModifierWithNotification implements DatabaseModifier {
// If the deletion is being made by the package that inserted the voicemail or by
// CP2 (cleanup after uninstall), then we don't need to wait for sync, so just delete it.
final int count;
+
+ final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(mTableName);
+ qb.setProjectionMap(CallLogProvider.sCallsProjectionMap);
+ qb.setStrict(true);
+ if (!mHasReadVoicemailPermission) {
+ qb.setStrictGrammar(true);
+ }
+
if (mIsCallsTable && isVoicemail && !isSelfModifyingOrInternal(packagesModified)) {
ContentValues values = new ContentValues();
values.put(VoicemailContract.Voicemails.DIRTY, 1);
values.put(VoicemailContract.Voicemails.DELETED, 1);
values.put(VoicemailContract.Voicemails.LAST_MODIFIED, getTimeMillis());
- count = mDb.update(table, values, whereClause, whereArgs);
+ count = qb.update(mDb, values, whereClause, whereArgs);
} else {
- count = mDb.delete(table, whereClause, whereArgs);
+ count = qb.delete(mDb, whereClause, whereArgs);
}
if (count > 0 && isVoicemail) {
diff --git a/src/com/android/providers/contacts/MetadataEntryParser.java b/src/com/android/providers/contacts/MetadataEntryParser.java
deleted file mode 100644
index 2fe423a8..00000000
--- a/src/com/android/providers/contacts/MetadataEntryParser.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.providers.contacts;
-
-import android.text.TextUtils;
-import com.android.providers.contacts.util.NeededForTesting;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-
-@NeededForTesting
-public class MetadataEntryParser {
-
- private final static String UNIQUE_CONTACT_ID = "unique_contact_id";
- private final static String ACCOUNT_TYPE = "account_type";
- private final static String CUSTOM_ACCOUNT_TYPE = "custom_account_type";
- private final static String ENUM_VALUE_FOR_GOOGLE_ACCOUNT = "GOOGLE_ACCOUNT";
- private final static String GOOGLE_ACCOUNT_TYPE = "com.google";
- private final static String ENUM_VALUE_FOR_CUSTOM_ACCOUNT = "CUSTOM_ACCOUNT";
- private final static String ACCOUNT_NAME = "account_name";
- private final static String DATA_SET = "data_set";
- private final static String ENUM_FOR_PLUS_DATA_SET = "GOOGLE_PLUS";
- private final static String ENUM_FOR_CUSTOM_DATA_SET = "CUSTOM";
- private final static String PLUS_DATA_SET_TYPE = "plus";
- private final static String CUSTOM_DATA_SET = "custom_data_set";
- private final static String CONTACT_ID = "contact_id";
- private final static String CONTACT_PREFS = "contact_prefs";
- private final static String SEND_TO_VOICEMAIL = "send_to_voicemail";
- private final static String STARRED = "starred";
- private final static String PINNED = "pinned";
- private final static String AGGREGATION_DATA = "aggregation_data";
- private final static String CONTACT_IDS = "contact_ids";
- private final static String TYPE = "type";
- private final static String FIELD_DATA = "field_data";
- private final static String FIELD_DATA_ID = "field_data_id";
- private final static String FIELD_DATA_PREFS = "field_data_prefs";
- private final static String IS_PRIMARY = "is_primary";
- private final static String IS_SUPER_PRIMARY = "is_super_primary";
- private final static String USAGE_STATS = "usage_stats";
- private final static String USAGE_TYPE = "usage_type";
- private final static String LAST_TIME_USED = "last_time_used";
- private final static String USAGE_COUNT = "usage_count";
-
- @NeededForTesting
- public static class UsageStats {
- @NeededForTesting
- final String mUsageType;
- @NeededForTesting
- final long mLastTimeUsed;
- @NeededForTesting
- final int mTimesUsed;
-
- @NeededForTesting
- public UsageStats(String usageType, long lastTimeUsed, int timesUsed) {
- this.mUsageType = usageType;
- this.mLastTimeUsed = lastTimeUsed;
- this.mTimesUsed = timesUsed;
- }
- }
-
- @NeededForTesting
- public static class FieldData {
- @NeededForTesting
- final String mDataHashId;
- @NeededForTesting
- final boolean mIsPrimary;
- @NeededForTesting
- final boolean mIsSuperPrimary;
- @NeededForTesting
- final ArrayList<UsageStats> mUsageStatsList;
-
- @NeededForTesting
- public FieldData(String dataHashId, boolean isPrimary, boolean isSuperPrimary,
- ArrayList<UsageStats> usageStatsList) {
- this.mDataHashId = dataHashId;
- this.mIsPrimary = isPrimary;
- this.mIsSuperPrimary = isSuperPrimary;
- this.mUsageStatsList = usageStatsList;
- }
- }
-
- @NeededForTesting
- public static class RawContactInfo {
- @NeededForTesting
- final String mBackupId;
- @NeededForTesting
- final String mAccountType;
- @NeededForTesting
- final String mAccountName;
- @NeededForTesting
- final String mDataSet;
-
- @NeededForTesting
- public RawContactInfo(String backupId, String accountType, String accountName,
- String dataSet) {
- this.mBackupId = backupId;
- this.mAccountType = accountType;
- this.mAccountName = accountName;
- mDataSet = dataSet;
- }
- }
-
- @NeededForTesting
- public static class AggregationData {
- @NeededForTesting
- final RawContactInfo mRawContactInfo1;
- @NeededForTesting
- final RawContactInfo mRawContactInfo2;
- @NeededForTesting
- final String mType;
-
- @NeededForTesting
- public AggregationData(RawContactInfo rawContactInfo1, RawContactInfo rawContactInfo2,
- String type) {
- this.mRawContactInfo1 = rawContactInfo1;
- this.mRawContactInfo2 = rawContactInfo2;
- this.mType = type;
- }
- }
-
- @NeededForTesting
- public static class MetadataEntry {
- @NeededForTesting
- final RawContactInfo mRawContactInfo;
- @NeededForTesting
- final int mSendToVoicemail;
- @NeededForTesting
- final int mStarred;
- @NeededForTesting
- final int mPinned;
- @NeededForTesting
- final ArrayList<FieldData> mFieldDatas;
- @NeededForTesting
- final ArrayList<AggregationData> mAggregationDatas;
-
- @NeededForTesting
- public MetadataEntry(RawContactInfo rawContactInfo,
- int sendToVoicemail, int starred, int pinned,
- ArrayList<FieldData> fieldDatas,
- ArrayList<AggregationData> aggregationDatas) {
- this.mRawContactInfo = rawContactInfo;
- this.mSendToVoicemail = sendToVoicemail;
- this.mStarred = starred;
- this.mPinned = pinned;
- this.mFieldDatas = fieldDatas;
- this.mAggregationDatas = aggregationDatas;
- }
- }
-
- @NeededForTesting
- static MetadataEntry parseDataToMetaDataEntry(String inputData) {
- if (TextUtils.isEmpty(inputData)) {
- throw new IllegalArgumentException("Input cannot be empty.");
- }
-
- try {
- final JSONObject root = new JSONObject(inputData);
- // Parse to get rawContactId and account info.
- final JSONObject uniqueContactJSON = root.getJSONObject(UNIQUE_CONTACT_ID);
- final RawContactInfo rawContactInfo = parseUniqueContact(uniqueContactJSON);
-
- // Parse contactPrefs to get sendToVoicemail, starred, pinned.
- final JSONObject contactPrefs = root.getJSONObject(CONTACT_PREFS);
- final boolean sendToVoicemail = contactPrefs.has(SEND_TO_VOICEMAIL)
- ? contactPrefs.getBoolean(SEND_TO_VOICEMAIL) : false;
- final boolean starred = contactPrefs.has(STARRED)
- ? contactPrefs.getBoolean(STARRED) : false;
- final int pinned = contactPrefs.has(PINNED) ? contactPrefs.getInt(PINNED) : 0;
-
- // Parse aggregationDatas
- final ArrayList<AggregationData> aggregationsList = new ArrayList<AggregationData>();
- if (root.has(AGGREGATION_DATA)) {
- final JSONArray aggregationDatas = root.getJSONArray(AGGREGATION_DATA);
-
- for (int i = 0; i < aggregationDatas.length(); i++) {
- final JSONObject aggregationData = aggregationDatas.getJSONObject(i);
- final JSONArray contacts = aggregationData.getJSONArray(CONTACT_IDS);
-
- if (contacts.length() != 2) {
- throw new IllegalArgumentException(
- "There should be two contacts for each aggregation.");
- }
- final JSONObject rawContact1 = contacts.getJSONObject(0);
- final RawContactInfo aggregationContact1 = parseUniqueContact(rawContact1);
- final JSONObject rawContact2 = contacts.getJSONObject(1);
- final RawContactInfo aggregationContact2 = parseUniqueContact(rawContact2);
- final String type = aggregationData.getString(TYPE);
- if (TextUtils.isEmpty(type)) {
- throw new IllegalArgumentException("Aggregation type cannot be empty.");
- }
-
- final AggregationData aggregation = new AggregationData(
- aggregationContact1, aggregationContact2, type);
- aggregationsList.add(aggregation);
- }
- }
-
- // Parse fieldDatas
- final ArrayList<FieldData> fieldDatasList = new ArrayList<FieldData>();
- if (root.has(FIELD_DATA)) {
- final JSONArray fieldDatas = root.getJSONArray(FIELD_DATA);
-
- for (int i = 0; i < fieldDatas.length(); i++) {
- final JSONObject fieldData = fieldDatas.getJSONObject(i);
- final String dataHashId = fieldData.getString(FIELD_DATA_ID);
- if (TextUtils.isEmpty(dataHashId)) {
- throw new IllegalArgumentException("Field data hash id cannot be empty.");
- }
- final JSONObject fieldDataPrefs = fieldData.getJSONObject(FIELD_DATA_PREFS);
- final boolean isPrimary = fieldDataPrefs.getBoolean(IS_PRIMARY);
- final boolean isSuperPrimary = fieldDataPrefs.getBoolean(IS_SUPER_PRIMARY);
-
- final ArrayList<UsageStats> usageStatsList = new ArrayList<UsageStats>();
- if (fieldData.has(USAGE_STATS)) {
- final JSONArray usageStats = fieldData.getJSONArray(USAGE_STATS);
- for (int j = 0; j < usageStats.length(); j++) {
- final JSONObject usageStat = usageStats.getJSONObject(j);
- final String usageType = usageStat.getString(USAGE_TYPE);
- if (TextUtils.isEmpty(usageType)) {
- throw new IllegalArgumentException("Usage type cannot be empty.");
- }
- final long lastTimeUsed = usageStat.getLong(LAST_TIME_USED);
- final int usageCount = usageStat.getInt(USAGE_COUNT);
-
- final UsageStats usageStatsParsed = new UsageStats(
- usageType, lastTimeUsed, usageCount);
- usageStatsList.add(usageStatsParsed);
- }
- }
-
- final FieldData fieldDataParse = new FieldData(dataHashId, isPrimary,
- isSuperPrimary, usageStatsList);
- fieldDatasList.add(fieldDataParse);
- }
- }
- final MetadataEntry metaDataEntry = new MetadataEntry(rawContactInfo,
- sendToVoicemail ? 1 : 0, starred ? 1 : 0, pinned,
- fieldDatasList, aggregationsList);
- return metaDataEntry;
- } catch (JSONException e) {
- throw new IllegalArgumentException("JSON Exception.", e);
- }
- }
-
- private static RawContactInfo parseUniqueContact(JSONObject uniqueContactJSON) {
- try {
- final String backupId = uniqueContactJSON.getString(CONTACT_ID);
- final String accountName = uniqueContactJSON.getString(ACCOUNT_NAME);
- String accountType = uniqueContactJSON.getString(ACCOUNT_TYPE);
- if (ENUM_VALUE_FOR_GOOGLE_ACCOUNT.equals(accountType)) {
- accountType = GOOGLE_ACCOUNT_TYPE;
- } else if (ENUM_VALUE_FOR_CUSTOM_ACCOUNT.equals(accountType)) {
- accountType = uniqueContactJSON.getString(CUSTOM_ACCOUNT_TYPE);
- } else {
- throw new IllegalArgumentException("Unknown account type.");
- }
-
- String dataSet = null;
- switch (uniqueContactJSON.getString(DATA_SET)) {
- case ENUM_FOR_PLUS_DATA_SET:
- dataSet = PLUS_DATA_SET_TYPE;
- break;
- case ENUM_FOR_CUSTOM_DATA_SET:
- dataSet = uniqueContactJSON.getString(CUSTOM_DATA_SET);
- break;
- }
- if (TextUtils.isEmpty(backupId) || TextUtils.isEmpty(accountType)
- || TextUtils.isEmpty(accountName)) {
- throw new IllegalArgumentException(
- "Contact backup id, account type, account name cannot be empty.");
- }
- final RawContactInfo rawContactInfo = new RawContactInfo(
- backupId, accountType, accountName, dataSet);
- return rawContactInfo;
- } catch (JSONException e) {
- throw new IllegalArgumentException("JSON Exception.", e);
- }
- }
-}
diff --git a/src/com/android/providers/contacts/ProfileProvider.java b/src/com/android/providers/contacts/ProfileProvider.java
index f3b6daf6..d9dc784e 100644
--- a/src/com/android/providers/contacts/ProfileProvider.java
+++ b/src/com/android/providers/contacts/ProfileProvider.java
@@ -116,8 +116,8 @@ public class ProfileProvider extends AbstractContactsProvider {
mDelegate.notifyChange();
}
- protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetWork) {
- mDelegate.notifyChange(syncToNetwork, syncToMetadataNetWork);
+ protected void notifyChange(boolean syncToNetwork) {
+ mDelegate.notifyChange(syncToNetwork);
}
protected Locale getLocale() {
diff --git a/src/com/android/providers/contacts/SearchIndexManager.java b/src/com/android/providers/contacts/SearchIndexManager.java
index e421654c..aeaa0e7d 100644
--- a/src/com/android/providers/contacts/SearchIndexManager.java
+++ b/src/com/android/providers/contacts/SearchIndexManager.java
@@ -55,7 +55,8 @@ public class SearchIndexManager {
private static final int MAX_STRING_BUILDER_SIZE = 1024 * 10;
public static final String PROPERTY_SEARCH_INDEX_VERSION = "search_index";
- private static final int SEARCH_INDEX_VERSION = 1;
+ private static final String ROW_ID_KEY = "rowid";
+ private static final int SEARCH_INDEX_VERSION = 2;
private static final class ContactIndexQuery {
public static final String[] COLUMNS = {
@@ -327,7 +328,7 @@ public class SearchIndexManager {
// Remove affected search_index rows.
final SQLiteDatabase db = mDbHelper.getWritableDatabase();
final int deleted = db.delete(Tables.SEARCH_INDEX,
- SearchIndexColumns.CONTACT_ID + " IN (SELECT " +
+ ROW_ID_KEY + " IN (SELECT " +
RawContacts.CONTACT_ID +
" FROM " + Tables.RAW_CONTACTS +
" WHERE " + rawContactsSelection +
@@ -400,6 +401,7 @@ public class SearchIndexManager {
mValues.put(SearchIndexColumns.NAME, builder.getName());
mValues.put(SearchIndexColumns.TOKENS, builder.getTokens());
mValues.put(SearchIndexColumns.CONTACT_ID, contactId);
+ mValues.put(ROW_ID_KEY, contactId);
db.insert(Tables.SEARCH_INDEX, null, mValues);
}
private int getSearchIndexVersion() {
diff --git a/src/com/android/providers/contacts/TransactionContext.java b/src/com/android/providers/contacts/TransactionContext.java
index dfb6d696..86dae01b 100644
--- a/src/com/android/providers/contacts/TransactionContext.java
+++ b/src/com/android/providers/contacts/TransactionContext.java
@@ -35,7 +35,6 @@ public class TransactionContext {
/** Map from raw contact id to account Id */
private ArrayMap<Long, Long> mInsertedRawContactsAccounts;
private ArraySet<Long> mUpdatedRawContacts;
- private ArraySet<Long> mMetadataDirtyRawContacts;
private ArraySet<Long> mBackupIdChangedRawContacts;
private ArraySet<Long> mDirtyRawContacts;
// Set used to track what has been changed and deleted. This is needed so we can update the
@@ -78,22 +77,6 @@ public class TransactionContext {
markRawContactChangedOrDeletedOrInserted(rawContactId);
}
- public void markRawContactMetadataDirty(long rawContactId, boolean isMetadataSyncAdapter) {
- if (!isMetadataSyncAdapter) {
- if (mMetadataDirtyRawContacts == null) {
- mMetadataDirtyRawContacts = new ArraySet<>();
- }
- mMetadataDirtyRawContacts.add(rawContactId);
- }
- }
-
- public void markBackupIdChangedRawContact(long rawContactId) {
- if (mBackupIdChangedRawContacts == null) {
- mBackupIdChangedRawContacts = new ArraySet<>();
- }
- mBackupIdChangedRawContacts.add(rawContactId);
- }
-
public void markRawContactChangedOrDeletedOrInserted(long rawContactId) {
if (mChangedRawContacts == null) {
mChangedRawContacts = new ArraySet<>();
@@ -131,16 +114,6 @@ public class TransactionContext {
return mDirtyRawContacts;
}
- public Set<Long> getMetadataDirtyRawContactIds() {
- if (mMetadataDirtyRawContacts == null) mMetadataDirtyRawContacts = new ArraySet<>();
- return mMetadataDirtyRawContacts;
- }
-
- public Set<Long> getBackupIdChangedRawContacts() {
- if (mBackupIdChangedRawContacts == null) mBackupIdChangedRawContacts = new ArraySet<>();
- return mBackupIdChangedRawContacts;
- }
-
public Set<Long> getChangedRawContactIds() {
if (mChangedRawContacts == null) mChangedRawContacts = new ArraySet<>();
return mChangedRawContacts;
@@ -176,7 +149,6 @@ public class TransactionContext {
mUpdatedRawContacts = null;
mUpdatedSyncStates = null;
mDirtyRawContacts = null;
- mMetadataDirtyRawContacts = null;
mChangedRawContacts = null;
mBackupIdChangedRawContacts = null;
}
diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java
index daecd973..ef7a3758 100644
--- a/src/com/android/providers/contacts/VoicemailContentProvider.java
+++ b/src/com/android/providers/contacts/VoicemailContentProvider.java
@@ -24,6 +24,7 @@ import static com.android.providers.contacts.util.DbQueryUtils.getEqualityClause
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.content.AttributionSource;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -119,24 +120,23 @@ public class VoicemailContentProvider extends ContentProvider
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
// Permit carrier-privileged apps regardless of ADD_VOICEMAIL permission state.
if (mVoicemailPermissions.callerHasCarrierPrivileges()) {
return MODE_ALLOWED;
}
- return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
+ return super.enforceReadPermissionInner(uri, attributionSource);
}
-
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
// Permit carrier-privileged apps regardless of ADD_VOICEMAIL permission state.
if (mVoicemailPermissions.callerHasCarrierPrivileges()) {
return MODE_ALLOWED;
}
- return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
+ return super.enforceWritePermissionInner(uri, attributionSource);
}
@VisibleForTesting
diff --git a/src/com/android/providers/contacts/VoicemailPermissions.java b/src/com/android/providers/contacts/VoicemailPermissions.java
index 58e7a146..b38f7f58 100644
--- a/src/com/android/providers/contacts/VoicemailPermissions.java
+++ b/src/com/android/providers/contacts/VoicemailPermissions.java
@@ -151,7 +151,12 @@ public class VoicemailPermissions {
}
private static boolean packageHasCarrierPrivileges(TelephonyManager tm, String packageName) {
- return tm.checkCarrierPrivilegesForPackageAnyPhone(packageName)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return tm.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
diff --git a/src/com/android/providers/contacts/util/LogFields.java b/src/com/android/providers/contacts/util/LogFields.java
new file mode 100644
index 00000000..4d07ca4b
--- /dev/null
+++ b/src/com/android/providers/contacts/util/LogFields.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts.util;
+
+import android.net.Uri;
+
+public final class LogFields {
+
+ private final int apiType;
+
+ private final int uriType;
+
+ private final boolean callerIsSyncAdapter;
+
+ private final long startNanos;
+
+ private Exception exception;
+
+ private Uri resultUri;
+
+ private int resultCount;
+
+ private int methodCall;
+
+ public LogFields(int apiType, int uriType, boolean callerIsSyncAdapter, long startNanos) {
+ this.apiType = apiType;
+ this.uriType = uriType;
+ this.callerIsSyncAdapter = callerIsSyncAdapter;
+ this.startNanos = startNanos;
+ }
+
+ public int getApiType() {
+ return apiType;
+ }
+
+ public int getUriType() {
+ return uriType;
+ }
+
+ public boolean isCallerIsSyncAdapter() {
+ return callerIsSyncAdapter;
+ }
+
+ public long getStartNanos() {
+ return startNanos;
+ }
+
+ public Exception getException() {
+ return exception;
+ }
+
+ public Uri getResultUri() {
+ return resultUri;
+ }
+
+ public int getResultCount() {
+ return resultCount;
+ }
+
+ public int getMethodCall() {
+ return methodCall;
+ }
+
+ public static final class Builder {
+ private int apiType;
+ private int uriType;
+ private boolean callerIsSyncAdapter;
+ private long startNanos;
+ private Exception exception;
+ private Uri resultUri;
+ private int resultCount;
+ private int methodCall;
+
+ private Builder() {
+ }
+
+ public static Builder aLogFields() {
+ return new Builder();
+ }
+
+ public Builder setApiType(int apiType) {
+ this.apiType = apiType;
+ return this;
+ }
+
+ public Builder setUriType(int uriType) {
+ this.uriType = uriType;
+ return this;
+ }
+
+ public Builder setCallerIsSyncAdapter(boolean callerIsSyncAdapter) {
+ this.callerIsSyncAdapter = callerIsSyncAdapter;
+ return this;
+ }
+
+ public Builder setStartNanos(long startNanos) {
+ this.startNanos = startNanos;
+ return this;
+ }
+
+ public Builder setException(Exception exception) {
+ this.exception = exception;
+ return this;
+ }
+
+ public Builder setResultUri(Uri resultUri) {
+ this.resultUri = resultUri;
+ return this;
+ }
+
+ public Builder setResultCount(int resultCount) {
+ this.resultCount = resultCount;
+ return this;
+ }
+
+ public Builder setMethodCall(int methodCall) {
+ this.methodCall = methodCall;
+ return this;
+ }
+
+ public LogFields build() {
+ LogFields logFields = new LogFields(apiType, uriType, callerIsSyncAdapter, startNanos);
+ logFields.resultCount = this.resultCount;
+ logFields.exception = this.exception;
+ logFields.resultUri = this.resultUri;
+ logFields.methodCall = this.methodCall;
+ return logFields;
+ }
+ }
+}
diff --git a/src/com/android/providers/contacts/util/LogUtils.java b/src/com/android/providers/contacts/util/LogUtils.java
new file mode 100644
index 00000000..a564a359
--- /dev/null
+++ b/src/com/android/providers/contacts/util/LogUtils.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.contacts.util;
+
+import android.os.SystemClock;
+import android.util.StatsEvent;
+import android.util.StatsLog;
+
+public class LogUtils {
+ // Keep in sync with ContactsProviderStatus#ResultType in
+ // frameworks/proto_logging/stats/atoms.proto file.
+ public interface ResultType {
+ int SUCCESS = 1;
+ int FAIL = 2;
+ int ILLEGAL_ARGUMENT = 3;
+ int UNSUPPORTED_OPERATION = 4;
+ }
+
+ // Keep in sync with ContactsProviderStatus#ApiType in
+ // frameworks/proto_logging/stats/atoms.proto file.
+ public interface ApiType {
+ int QUERY = 1;
+ int INSERT = 2;
+ int UPDATE = 3;
+ int DELETE = 4;
+ int CALL = 5;
+ }
+
+ // Keep in sync with ContactsProviderStatus#CallerType in
+ // frameworks/proto_logging/stats/atoms.proto file.
+ public interface CallerType {
+ int CALLER_IS_SYNC_ADAPTER = 1;
+ int CALLER_IS_NOT_SYNC_ADAPTER = 2;
+ }
+
+ // Keep in sync with ContactsProviderStatus#MethodCall in
+ // frameworks/proto_logging/stats/atoms.proto file.
+ public interface MethodCall {
+ int ADD_SIM_ACCOUNTS = 1;
+ int REMOVE_SIM_ACCOUNTS = 2;
+ int GET_SIM_ACCOUNTS = 3;
+ }
+
+ private static final int STATSD_LOG_ATOM_ID = 301;
+
+ public static void log(LogFields logFields) {
+ StatsLog.write(StatsEvent.newBuilder()
+ .setAtomId(STATSD_LOG_ATOM_ID)
+ .writeInt(logFields.getApiType())
+ .writeInt(logFields.getUriType())
+ .writeInt(getCallerType(logFields.isCallerIsSyncAdapter()))
+ .writeInt(getResultType(logFields.getException()))
+ .writeInt(logFields.getResultCount())
+ .writeLong(getLatencyMicros(logFields.getStartNanos()))
+ .writeInt(0) // Empty value for TaskType
+ .writeInt(logFields.getMethodCall())
+ .usePooledBuffer()
+ .build());
+ }
+
+ private static int getCallerType(boolean callerIsSyncAdapter) {
+ return callerIsSyncAdapter
+ ? CallerType.CALLER_IS_SYNC_ADAPTER : CallerType.CALLER_IS_NOT_SYNC_ADAPTER;
+ }
+
+ private static int getResultType(Exception exception) {
+ if (exception == null) {
+ return ResultType.SUCCESS;
+ } else if (exception instanceof IllegalArgumentException) {
+ return ResultType.ILLEGAL_ARGUMENT;
+ } else if (exception instanceof UnsupportedOperationException) {
+ return ResultType.UNSUPPORTED_OPERATION;
+ } else {
+ return ResultType.FAIL;
+ }
+ }
+
+ private static long getLatencyMicros(long startNanos) {
+ return (SystemClock.elapsedRealtimeNanos() - startNanos) / 1000;
+ }
+}
+
+
diff --git a/tests/assets/test1/testFileDeviceContactMetadataJSON.txt b/tests/assets/test1/testFileDeviceContactMetadataJSON.txt
deleted file mode 100644
index 65e624d1..00000000
--- a/tests/assets/test1/testFileDeviceContactMetadataJSON.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "unique_contact_id": {
- "account_type": "CUSTOM_ACCOUNT",
- "custom_account_type": "facebook",
- "account_name": "android-test",
- "contact_id": "1111111",
- "data_set": "FOCUS"
- },
- "contact_prefs": {
- "send_to_voicemail": true,
- "starred": false,
- "pinned": 2
- },
- "aggregation_data": [
- {
- "type": "TOGETHER",
- "contact_ids": [
- {
- "account_type": "GOOGLE_ACCOUNT",
- "account_name": "android-test2",
- "contact_id": "2222222",
- "data_set": "GOOGLE_PLUS"
- },
- {
- "account_type": "GOOGLE_ACCOUNT",
- "account_name": "android-test3",
- "contact_id": "3333333",
- "data_set": "CUSTOM",
- "custom_data_set": "custom type"
- }
- ]
- }
- ],
- "field_data": [
- {
- "field_data_id": "1001",
- "field_data_prefs": {
- "is_primary": true,
- "is_super_primary": true
- },
- "usage_stats": [
- {
- "usage_type": "CALL",
- "last_time_used": 10000001,
- "usage_count": 10
- },
- {
- "usage_type": "SHORT_TEXT",
- "last_time_used": 20000002,
- "usage_count": 20
- }
- ]
- },
- {
- "field_data_id": "1002",
- "field_data_prefs": {
- "is_primary": false,
- "is_super_primary": false
- },
- "usage_stats": [
- {
- "usage_type": "LONG_TEXT",
- "last_time_used": 30000003,
- "usage_count": 30
- }
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 91a76a31..816d10d7 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -1331,10 +1331,6 @@ public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase {
assertEquals(expected, (getContactsProvider()).isNetworkNotified());
}
- protected void assertMetadataNetworkNotified(boolean expected) {
- assertEquals(expected, (getContactsProvider()).isMetadataNetworkNotified());
- }
-
protected void assertProjection(Uri uri, String[] expectedProjection) {
Cursor cursor = mResolver.query(uri, null, "0", null, null);
String[] actualProjection = cursor.getColumnNames();
diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
index c9d09635..92b4b171 100644
--- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java
+++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java
@@ -62,7 +62,7 @@ public class CallLogProviderTest extends BaseContactsProvider2Test {
Voicemails.DIRTY,
Voicemails.DELETED};
/** Total number of columns exposed by call_log provider. */
- private static final int NUM_CALLLOG_FIELDS = 36;
+ private static final int NUM_CALLLOG_FIELDS = 40;
private static final int MIN_MATCH = 7;
@@ -498,6 +498,7 @@ public class CallLogProviderTest extends BaseContactsProvider2Test {
values.put(Calls.DATA_USAGE, 1000);
values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, (String) null);
values.put(Calls.PHONE_ACCOUNT_ID, (Long) null);
+ values.put(Calls.PRIORITY, Calls.PRIORITY_NORMAL);
break;
case 1:
values.put(Calls.NUMBER, "654321");
@@ -509,6 +510,7 @@ public class CallLogProviderTest extends BaseContactsProvider2Test {
values.put(Calls.DATA_USAGE, 0);
values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, (String) null);
values.put(Calls.PHONE_ACCOUNT_ID, (Long) null);
+ values.put(Calls.PRIORITY, Calls.PRIORITY_NORMAL);
break;
case 2:
values.put(Calls.NUMBER, "123456");
@@ -520,6 +522,7 @@ public class CallLogProviderTest extends BaseContactsProvider2Test {
values.put(Calls.DATA_USAGE, 2000);
values.put(Calls.PHONE_ACCOUNT_COMPONENT_NAME, (String) null);
values.put(Calls.PHONE_ACCOUNT_ID, (Long) null);
+ values.put(Calls.PRIORITY, Calls.PRIORITY_URGENT);
break;
}
return values;
diff --git a/tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java b/tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java
deleted file mode 100644
index 3d8b8eb3..00000000
--- a/tests/src/com/android/providers/contacts/ContactMetadataProviderTest.java
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.contacts;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract.MetadataSync;
-import android.provider.ContactsContract.MetadataSyncState;
-import android.provider.ContactsContract.RawContacts;
-import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.MediumTest;
-import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncColumns;
-import com.android.providers.contacts.testutil.RawContactUtil;
-import com.google.android.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Unit tests for {@link com.android.providers.contacts.ContactMetadataProvider}.
- * <p/>
- * Run the test like this:
- * <code>
- * adb shell am instrument -e class com.android.providers.contacts.ContactMetadataProviderTest -w \
- * com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
- * </code>
- */
-@MediumTest
-public class ContactMetadataProviderTest extends BaseContactsProvider2Test {
- private static String TEST_ACCOUNT_TYPE1 = "test_account_type1";
- private static String TEST_ACCOUNT_NAME1 = "test_account_name1";
- private static String TEST_DATA_SET1 = "plus";
- private static String TEST_BACKUP_ID1 = "1001";
- private static String TEST_DATA1 = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + TEST_BACKUP_ID1 + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 2\n" +
- " }\n" +
- " }";
- private static byte[] TEST_SYNC_STATE1 = "sync state1".getBytes();
- private static String TEST_ACCOUNT_TYPE2 = "test_account_type2";
- private static String TEST_ACCOUNT_NAME2 = "test_account_name2";
- private static String TEST_DATA_SET2 = null;
- private static String TEST_BACKUP_ID2 = "1002";
- private static String TEST_DATA2 = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE2 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME2 + ",\n" +
- " \"contact_id\": " + TEST_BACKUP_ID2 + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 2\n" +
- " }\n" +
- " }";
- private static byte[] TEST_SYNC_STATE2 = "sync state2".getBytes();
- private static String SELECTION_BY_TEST_ACCOUNT1 = MetadataSync.ACCOUNT_NAME + "='" +
- TEST_ACCOUNT_NAME1 + "' AND " + MetadataSync.ACCOUNT_TYPE + "='" + TEST_ACCOUNT_TYPE1 +
- "' AND " + MetadataSync.DATA_SET + "='" + TEST_DATA_SET1 + "'";
-
- private static String SELECTION_BY_TEST_ACCOUNT2 = MetadataSync.ACCOUNT_NAME + "='" +
- TEST_ACCOUNT_NAME2 + "' AND " + MetadataSync.ACCOUNT_TYPE + "='" + TEST_ACCOUNT_TYPE2 +
- "' AND " + MetadataSync.DATA_SET + "='" + TEST_DATA_SET2 + "'";
-
- private ContactMetadataProvider mContactMetadataProvider;
- private ContentValues defaultValues;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContactMetadataProvider = addProvider(
- ContactMetadataProviderTestable.class, MetadataSync.METADATA_AUTHORITY);
- // Reset the dbHelper to be the one ContactsProvider2 is using. Before this, two providers
- // are using different dbHelpers.
- mContactMetadataProvider.setDatabaseHelper(((SynchronousContactsProvider2)
- mActor.provider).getDatabaseHelper());
- setupData();
- }
-
- public void testInsertWithInvalidUri() {
- try {
- mResolver.insert(Uri.withAppendedPath(MetadataSync.METADATA_AUTHORITY_URI,
- "metadata"), getDefaultValues());
- fail("the insert was expected to fail, but it succeeded");
- } catch (IllegalArgumentException e) {
- // this was expected
- }
- }
-
- public void testUpdateWithInvalidUri() {
- try {
- mResolver.update(Uri.withAppendedPath(MetadataSync.METADATA_AUTHORITY_URI,
- "metadata"), getDefaultValues(), null, null);
- fail("the update was expected to fail, but it succeeded");
- } catch (IllegalArgumentException e) {
- // this was expected
- }
- }
-
- public void testGetMetadataByAccount() {
- Cursor c = mResolver.query(MetadataSync.CONTENT_URI, null, SELECTION_BY_TEST_ACCOUNT1,
- null, null);
- assertEquals(1, c.getCount());
-
- ContentValues expectedValues = defaultValues;
- expectedValues.remove(MetadataSyncColumns.ACCOUNT_ID);
- c.moveToFirst();
- assertCursorValues(c, expectedValues);
- c.close();
- }
-
- public void testFailOnInsertMetadataForSameAccountIdAndBackupId() {
- // Insert a new metadata with same account and backupId as defaultValues should fail.
- String newData = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + TEST_BACKUP_ID1 + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": false,\n" +
- " \"starred\": false,\n" +
- " \"pinned\": 1\n" +
- " }\n" +
- " }";
-
- ContentValues newValues = new ContentValues();
- newValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- newValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- newValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
- newValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, TEST_BACKUP_ID1);
- newValues.put(MetadataSync.DATA, newData);
- newValues.put(MetadataSync.DELETED, 0);
- try {
- mResolver.insert(MetadataSync.CONTENT_URI, newValues);
- } catch (Exception e) {
- // Expected.
- }
- }
-
- public void testInsertAndUpdateMetadataSync() {
- // Create a raw contact with backupId.
- String backupId = "backupId10001";
- long rawContactId = RawContactUtil.createRawContactWithAccountDataSet(
- mResolver, TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1);
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
- ContentValues values = new ContentValues();
- values.put(RawContacts.BACKUP_ID, backupId);
- assertEquals(1, mResolver.update(rawContactUri, values, null, null));
-
- assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, backupId);
- assertStoredValue(rawContactUri, RawContacts.DATA_SET, TEST_DATA_SET1);
-
- String deleted = "0";
- String insertJson = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 2\n" +
- " }\n" +
- " }";
-
- // Insert to MetadataSync table.
- ContentValues insertedValues = new ContentValues();
- insertedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- insertedValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- insertedValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- insertedValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
- insertedValues.put(MetadataSync.DATA, insertJson);
- insertedValues.put(MetadataSync.DELETED, deleted);
- Uri metadataUri = mResolver.insert(MetadataSync.CONTENT_URI, insertedValues);
-
- long metadataId = ContentUris.parseId(metadataUri);
- assertEquals(true, metadataId > 0);
-
- // Check if RawContact table is updated after inserting metadata.
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, backupId);
- assertStoredValue(rawContactUri, RawContacts.DATA_SET, TEST_DATA_SET1);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "1");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "2");
-
- // Update the MetadataSync table.
- String updatedJson = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": false,\n" +
- " \"starred\": false,\n" +
- " \"pinned\": 1\n" +
- " }\n" +
- " }";
- ContentValues updatedValues = new ContentValues();
- updatedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- updatedValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- updatedValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- updatedValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
- updatedValues.put(MetadataSync.DATA, updatedJson);
- updatedValues.put(MetadataSync.DELETED, deleted);
- mResolver.insert(MetadataSync.CONTENT_URI, updatedValues);
-
- // Check if the insert (actually update) is correct.
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- assertStoredValue(rawContactUri, RawContacts.DATA_SET, TEST_DATA_SET1);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "1");
- }
-
- public void testInsertMetadata() {
- String backupId = "newBackupId";
- String deleted = "0";
- String insertJson = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 2\n" +
- " }\n" +
- " }";
-
- // Insert to MetadataSync table.
- ContentValues insertedValues = new ContentValues();
- insertedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- insertedValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- insertedValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- insertedValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
- insertedValues.put(MetadataSync.DATA, insertJson);
- insertedValues.put(MetadataSync.DELETED, deleted);
- Uri metadataUri = mResolver.insert(MetadataSync.CONTENT_URI, insertedValues);
-
- long metadataId = ContentUris.parseId(metadataUri);
- assertEquals(true, metadataId > 0);
- }
-
- public void testFailUpdateDeletedMetadata() {
- String backupId = "backupId001";
- String newData = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": false,\n" +
- " \"starred\": false,\n" +
- " \"pinned\": 1\n" +
- " }\n" +
- " }";
-
- ContentValues newValues = new ContentValues();
- newValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- newValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- newValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
- newValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- newValues.put(MetadataSync.DATA, newData);
- newValues.put(MetadataSync.DELETED, 1);
-
- try {
- mResolver.insert(MetadataSync.CONTENT_URI, newValues);
- fail("the update was expected to fail, but it succeeded");
- } catch (IllegalArgumentException e) {
- // Expected
- }
- }
-
- public void testInsertWithNullData() {
- ContentValues newValues = new ContentValues();
- String data = null;
- String backupId = "backupId002";
- newValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- newValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- newValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
- newValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- newValues.put(MetadataSync.DATA, data);
- newValues.put(MetadataSync.DELETED, 0);
-
- try {
- mResolver.insert(MetadataSync.CONTENT_URI, newValues);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testDeleteMetadata() {
- //insert another metadata for TEST_ACCOUNT
- insertMetadata(TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, "2", TEST_DATA1, 0);
- Cursor c = mResolver.query(MetadataSync.CONTENT_URI, null, SELECTION_BY_TEST_ACCOUNT1,
- null, null);
- assertEquals(2, c.getCount());
- int numOfDeletion = mResolver.delete(MetadataSync.CONTENT_URI, SELECTION_BY_TEST_ACCOUNT1,
- null);
- assertEquals(2, numOfDeletion);
- c = mResolver.query(MetadataSync.CONTENT_URI, null, SELECTION_BY_TEST_ACCOUNT1,
- null, null);
- assertEquals(0, c.getCount());
- }
-
- public void testBulkInsert() {
- Cursor c = mResolver.query(MetadataSync.CONTENT_URI, new String[]{MetadataSync._ID},
- SELECTION_BY_TEST_ACCOUNT1, null, null);
- assertEquals(1, c.getCount());
-
- ContentValues values1 = getMetadataContentValues(
- TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, "123", TEST_DATA1, 0);
- ContentValues values2 = getMetadataContentValues(
- TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, "456", TEST_DATA1, 0);
- ContentValues[] values = new ContentValues[] {values1, values2};
-
- mResolver.bulkInsert(MetadataSync.CONTENT_URI, values);
- c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync._ID},
- SELECTION_BY_TEST_ACCOUNT1, null, null);
- assertEquals(3, c.getCount());
- }
-
- public void testBatchOperations() throws Exception {
- // Two mentadata_sync entries in the beginning, one for TEST_ACCOUNT1 and another for
- // TEST_ACCOUNT2
- Cursor c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync._ID},
- null, null, null);
- assertEquals(2, c.getCount());
-
- String updatedData = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + TEST_BACKUP_ID1 + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": false,\n" +
- " \"pinned\": 5\n" +
- " }\n" +
- " }";
-
- String newBackupId = "2222";
- String newData = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + TEST_ACCOUNT_TYPE1 + ",\n" +
- " \"account_name\": " + TEST_ACCOUNT_NAME1 + ",\n" +
- " \"contact_id\": " + newBackupId + ",\n" +
- " \"data_set\": \"GOOGLE_PLUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": false,\n" +
- " \"pinned\": 5\n" +
- " }\n" +
- " }";
-
- ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
- ops.add(ContentProviderOperation.newInsert(MetadataSync.CONTENT_URI)
- .withValue(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1)
- .withValue(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1)
- .withValue(MetadataSync.DATA_SET, TEST_DATA_SET1)
- .withValue(MetadataSync.RAW_CONTACT_BACKUP_ID, TEST_BACKUP_ID1)
- .withValue(MetadataSync.DATA, updatedData)
- .withValue(MetadataSync.DELETED, 0)
- .build());
-
- ops.add(ContentProviderOperation.newInsert(MetadataSync.CONTENT_URI)
- .withValue(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1)
- .withValue(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1)
- .withValue(MetadataSync.DATA_SET, TEST_DATA_SET1)
- .withValue(MetadataSync.RAW_CONTACT_BACKUP_ID, newBackupId)
- .withValue(MetadataSync.DATA, newData)
- .withValue(MetadataSync.DELETED, 0)
- .build());
-
- ops.add(ContentProviderOperation.newDelete(MetadataSync.CONTENT_URI)
- .withSelection(SELECTION_BY_TEST_ACCOUNT2, null)
- .build());
-
- // Batch three operations: update the metadata_entry of TEST_ACCOUNT1; insert one new
- // metadata_entry for TEST_ACCOUNT1; delete metadata_entry of TEST_ACCOUNT2
- mResolver.applyBatch(MetadataSync.METADATA_AUTHORITY, ops);
-
- // After the batch operations, there should be two metadata_entry for TEST_ACCOUNT1 with
- // new data value and no metadata_entry for TEST_ACCOUNT2.
- c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync.DATA},
- SELECTION_BY_TEST_ACCOUNT1, null, null);
- assertEquals(2, c.getCount());
- Set<String> actualData = new HashSet<>();
- while (c.moveToNext()) {
- actualData.add(c.getString(0));
- }
- c.close();
- MoreAsserts.assertContentsInAnyOrder(actualData, updatedData, newData);
-
- c = mResolver.query(MetadataSync.CONTENT_URI, new String[] {MetadataSync._ID},
- SELECTION_BY_TEST_ACCOUNT2, null, null);
- assertEquals(0, c.getCount());
- }
-
- public void testQueryMetadataSyncState() {
- String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
- MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
- final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
- final String[] projection = new String[]{MetadataSyncState.STATE};
- Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
- null);
- assertEquals(1, c.getCount());
- c.moveToFirst();
- assertTrue(Arrays.equals(TEST_SYNC_STATE1, c.getBlob(0)));
- c.close();
- }
-
- public void testUpdateMetadataSyncState() {
- mResolver.update(MetadataSyncState.CONTENT_URI, getSyncStateValues(TEST_ACCOUNT_NAME1,
- TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, TEST_SYNC_STATE2), null, null);
- String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
- MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
- final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
- final String[] projection = new String[] {MetadataSyncState.STATE};
- Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
- null);
-
- assertEquals(1, c.getCount());
- c.moveToFirst();
- assertTrue(Arrays.equals(TEST_SYNC_STATE2, c.getBlob(0)));
- c.close();
- }
-
- public void testUpdateMetadataSyncState_NonExisting() {
- // Delete the existing one first.
- String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
- MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
- final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
-
- mResolver.delete(MetadataSyncState.CONTENT_URI, selection, args);
-
- mResolver.update(MetadataSyncState.CONTENT_URI, getSyncStateValues(TEST_ACCOUNT_NAME1,
- TEST_ACCOUNT_TYPE1, TEST_DATA_SET1, TEST_SYNC_STATE2), null, null);
- final String[] projection = new String[] {MetadataSyncState.STATE};
- Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
- null);
-
- assertEquals(1, c.getCount());
- c.moveToFirst();
- assertTrue(Arrays.equals(TEST_SYNC_STATE2, c.getBlob(0)));
- c.close();
- }
-
- public void testDeleteMetadataSyncState() {
- String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND " +
- MetadataSyncState.ACCOUNT_TYPE + "=?2 AND " + MetadataSyncState.DATA_SET + "=?3";
- final String[] args = new String[]{TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1};
- final String[] projection = new String[]{MetadataSyncState.STATE};
- Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
- null);
- assertEquals(1, c.getCount());
- c.close();
-
- mResolver.delete(MetadataSyncState.CONTENT_URI, selection, args);
- c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
- null);
- assertEquals(0, c.getCount());
- c.close();
- }
-
- private void setupData() {
- long rawContactId1 = RawContactUtil.createRawContactWithAccountDataSet(
- mResolver, TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1);
- createAccount(TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1);
- insertMetadata(getDefaultValues());
- insertMetadataSyncState(TEST_ACCOUNT_NAME1, TEST_ACCOUNT_TYPE1, TEST_DATA_SET1,
- TEST_SYNC_STATE1);
-
- // Insert another entry for another account
- createAccount(TEST_ACCOUNT_NAME2, TEST_ACCOUNT_TYPE2, TEST_DATA_SET2);
- insertMetadata(TEST_ACCOUNT_NAME2, TEST_ACCOUNT_TYPE2, TEST_DATA_SET2, TEST_BACKUP_ID2,
- TEST_DATA2, 0);
- insertMetadataSyncState(TEST_ACCOUNT_NAME2, TEST_ACCOUNT_TYPE2, TEST_DATA_SET2,
- TEST_SYNC_STATE2);
- }
-
- private ContentValues getDefaultValues() {
- defaultValues = new ContentValues();
- defaultValues.put(MetadataSync.ACCOUNT_NAME, TEST_ACCOUNT_NAME1);
- defaultValues.put(MetadataSync.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE1);
- defaultValues.put(MetadataSync.DATA_SET, TEST_DATA_SET1);
- defaultValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, TEST_BACKUP_ID1);
- defaultValues.put(MetadataSync.DATA, TEST_DATA1);
- defaultValues.put(MetadataSync.DELETED, 0);
- return defaultValues;
- }
-
- private long insertMetadata(String accountName, String accountType, String dataSet,
- String backupId, String data, int deleted) {
- return insertMetadata(getMetadataContentValues(
- accountName, accountType, dataSet, backupId, data, deleted));
- }
-
- private ContentValues getMetadataContentValues(String accountName, String accountType,
- String dataSet, String backupId, String data, int deleted) {
- ContentValues values = new ContentValues();
- values.put(MetadataSync.ACCOUNT_NAME, accountName);
- values.put(MetadataSync.ACCOUNT_TYPE, accountType);
- values.put(MetadataSync.DATA_SET, dataSet);
- values.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- values.put(MetadataSync.DATA, data);
- values.put(MetadataSync.DELETED, deleted);
- return values;
- }
-
- private long insertMetadata(ContentValues values) {
- return ContentUris.parseId(mResolver.insert(MetadataSync.CONTENT_URI, values));
- }
-
- private long insertMetadataSyncState(String accountName, String accountType,
- String dataSet, byte[] state) {
- return ContentUris.parseId(mResolver.insert(MetadataSyncState.CONTENT_URI,
- getSyncStateValues(accountName, accountType, dataSet, state)));
- }
-
- private ContentValues getSyncStateValues(String accountName, String accountType,
- String dataSet, byte[] state) {
- ContentValues values = new ContentValues();
- values.put(MetadataSyncState.ACCOUNT_NAME, accountName);
- values.put(MetadataSyncState.ACCOUNT_TYPE, accountType);
- values.put(MetadataSyncState.DATA_SET, dataSet);
- values.put(MetadataSyncState.STATE, state);
- return values;
- }
-}
diff --git a/tests/src/com/android/providers/contacts/ContactMetadataProviderTestable.java b/tests/src/com/android/providers/contacts/ContactMetadataProviderTestable.java
deleted file mode 100644
index c46643f2..00000000
--- a/tests/src/com/android/providers/contacts/ContactMetadataProviderTestable.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.providers.contacts;
-
-public class ContactMetadataProviderTestable extends ContactMetadataProvider {
- @Override
- void ensureCaller() {
- // Not testable, skip.
- }
-}
diff --git a/tests/src/com/android/providers/contacts/ContactsActor.java b/tests/src/com/android/providers/contacts/ContactsActor.java
index 804e79a9..56a4fc4b 100644
--- a/tests/src/com/android/providers/contacts/ContactsActor.java
+++ b/tests/src/com/android/providers/contacts/ContactsActor.java
@@ -248,6 +248,11 @@ public class ContactsActor {
public boolean isUserUnlocked(int userId) {
return true; // Just make it always unlocked for now.
}
+
+ @Override
+ public boolean isUserRunning(int userId) {
+ return true;
+ }
}
private MockTelephonyManager mMockTelephonyManager;
diff --git a/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java
index 1832b4e4..d50a2922 100644
--- a/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java
+++ b/tests/src/com/android/providers/contacts/ContactsDatabaseHelperUpgradeTest.java
@@ -30,8 +30,6 @@ import android.provider.ContactsContract.DeletedContacts;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.DisplayNameSources;
import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.MetadataSync;
-import android.provider.ContactsContract.MetadataSyncState;
import android.provider.ContactsContract.PhotoFiles;
import android.provider.ContactsContract.PinnedPositions;
import android.provider.ContactsContract.RawContacts;
@@ -51,8 +49,6 @@ import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncColumns;
-import com.android.providers.contacts.ContactsDatabaseHelper.MetadataSyncStateColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NicknameLookupColumns;
@@ -63,7 +59,6 @@ import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.testutil.TestUtil;
import com.android.providers.contacts.util.PropertyUtils;
/**
@@ -225,6 +220,8 @@ public class ContactsDatabaseHelperUpgradeTest extends BaseDatabaseHelperUpgrade
new TableColumn(AccountsColumns.ACCOUNT_NAME, TEXT, false, null),
new TableColumn(AccountsColumns.ACCOUNT_TYPE, TEXT, false, null),
new TableColumn(AccountsColumns.DATA_SET, TEXT, false, null),
+ new TableColumn(AccountsColumns.SIM_SLOT_INDEX, INTEGER, false, null),
+ new TableColumn(AccountsColumns.SIM_EF_TYPE, INTEGER, false, null),
};
private static final TableColumn[] CONTACTS_COLUMNS = new TableColumn[] {
@@ -529,26 +526,12 @@ public class ContactsDatabaseHelperUpgradeTest extends BaseDatabaseHelperUpgrade
new TableColumn(DataUsageStatColumns.LR_LAST_TIME_USED, INTEGER, true, "0"),
};
- private static final TableColumn[] METADATA_SYNC_COLUMNS = new TableColumn[] {
- new TableColumn(MetadataSync._ID, INTEGER, false, null),
- new TableColumn(MetadataSync.RAW_CONTACT_BACKUP_ID, TEXT, true, null),
- new TableColumn(MetadataSyncColumns.ACCOUNT_ID, INTEGER, true, null),
- new TableColumn(MetadataSync.DATA, TEXT, false, null),
- new TableColumn(MetadataSync.DELETED, INTEGER, true, "0"),
- };
-
private static final TableColumn[] PRE_AUTHORIZED_URIS_COLUMNS = new TableColumn[] {
new TableColumn(PreAuthorizedUris._ID, INTEGER, false, null),
new TableColumn(PreAuthorizedUris.URI, STRING, true, null),
new TableColumn(PreAuthorizedUris.EXPIRATION, INTEGER, true, "0"),
};
- private static final TableColumn[] METADATA_SYNC_STATE_COLUMNS = new TableColumn[] {
- new TableColumn(MetadataSyncState._ID, INTEGER, false, null),
- new TableColumn(MetadataSyncStateColumns.ACCOUNT_ID, INTEGER, true, null),
- new TableColumn(MetadataSyncState.STATE, BLOB, false, null),
- };
-
private static final TableColumn[] PRESENCE_COLUMNS = new TableColumn[] {
new TableColumn(StatusUpdates.DATA_ID, INTEGER, false, null),
new TableColumn(StatusUpdates.PROTOCOL, INTEGER, true, null),
@@ -592,9 +575,7 @@ public class ContactsDatabaseHelperUpgradeTest extends BaseDatabaseHelperUpgrade
new TableListEntry(Tables.STATUS_UPDATES, STATUS_UPDATES_COLUMNS),
new TableListEntry(Tables.DIRECTORIES, DIRECTORIES_COLUMNS),
new TableListEntry(Tables.DATA_USAGE_STAT, DATA_USAGE_STAT_COLUMNS),
- new TableListEntry(Tables.METADATA_SYNC, METADATA_SYNC_COLUMNS),
new TableListEntry(Tables.PRE_AUTHORIZED_URIS, PRE_AUTHORIZED_URIS_COLUMNS),
- new TableListEntry(Tables.METADATA_SYNC_STATE, METADATA_SYNC_STATE_COLUMNS),
new TableListEntry(Tables.PRESENCE, PRESENCE_COLUMNS),
new TableListEntry(Tables.AGGREGATED_PRESENCE, AGGREGATED_PRESENCE_COLUMNS)
};
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 7860b63d..7efc2f47 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -55,8 +55,6 @@ import android.provider.ContactsContract.DisplayNameSources;
import android.provider.ContactsContract.DisplayPhoto;
import android.provider.ContactsContract.FullNameStyle;
import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.MetadataSync;
-import android.provider.ContactsContract.MetadataSyncState;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.PhoneticNameStyle;
import android.provider.ContactsContract.PinnedPositions;
@@ -86,11 +84,6 @@ import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
-import com.android.providers.contacts.MetadataEntryParser.AggregationData;
-import com.android.providers.contacts.MetadataEntryParser.FieldData;
-import com.android.providers.contacts.MetadataEntryParser.MetadataEntry;
-import com.android.providers.contacts.MetadataEntryParser.RawContactInfo;
-import com.android.providers.contacts.MetadataEntryParser.UsageStats;
import com.android.providers.contacts.testutil.CommonDatabaseUtils;
import com.android.providers.contacts.testutil.ContactUtil;
import com.android.providers.contacts.testutil.DataUtil;
@@ -2894,333 +2887,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
}
}
- public void testUpdateFromMetadataEntry() {
- String accountType1 = "accountType1";
- String accountName1 = "accountName1";
- String dataSet1 = "plus";
- Account account1 = new Account(accountName1, accountType1);
- long rawContactId = RawContactUtil.createRawContactWithName(mResolver, account1);
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
- // Add backup_id for the raw contact.
- String backupId = "backupId100001";
- ContentValues values = new ContentValues();
- values.put(RawContacts.BACKUP_ID, backupId);
- assertEquals(1, mResolver.update(rawContactUri, values, null, null));
-
- String emailAddress = "address@email.com";
- Uri dataUri = insertEmail(rawContactId, emailAddress);
- String hashId = getStoredValue(dataUri, Data.HASH_ID);
-
- // Another data that should not be updated.
- String phoneNumber = "111-111-1111";
- Uri dataUri2 = insertPhoneNumber(rawContactId, phoneNumber);
-
- // Aggregation should be deleted from local since it doesn't exist in server.
- long toBeDeletedAggRawContactId = RawContactUtil.createRawContactWithName(
- mResolver, account1);
- setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
- rawContactId, toBeDeletedAggRawContactId);
-
- // Check if AggregationException table has one value.
- assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.RAW_CONTACT_ID1,
- rawContactId);
- assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.RAW_CONTACT_ID2,
- toBeDeletedAggRawContactId);
- assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.TYPE,
- AggregationExceptions.TYPE_KEEP_SEPARATE);
-
- String accountType2 = "accountType2";
- String accountName2 = "accountName2";
- Account account2 = new Account(accountName2, accountType2);
- long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, account2);
- Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
- String backupId2 = "backupId100003";
- ContentValues values2 = new ContentValues();
- values2.put(RawContacts.BACKUP_ID, backupId2);
- assertEquals(1, mResolver.update(rawContactUri2, values2, null, null));
-
- String usageTypeString = "CALL";
- int lastTimeUsed = 1111111;
- int timesUsed = 5;
- String aggregationTypeString = "TOGETHER";
- int aggregationType = AggregationExceptions.TYPE_KEEP_TOGETHER;
-
- RawContactInfo rawContactInfo = new RawContactInfo(
- backupId, accountType1, accountName1, null);
- UsageStats usageStats = new UsageStats(usageTypeString, lastTimeUsed, timesUsed);
- ArrayList<UsageStats> usageStatsList = new ArrayList<>();
- usageStatsList.add(usageStats);
- FieldData fieldData = new FieldData(hashId, true, true, usageStatsList);
- ArrayList<FieldData> fieldDataList = new ArrayList<>();
- fieldDataList.add(fieldData);
- ArrayList<AggregationData> aggregationDataList = new ArrayList<>();
- MetadataEntry metadataEntry = new MetadataEntry(rawContactInfo,
- 1, 1, 1, fieldDataList, aggregationDataList);
-
- ContactsProvider2 provider = (ContactsProvider2) getProvider();
- final ContactsDatabaseHelper helper =
- ((ContactsDatabaseHelper) provider.getDatabaseHelper());
- SQLiteDatabase db = helper.getWritableDatabase();
-
- // Before updating tables from MetadataEntry.
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType1);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName1);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
- assertStoredValue(dataUri, Data.IS_PRIMARY, 0);
- assertStoredValue(dataUri, Data.IS_SUPER_PRIMARY, 0);
-
- // Update tables without aggregation first, since aggregator will affect pinned value.
- provider.updateFromMetaDataEntry(db, metadataEntry);
-
- // After updating tables from MetadataEntry.
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType1);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName1);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "1");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "1");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "1");
- assertStoredValue(dataUri, Data.IS_PRIMARY, 1);
- assertStoredValue(dataUri, Data.IS_SUPER_PRIMARY, 1);
- assertStoredValue(dataUri2, Data.IS_PRIMARY, 0);
- assertStoredValue(dataUri2, Data.IS_SUPER_PRIMARY, 0);
- final Uri dataUriWithUsageType = Data.CONTENT_URI.buildUpon().appendQueryParameter(
- DataUsageFeedback.USAGE_TYPE, usageTypeString).build();
- assertDataUsageZero(dataUriWithUsageType, emailAddress);
-
- // Update AggregationException table.
- RawContactInfo aggregationContact = new RawContactInfo(
- backupId2, accountType2, accountName2, null);
- AggregationData aggregationData = new AggregationData(
- rawContactInfo, aggregationContact, aggregationTypeString);
- aggregationDataList.add(aggregationData);
- metadataEntry = new MetadataEntry(rawContactInfo,
- 1, 1, 1, fieldDataList, aggregationDataList);
- provider.updateFromMetaDataEntry(db, metadataEntry);
-
- // Check if AggregationException table is updated.
- assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.RAW_CONTACT_ID1,
- rawContactId);
- assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.RAW_CONTACT_ID2,
- rawContactId2);
- assertStoredValue(AggregationExceptions.CONTENT_URI, AggregationExceptions.TYPE,
- aggregationType);
-
- // After aggregation, check if rawContacts.starred/send_to_voicemail
- // were copied to contacts table.
- final long contactId = queryContactId(rawContactId);
- Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
- // The merged contact should be starred if any of the rawcontact is starred.
- assertStoredValue(contactUri, Contacts.STARRED, 1);
- // The merged contact should be send_to_voicemail
- // if all of the rawcontact is send_to_voicemail.
- assertStoredValue(contactUri, Contacts.SEND_TO_VOICEMAIL, 0);
- }
-
- public void testUpdateMetadataOnRawContactInsert() throws Exception {
- ContactMetadataProvider contactMetadataProvider = addProvider(
- ContactMetadataProviderTestable.class, MetadataSync.METADATA_AUTHORITY);
- // Reset the dbHelper to be the one ContactsProvider2 is using. Before this, two providers
- // are using different dbHelpers.
- contactMetadataProvider.setDatabaseHelper(((SynchronousContactsProvider2)
- mActor.provider).getDatabaseHelper());
- // Create an account first.
- String backupId = "backupId001";
- String accountType = "accountType";
- String accountName = "accountName";
- Account account = new Account(accountName, accountType);
- createAccount(accountName, accountType, null);
-
- // Insert a metadata to MetadataSync table.
- String data = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + accountType + ",\n" +
- " \"account_name\": " + accountName + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"FOCUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 1\n" +
- " }\n" +
- " }";
-
- ContentValues insertedValues = new ContentValues();
- insertedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- insertedValues.put(MetadataSync.ACCOUNT_TYPE, accountType);
- insertedValues.put(MetadataSync.ACCOUNT_NAME, accountName);
- insertedValues.put(MetadataSync.DATA, data);
- mResolver.insert(MetadataSync.CONTENT_URI, insertedValues);
-
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
- // Insert a raw contact.
- long rawContactId = RawContactUtil.createRawContactWithBackupId(mResolver, backupId,
- account);
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
- // Check if the raw contact is not updated since Lychee is removed.
- assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName);
- assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, backupId);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
- // No metadata network notify.
- assertMetadataNetworkNotified(false);
- }
-
- public void testUpdateMetadataOnRawContactBackupIdChange() throws Exception {
- ContactMetadataProvider contactMetadataProvider = addProvider(
- ContactMetadataProviderTestable.class, MetadataSync.METADATA_AUTHORITY);
- // Reset the dbHelper to be the one ContactsProvider2 is using. Before this, two providers
- // are using different dbHelpers.
- contactMetadataProvider.setDatabaseHelper(((SynchronousContactsProvider2)
- mActor.provider).getDatabaseHelper());
- // Create an account first.
- String backupId = "backupId001";
- String accountType = "accountType";
- String accountName = "accountName";
- Account account = new Account(accountName, accountType);
- createAccount(accountName, accountType, null);
-
- // Insert a metadata to MetadataSync table.
- String data = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + accountType + ",\n" +
- " \"account_name\": " + accountName + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"FOCUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 1\n" +
- " }\n" +
- " }";
-
- ContentValues insertedValues = new ContentValues();
- insertedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- insertedValues.put(MetadataSync.ACCOUNT_TYPE, accountType);
- insertedValues.put(MetadataSync.ACCOUNT_NAME, accountName);
- insertedValues.put(MetadataSync.DATA, data);
- mResolver.insert(MetadataSync.CONTENT_URI, insertedValues);
-
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
- // Insert a raw contact without backup_id.
- long rawContactId = RawContactUtil.createRawContact(mResolver, account);
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
- // Check if the raw contact is not updated because of no backup_id.
- assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
-
- // Update the raw contact with backup_id.
- ContentValues updatedValues = new ContentValues();
- updatedValues.put(RawContacts.BACKUP_ID, backupId);
- mResolver.update(RawContacts.CONTENT_URI, updatedValues, null, null);
- // Check if the raw contact is still not updated.
- assertStoredValue(rawContactUri, RawContacts._ID, rawContactId);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_TYPE, accountType);
- assertStoredValue(rawContactUri, RawContacts.ACCOUNT_NAME, accountName);
- assertStoredValue(rawContactUri, RawContacts.SEND_TO_VOICEMAIL, "0");
- assertStoredValue(rawContactUri, RawContacts.STARRED, "0");
- assertStoredValue(rawContactUri, RawContacts.PINNED, "0");
- // No metadata network notify.
- assertMetadataNetworkNotified(false);
- }
-
- public void testDeleteMetadataOnRawContactDelete() throws Exception {
- ContactMetadataProvider contactMetadataProvider = addProvider(
- ContactMetadataProviderTestable.class, MetadataSync.METADATA_AUTHORITY);
- // Reset the dbHelper to be the one ContactsProvider2 is using. Before this, two providers
- // are using different dbHelpers.
- contactMetadataProvider.setDatabaseHelper(((SynchronousContactsProvider2)
- mActor.provider).getDatabaseHelper());
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
- // Create an account first.
- String backupId = "backupId001";
- String accountType = "accountType";
- String accountName = "accountName";
- Account account = new Account(accountName, accountType);
- createAccount(accountName, accountType, null);
-
- // Insert a raw contact.
- long rawContactId = RawContactUtil.createRawContactWithBackupId(mResolver, backupId,
- account);
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-
- // Insert a metadata to MetadataSync table.
- String data = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + accountType + ",\n" +
- " \"account_name\": " + accountName + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"FOCUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 1\n" +
- " }\n" +
- " }";
-
- ContentValues insertedValues = new ContentValues();
- insertedValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- insertedValues.put(MetadataSync.ACCOUNT_TYPE, accountType);
- insertedValues.put(MetadataSync.ACCOUNT_NAME, accountName);
- insertedValues.put(MetadataSync.DATA, data);
- Uri metadataUri = mResolver.insert(MetadataSync.CONTENT_URI, insertedValues);
-
- // Delete raw contact.
- mResolver.delete(rawContactUri, null, null);
- // Check if the metadata is not deleted.
- assertStoredValue(metadataUri, MetadataSync.DELETED, "0");
- // check raw contact metadata_dirty column is not changed on raw contact deletion
- assertMetadataDirty(rawContactUri, false);
- // Lychee removed. Will not notify it.
- assertMetadataNetworkNotified(false);
-
- // Add another rawcontact and metadata, and don't delete them.
- // Insert a raw contact.
- String backupId2 = "newBackupId";
- long rawContactId2 = RawContactUtil.createRawContactWithBackupId(mResolver, backupId2,
- account);
- Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-
- // Insert a metadata to MetadataSync table.
- ContentValues insertedValues2 = new ContentValues();
- insertedValues2.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId2);
- insertedValues2.put(MetadataSync.ACCOUNT_TYPE, accountType);
- insertedValues2.put(MetadataSync.ACCOUNT_NAME, accountName);
- insertedValues2.put(MetadataSync.DATA, data);
- Uri metadataUri2 = mResolver.insert(MetadataSync.CONTENT_URI, insertedValues2);
-
- // Update raw contact but not delete.
- ContentValues values = new ContentValues();
- values.put(RawContacts.STARRED, "1");
- mResolver.update(rawContactUri2, values, null, null);
-
- // Check if the metadata is not marked as deleted.
- assertStoredValue(metadataUri2, MetadataSync.DELETED, "0");
- // Will not set metadata_dirty since Lychee is removed.
- assertMetadataDirty(rawContactUri2, false);
- // Will not notify Lychee since it's removed.
- assertMetadataNetworkNotified(false);
- }
-
public void testPostalsQuery() {
long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Alice", "Nextore");
Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
@@ -5197,10 +4863,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
}
public void testSetSendToVoicemailAndRingtone() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
assertDirty(rawContactUri, true);
@@ -5210,14 +4872,12 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
updateSendToVoicemailAndRingtone(contactId, true, "foo");
assertSendToVoicemailAndRingtone(contactId, true, "foo");
assertNetworkNotified(false);
- assertMetadataNetworkNotified(false);
assertDirty(rawContactUri, false);
assertMetadataDirty(rawContactUri, false);
updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
assertSendToVoicemailAndRingtone(contactId, false, "bar");
assertNetworkNotified(false);
- assertMetadataNetworkNotified(false);
assertDirty(rawContactUri, false);
assertMetadataDirty(rawContactUri, false);
}
@@ -5278,10 +4938,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
}
public void testMarkMetadataDirtyAfterAggregation() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "i", "j");
long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "k", "l");
Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
@@ -5300,7 +4956,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertMetadataDirty(rawContactUri1, false);
assertMetadataDirty(rawContactUri2, false);
assertNetworkNotified(false);
- assertMetadataNetworkNotified(false);
}
public void testStatusUpdateInsert() {
@@ -6766,98 +6421,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertStoredValue(safeStreamItemPhotoUri, StreamItemPhotos._ID, safeStreamItemPhotoId);
}
- public void testMetadataSyncCleanedUpOnAccountRemoval() throws Exception {
- Account doomedAccount = new Account("doom", "doom");
- createAccount(doomedAccount.name, doomedAccount.type, null);
- Account safeAccount = new Account("safe", "safe");
- createAccount(safeAccount.name, safeAccount.type, null);
- ContactsProvider2 cp = (ContactsProvider2) getProvider();
- mActor.setAccounts(new Account[]{doomedAccount, safeAccount});
- cp.onAccountsUpdated(new Account[]{doomedAccount, safeAccount});
-
- ContactMetadataProvider contactMetadataProvider = addProvider(
- ContactMetadataProviderTestable.class, MetadataSync.METADATA_AUTHORITY);
- // Reset the dbHelper to be the one ContactsProvider2 is using. Before this, two providers
- // are using different dbHelpers.
- contactMetadataProvider.setDatabaseHelper(((SynchronousContactsProvider2)
- mActor.provider).getDatabaseHelper());
-
- // Create a doomed metadata.
- String backupId = "backupIdForDoomed";
- ContentValues metadataValues = new ContentValues();
- metadataValues.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId);
- metadataValues.put(MetadataSync.ACCOUNT_TYPE, doomedAccount.type);
- metadataValues.put(MetadataSync.ACCOUNT_NAME, doomedAccount.name);
- metadataValues.put(MetadataSync.DATA,
- getDefaultMetadataJSONString(doomedAccount.type, doomedAccount.name, backupId));
- Uri doomedMetadataUri = mResolver.insert(MetadataSync.CONTENT_URI, metadataValues);
- // Create a doomed metadata sync state.
- ContentValues syncStateValues = new ContentValues();
- syncStateValues.put(MetadataSyncState.ACCOUNT_TYPE, doomedAccount.type);
- syncStateValues.put(MetadataSyncState.ACCOUNT_NAME, doomedAccount.name);
- syncStateValues.put(MetadataSyncState.STATE, "syncState");
- mResolver.insert(MetadataSyncState.CONTENT_URI, syncStateValues);
-
- // Create a safe metadata.
- String backupId2 = "backupIdForSafe";
- ContentValues insertedValues2 = new ContentValues();
- insertedValues2.put(MetadataSync.RAW_CONTACT_BACKUP_ID, backupId2);
- insertedValues2.put(MetadataSync.ACCOUNT_TYPE, safeAccount.type);
- insertedValues2.put(MetadataSync.ACCOUNT_NAME, safeAccount.name);
- insertedValues2.put(MetadataSync.DATA,
- getDefaultMetadataJSONString(safeAccount.type, safeAccount.name, backupId2));
- Uri safeMetadataUri = mResolver.insert(MetadataSync.CONTENT_URI, insertedValues2);
- // Create a safe metadata sync state.
- ContentValues syncStateValues2 = new ContentValues();
- syncStateValues2.put(MetadataSyncState.ACCOUNT_TYPE, safeAccount.type);
- syncStateValues2.put(MetadataSyncState.ACCOUNT_NAME, safeAccount.name);
- syncStateValues2.put(MetadataSyncState.STATE, "syncState2");
- mResolver.insert(MetadataSyncState.CONTENT_URI, syncStateValues2);
-
- // Remove the doomed account.
- mActor.setAccounts(new Account[]{safeAccount});
- cp.onAccountsUpdated(new Account[]{safeAccount});
-
- // Check that the doomed stuff has all been nuked.
- ContentValues[] noValues = new ContentValues[0];
- assertStoredValues(doomedMetadataUri, noValues);
- String selection = MetadataSyncState.ACCOUNT_NAME + "=?1 AND "
- + MetadataSyncState.ACCOUNT_TYPE + "=?2";
- String[] args = new String[]{doomedAccount.name, doomedAccount.type};
- final String[] projection = new String[]{MetadataSyncState.STATE};
- Cursor c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
- null);
- assertEquals(0, c.getCount());
-
- // Check that the safe stuff lives on.
- assertStoredValue(safeMetadataUri, MetadataSync.RAW_CONTACT_BACKUP_ID, backupId2);
- args = new String[]{safeAccount.name, safeAccount.type};
- c = mResolver.query(MetadataSyncState.CONTENT_URI, projection, selection, args,
- null);
- assertEquals(1, c.getCount());
- c.moveToNext();
- assertEquals("syncState2", c.getString(0));
- c.close();
- }
-
- private String getDefaultMetadataJSONString(
- String accountType, String accountName, String backupId) {
- return "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": " + accountType + ",\n" +
- " \"account_name\": " + accountName + ",\n" +
- " \"contact_id\": " + backupId + ",\n" +
- " \"data_set\": \"FOCUS\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": true,\n" +
- " \"pinned\": 1\n" +
- " }\n" +
- " }";
- }
-
public void testContactDeletion() {
long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe",
TestUtil.ACCOUNT_1);
@@ -6891,35 +6454,15 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
}
public void testDirtyWhenRawContactInsert() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
- // When inserting a rawcontact without metadata.
+ // When inserting a rawcontact.
long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
assertDirty(rawContactUri, false);
assertMetadataDirty(rawContactUri, false);
assertNetworkNotified(true);
- assertMetadataNetworkNotified(false);
-
- // When inserting a rawcontact with metadata.
- ContentValues values = new ContentValues();
- values.put(ContactsContract.RawContacts.STARRED, 1);
- values.put(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name);
- values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type);
- Uri rawContactId2Uri = mResolver.insert(RawContacts.CONTENT_URI, values);
- assertDirty(rawContactId2Uri, false);
- assertMetadataDirty(rawContactId2Uri, false);
- assertNetworkNotified(true);
- assertMetadataNetworkNotified(false);
}
public void testRawContactDirtyAndVersion() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
assertDirty(uri, false);
@@ -6933,9 +6476,8 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertEquals(version, getVersion(uri));
assertDirty(uri, false);
- assertNetworkNotified(false);
assertMetadataDirty(uri, false);
- assertMetadataNetworkNotified(false);
+ assertNetworkNotified(false);
Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
assertDirty(uri, true);
@@ -6992,10 +6534,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
}
public void testNotifyMetadataChangeForRawContactInsertBySyncAdapter() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
Uri uri = RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccount.name)
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccount.type)
@@ -7005,15 +6543,9 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
long rawContactId = ContentUris.parseId(mResolver.insert(uri, new ContentValues()));
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
assertMetadataDirty(rawContactUri, false);
- // Will not notify Lychee since it's removed.
- assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForRawContactMetadataChange() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
long contactId = queryContactId(rawContactId);
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
@@ -7025,7 +6557,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
assertMetadataDirty(rawContactUri, false);
- assertMetadataNetworkNotified(false);
clearMetadataDirty(rawContactUri);
values = new ContentValues();
@@ -7034,7 +6565,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertStoredValue(contactUri, Contacts.PINNED, 1);
assertMetadataDirty(rawContactUri, false);
- assertMetadataNetworkNotified(false);
clearMetadataDirty(rawContactUri);
values = new ContentValues();
@@ -7043,14 +6573,9 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertStoredValue(contactUri, Contacts.SEND_TO_VOICEMAIL, 1);
assertMetadataDirty(rawContactUri, false);
- assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForRawContactBackupIdChange() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
@@ -7066,14 +6591,9 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
mResolver.update(rawContactUri, values, null, null);
assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, "newBackupId");
assertMetadataDirty(rawContactUri, false);
- assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForAggregationExceptionChange() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
long rawContactId2 = RawContactUtil.createRawContact(mResolver, new Account("b", "b"));
@@ -7084,28 +6604,18 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
false);
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
false);
- assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataNotDirtyForUsageStatsChange() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "contact", "a");
final long did1a = ContentUris.parseId(insertEmail(rid1, "email_1_a@email.com"));
updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a);
// Usage feedback no longer works, so "false".
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rid1), false);
- assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForDataPrimarySettingInsert() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
Uri mailUri11 = insertEmail(rawContactId1, "test1@domain1.com", true, true);
@@ -7113,14 +6623,9 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1);
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
false);
- assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForDataPrimarySettingUpdate() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
Uri mailUri1 = insertEmail(rawContactId, "test1@domain1.com");
@@ -7133,14 +6638,9 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
false);
- assertMetadataNetworkNotified(false);
}
public void testMarkAsMetadataDirtyForDataDelete() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
Uri mailUri1 = insertEmail(rawContactId, "test1@domain1.com", true, true);
@@ -7148,7 +6648,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
false);
- assertMetadataNetworkNotified(false);
}
public void testDeleteContactWithoutName() {
@@ -8809,25 +8308,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
}
}
- public void testMarkMetadataNotDirtyWhenDataUsageUpdate() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
- final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "contact", "a");
- final long did1a = ContentUris.parseId(insertEmail(rid1, "email_1_a@email.com"));
- final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rid1);
- assertDirty(rawContactUri, true);
- clearDirty(rawContactUri);
- updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a);
-
- assertDirty(rawContactUri, false);
- // Usage feedback no longer works, so "false".
- assertMetadataDirty(rawContactUri, false);
- assertNetworkNotified(false);
- assertMetadataNetworkNotified(false);
- }
-
public void testDataUsageFeedbackAndDelete() {
sMockClock.install();
@@ -9006,10 +8486,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
}
public void testContactUpdate_metadataChange() {
- // Enable metadataSync flag.
- final ContactsProvider2 cp = (ContactsProvider2) getProvider();
- cp.setMetadataSyncForTest(true);
-
DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, ids.mRawContactId);
assertDirty(rawContactUri, true);
@@ -9022,7 +8498,6 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test {
assertDirty(rawContactUri, false);
assertMetadataDirty(rawContactUri, false);
assertNetworkNotified(false);
- assertMetadataNetworkNotified(false);
}
public void testContactUpdate_updatesContactUpdatedTimestamp() {
diff --git a/tests/src/com/android/providers/contacts/MetadataEntryParserTest.java b/tests/src/com/android/providers/contacts/MetadataEntryParserTest.java
deleted file mode 100644
index fe6ed132..00000000
--- a/tests/src/com/android/providers/contacts/MetadataEntryParserTest.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.contacts;
-
-import android.content.Context;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.providers.contacts.MetadataEntryParser.AggregationData;
-import com.android.providers.contacts.MetadataEntryParser.FieldData;
-import com.android.providers.contacts.MetadataEntryParser.MetadataEntry;
-import com.android.providers.contacts.MetadataEntryParser.RawContactInfo;
-import com.android.providers.contacts.MetadataEntryParser.UsageStats;
-import org.json.JSONException;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-/**
- * Unit tests for {@link MetadataEntryParser}.
- *
- * Run the test like this:
- * <code>
- adb shell am instrument -e class com.android.providers.contacts.MetadataEntryParserTest -w \
- com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
- * </code>
- */
-@SmallTest
-public class MetadataEntryParserTest extends FixedAndroidTestCase {
-
- public void testErrorForEmptyInput() {
- try {
- MetadataEntryParser.parseDataToMetaDataEntry("");
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testParseDataToMetadataEntry() throws IOException {
- String contactBackupId = "1111111";
- String accountType = "facebook";
- String accountName = "android-test";
- String dataSet = null;
- int sendToVoicemail = 1;
- int starred = 0;
- int pinned = 2;
- String dataHashId1 = "1001";
- String usageType1_1 = "CALL";
- long lastTimeUsed1_1 = 10000001;
- int timesUsed1_1 = 10;
- String usageType1_2 = "SHORT_TEXT";
- long lastTimeUsed1_2 = 20000002;
- int timesUsed1_2 = 20;
- String dataHashId2 = "1002";
- String usageType2 = "LONG_TEXT";
- long lastTimeUsed2 = 30000003;
- int timesUsed2 = 30;
- String aggregationContactBackupId1 = "2222222";
- String aggregationAccountType1 = "com.google";
- String aggregationAccountName1 = "android-test2";
- String aggregationDataSet1 = "plus";
- String aggregationContactBackupId2 = "3333333";
- String aggregationAccountType2 = "com.google";
- String aggregationAccountName2 = "android-test3";
- String aggregationDataSet2 = "custom type";
- String type = "TOGETHER";
- String inputFile = "test1/testFileDeviceContactMetadataJSON.txt";
-
- RawContactInfo rawContactInfo = new RawContactInfo(
- contactBackupId, accountType, accountName, dataSet);
- RawContactInfo aggregationContact1 = new RawContactInfo(aggregationContactBackupId1,
- aggregationAccountType1, aggregationAccountName1, aggregationDataSet1);
- RawContactInfo aggregationContact2 = new RawContactInfo(aggregationContactBackupId2,
- aggregationAccountType2, aggregationAccountName2, aggregationDataSet2);
- AggregationData aggregationData = new AggregationData(
- aggregationContact1, aggregationContact2, type);
- ArrayList<AggregationData> aggregationDataList = new ArrayList<>();
- aggregationDataList.add(aggregationData);
-
- UsageStats usageStats1_1 = new UsageStats(usageType1_1, lastTimeUsed1_1, timesUsed1_1);
- UsageStats usageStats1_2 = new UsageStats(usageType1_2, lastTimeUsed1_2, timesUsed1_2);
- UsageStats usageStats2 = new UsageStats(usageType2, lastTimeUsed2, timesUsed2);
-
- ArrayList<UsageStats> usageStats1List = new ArrayList<>();
- usageStats1List.add(usageStats1_1);
- usageStats1List.add(usageStats1_2);
- FieldData fieldData1 = new FieldData(dataHashId1, true, true, usageStats1List);
-
- ArrayList<UsageStats> usageStats2List = new ArrayList<>();
- usageStats2List.add(usageStats2);
- FieldData fieldData2 = new FieldData(dataHashId2, false, false, usageStats2List);
-
- ArrayList<FieldData> fieldDataList = new ArrayList<>();
- fieldDataList.add(fieldData1);
- fieldDataList.add(fieldData2);
-
- MetadataEntry expectedResult = new MetadataEntry(rawContactInfo,
- sendToVoicemail, starred, pinned, fieldDataList, aggregationDataList);
-
- String inputJson = readAssetAsString(inputFile);
- MetadataEntry metadataEntry = MetadataEntryParser.parseDataToMetaDataEntry(
- inputJson.toString());
- assertMetaDataEntry(expectedResult, metadataEntry);
- }
-
- public void testErrorForMissingContactId() {
- String input = "{\"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": \"facebook\",\n" +
- " \"account_name\": \"android-test\"\n" +
- " }}";
- try {
- MetadataEntryParser.parseDataToMetaDataEntry(input);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testErrorForNullContactId() throws JSONException {
- String input = "{\"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": \"facebook\",\n" +
- " \"account_name\": \"android-test\",\n" +
- " \"contact_id\": \"\"\n" +
- " }}";
- try {
- MetadataEntryParser.parseDataToMetaDataEntry(input);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testErrorForNullAccountType() throws JSONException {
- String input = "{\"unique_contact_id\": {\n" +
- " \"account_type\": \"\",\n" +
- " \"custom_account_type\": \"facebook\",\n" +
- " \"account_name\": \"android-test\",\n" +
- " \"contact_id\": \"\"\n" +
- " }}";
- try {
- MetadataEntryParser.parseDataToMetaDataEntry(input);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testErrorForNullAccountName() throws JSONException {
- String input = "{\"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": \"facebook\",\n" +
- " \"account_name\": \"\",\n" +
- " \"contact_id\": \"1111111\"\n" +
- " }}";
- try {
- MetadataEntryParser.parseDataToMetaDataEntry(input);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testErrorForNullFieldDataId() throws JSONException {
- String input = "{\"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": \"facebook\",\n" +
- " \"account_name\": \"android-test\",\n" +
- " \"contact_id\": \"1111111\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": false,\n" +
- " \"pinned\": 2\n" +
- " }," +
- " \"field_data\": [{\n" +
- " \"field_data_id\": \"\"}]" +
- "}";
- try {
- MetadataEntryParser.parseDataToMetaDataEntry(input);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- public void testErrorForNullAggregationType() throws JSONException {
- String input = "{\n" +
- " \"unique_contact_id\": {\n" +
- " \"account_type\": \"CUSTOM_ACCOUNT\",\n" +
- " \"custom_account_type\": \"facebook\",\n" +
- " \"account_name\": \"android-test\",\n" +
- " \"contact_id\": \"1111111\"\n" +
- " },\n" +
- " \"contact_prefs\": {\n" +
- " \"send_to_voicemail\": true,\n" +
- " \"starred\": false,\n" +
- " \"pinned\": 2\n" +
- " },\n" +
- " \"aggregation_data\": [\n" +
- " {\n" +
- " \"type\": \"\",\n" +
- " \"contact_ids\": [\n" +
- " {\n" +
- " \"contact_id\": \"2222222\"\n" +
- " },\n" +
- " {\n" +
- " \"contact_id\": \"3333333\"\n" +
- " }\n" +
- " ]\n" +
- " }\n" +
- " ]}";
- try {
- MetadataEntryParser.parseDataToMetaDataEntry(input);
- } catch (IllegalArgumentException e) {
- // Expected.
- }
- }
-
- private String readAssetAsString(String fileName) throws IOException {
- Context context = getTestContext();
- InputStream input = context.getAssets().open(fileName);
- ByteArrayOutputStream contents = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[1024];
- do {
- len = input.read(data);
- if (len > 0) contents.write(data, 0, len);
- } while (len == data.length);
- return contents.toString();
- }
-
- private void assertMetaDataEntry(MetadataEntry entry1, MetadataEntry entry2) {
- assertRawContactInfoEquals(entry1.mRawContactInfo, entry2.mRawContactInfo);
- assertEquals(entry1.mSendToVoicemail, entry2.mSendToVoicemail);
- assertEquals(entry1.mStarred, entry2.mStarred);
- assertEquals(entry1.mPinned, entry2.mPinned);
- assertAggregationDataListEquals(entry1.mAggregationDatas, entry2.mAggregationDatas);
- assertFieldDataListEquals(entry1.mFieldDatas, entry2.mFieldDatas);
- }
-
- private void assertRawContactInfoEquals(RawContactInfo contact1, RawContactInfo contact2) {
- assertEquals(contact1.mBackupId, contact2.mBackupId);
- assertEquals(contact1.mAccountType, contact2.mAccountType);
- assertEquals(contact1.mAccountName, contact2.mAccountName);
- assertEquals(contact1.mDataSet, contact2.mDataSet);
- }
-
- private void assertAggregationDataListEquals(ArrayList<AggregationData> aggregationList1,
- ArrayList<AggregationData> aggregationList2) {
- assertEquals(aggregationList1.size(), aggregationList2.size());
- for (int i = 0; i < aggregationList1.size(); i++) {
- assertAggregationDataEquals(aggregationList1.get(i), aggregationList2.get(i));
- }
- }
-
- private void assertAggregationDataEquals(AggregationData aggregationData1,
- AggregationData aggregationData2) {
- assertRawContactInfoEquals(aggregationData1.mRawContactInfo1,
- aggregationData2.mRawContactInfo1);
- assertRawContactInfoEquals(aggregationData1.mRawContactInfo2,
- aggregationData2.mRawContactInfo2);
- assertEquals(aggregationData1.mType, aggregationData2.mType);
- }
-
- private void assertFieldDataListEquals(ArrayList<FieldData> fieldDataList1,
- ArrayList<FieldData> fieldDataList2) {
- assertEquals(fieldDataList1.size(), fieldDataList2.size());
- for (int i = 0; i < fieldDataList1.size(); i++) {
- assertFieldDataEquals(fieldDataList1.get(i), fieldDataList2.get(i));
- }
- }
-
- private void assertFieldDataEquals(FieldData fieldData1, FieldData fieldData2) {
- assertEquals(fieldData1.mDataHashId, fieldData2.mDataHashId);
- assertEquals(fieldData1.mIsPrimary, fieldData2.mIsPrimary);
- assertEquals(fieldData1.mIsSuperPrimary, fieldData2.mIsSuperPrimary);
- assertUsageStatsListEquals(fieldData1.mUsageStatsList, fieldData2.mUsageStatsList);
- }
-
- private void assertUsageStatsListEquals(ArrayList<UsageStats> usageStatsList1,
- ArrayList<UsageStats> usageStatsList2) {
- assertEquals(usageStatsList1.size(), usageStatsList2.size());
- for (int i = 0; i < usageStatsList1.size(); i++) {
- assertUsageStatsEquals(usageStatsList1.get(i), usageStatsList2.get(i));
- }
- }
-
- private void assertUsageStatsEquals(UsageStats usageStats1, UsageStats usageStats2) {
- assertEquals(usageStats1.mUsageType, usageStats2.mUsageType);
- assertEquals(usageStats1.mLastTimeUsed, usageStats2.mLastTimeUsed);
- assertEquals(usageStats1.mTimesUsed, usageStats2.mTimesUsed);
- }
-}
diff --git a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
index f674dd5b..c2ab74fc 100644
--- a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
+++ b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java
@@ -36,7 +36,6 @@ public class SynchronousContactsProvider2 extends ContactsProvider2 {
private static ContactsDatabaseHelper sDbHelper;
private Account mAccount;
private boolean mNetworkNotified;
- private boolean mMetadataNetworkNotified;
private boolean mIsPhone = true;
private boolean mIsVoiceCapable = true;
@@ -62,23 +61,17 @@ public class SynchronousContactsProvider2 extends ContactsProvider2 {
public void onBegin() {
super.onBegin();
mNetworkNotified = false;
- mMetadataNetworkNotified = false;
}
@Override
- protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetwork) {
+ protected void notifyChange(boolean syncToNetwork) {
mNetworkNotified |= syncToNetwork;
- mMetadataNetworkNotified |= syncToMetadataNetwork;
}
public boolean isNetworkNotified() {
return mNetworkNotified;
}
- public boolean isMetadataNetworkNotified() {
- return mMetadataNetworkNotified;
- }
-
public void setIsPhone(boolean flag) {
mIsPhone = flag;
}