Merge "Provider side changes for exposing data usage stats" into jb-mr2-dev
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 9e52f54..cf9068a 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -210,6 +210,8 @@
*/
private static final int DEFAULT_PREAUTHORIZED_URI_EXPIRATION = 5 * 60 * 1000;
+ private static final int USAGE_TYPE_ALL = -1;
+
/**
* Random URI parameter that will be appended to preauthorized URIs for uniqueness.
*/
@@ -671,6 +673,11 @@
.add(Data.STATUS_ICON, StatusUpdatesColumns.CONCRETE_STATUS_ICON)
.build();
+ private static final ProjectionMap sDataUsageColumns = ProjectionMap.builder()
+ .add(Data.TIMES_USED, Tables.DATA_USAGE_STAT + "." + Data.TIMES_USED)
+ .add(Data.LAST_TIME_USED, Tables.DATA_USAGE_STAT + "." + Data.LAST_TIME_USED)
+ .build();
+
/** Contains just BaseColumns._COUNT */
private static final ProjectionMap sCountProjectionMap = ProjectionMap.builder()
.add(BaseColumns._COUNT, "COUNT(*)")
@@ -824,6 +831,7 @@
.addAll(sRawContactColumns)
.addAll(sContactsColumns)
.addAll(sContactPresenceColumns)
+ .addAll(sDataUsageColumns)
.build();
/** Contains columns from the data view used for SIP address lookup. */
@@ -841,6 +849,7 @@
.addAll(sDataPresenceColumns)
.addAll(sContactsColumns)
.addAll(sContactPresenceColumns)
+ .addAll(sDataUsageColumns)
.build();
/** Contains columns from the data view used for SIP address lookup. */
@@ -5913,7 +5922,9 @@
case DATA:
case PROFILE_DATA: {
- setTablesAndProjectionMapForData(qb, uri, projection, false);
+ final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
+ final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL);
+ setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt);
if (uri.getBooleanQueryParameter(Data.VISIBLE_CONTACTS_ONLY, false)) {
qb.appendWhere(" AND " + Data.CONTACT_ID + " in " +
Tables.DEFAULT_DIRECTORY);
@@ -7009,9 +7020,8 @@
appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
- if (usageType != null) {
- appendDataUsageStatJoin(sb, usageType, DataColumns.CONCRETE_ID);
- }
+ appendDataUsageStatJoin(sb, usageType == null ? USAGE_TYPE_ALL : usageType,
+ DataColumns.CONCRETE_ID);
qb.setTables(sb.toString());
@@ -7108,9 +7118,29 @@
}
private void appendDataUsageStatJoin(StringBuilder sb, int usageType, String dataIdColumn) {
- sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
- " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=" + dataIdColumn +
- " AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=" + usageType + ")");
+ if (usageType != USAGE_TYPE_ALL) {
+ sb.append(" LEFT OUTER JOIN " + Tables.DATA_USAGE_STAT +
+ " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=");
+ sb.append(dataIdColumn);
+ sb.append(" AND " + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "=");
+ sb.append(usageType);
+ sb.append(")");
+ } else {
+ sb.append(
+ " LEFT OUTER JOIN " +
+ "(SELECT " +
+ DataUsageStatColumns.CONCRETE_DATA_ID + ", " +
+ "SUM(" + DataUsageStatColumns.CONCRETE_TIMES_USED +
+ ") as " + DataUsageStatColumns.TIMES_USED + ", " +
+ "MAX(" + DataUsageStatColumns.CONCRETE_LAST_TIME_USED +
+ ") as " + DataUsageStatColumns.LAST_TIME_USED +
+ " FROM " + Tables.DATA_USAGE_STAT + " GROUP BY " +
+ DataUsageStatColumns.DATA_ID + ") as " + Tables.DATA_USAGE_STAT
+ );
+ sb.append(" ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "=");
+ sb.append(dataIdColumn);
+ sb.append(")");
+ }
}
private void appendContactPresenceJoin(StringBuilder sb, String[] projection,
diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
index 384d547..8e763f1 100644
--- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java
@@ -17,6 +17,7 @@
package com.android.providers.contacts;
import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
+import static com.android.providers.contacts.TestUtils.cv;
import android.accounts.Account;
import android.content.ContentProvider;
@@ -1112,6 +1113,22 @@
assertTrue(message.toString(), result);
}
+ protected void assertCursorContains(Cursor cursor, ContentValues expectedValues) {
+ final StringBuilder message = new StringBuilder();
+ boolean found = false;
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ message.setLength(0);
+ final int pos = cursor.getPosition();
+ found = equalsWithExpectedValues(cursor, expectedValues, message);
+ if (found) {
+ break;
+ }
+ }
+ assertTrue("Expected values can not be found " + expectedValues + "," + message.toString(),
+ found);
+ }
+
protected void assertCursorValues(Cursor cursor, ContentValues... expectedValues) {
StringBuilder message = new StringBuilder();
@@ -1179,6 +1196,25 @@
return true;
}
+ private static final String[] DATA_USAGE_PROJECTION =
+ new String[] {Data.DATA1, Data.TIMES_USED, Data.LAST_TIME_USED};
+
+ protected void assertDataUsageCursorContains(Uri uri, String data1, int timesUsed,
+ int lastTimeUsed) {
+ final Cursor cursor = mResolver.query(uri, DATA_USAGE_PROJECTION, null, null,
+ null);
+ try {
+ assertCursorContains(cursor,
+ cv(
+ Data.DATA1, data1,
+ Data.TIMES_USED, timesUsed,
+ Data.LAST_TIME_USED, lastTimeUsed)
+ );
+ } finally {
+ cursor.close();
+ }
+ }
+
private String[] buildProjection(ContentValues values) {
String[] projection = new String[values.size()];
Iterator<Entry<String, Object>> iter = values.valueSet().iterator();
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index 84404b2..3f8b001 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -32,7 +32,7 @@
import android.provider.ContactsContract;
import android.provider.ContactsContract.AggregationExceptions;
import android.provider.ContactsContract.CommonDataKinds.Callable;
-import android.provider.ContactsContract.CommonDataKinds.Contactables;;
+import android.provider.ContactsContract.CommonDataKinds.Contactables;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Im;
@@ -343,6 +343,8 @@
Data.STATUS_RES_PACKAGE,
Data.STATUS_LABEL,
Data.STATUS_ICON,
+ Data.TIMES_USED,
+ Data.LAST_TIME_USED,
RawContacts.ACCOUNT_NAME,
RawContacts.ACCOUNT_TYPE,
RawContacts.DATA_SET,
@@ -424,6 +426,8 @@
Data.STATUS_RES_PACKAGE,
Data.STATUS_LABEL,
Data.STATUS_ICON,
+ Data.TIMES_USED,
+ Data.LAST_TIME_USED,
RawContacts.RAW_CONTACT_IS_USER_PROFILE,
Contacts._ID,
Contacts.DISPLAY_NAME_PRIMARY,
@@ -2514,6 +2518,48 @@
// Now we have only 1 frequent.
assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values1});
+
+ }
+
+ public void testQueryDataUsageStat() {
+ ContentValues values1 = new ContentValues();
+ final String email1 = "a@acme.com";
+ final long cid1 = createContact(values1, "Noah", "Tever", "18004664411",
+ email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
+
+ sMockClock.install();
+ sMockClock.setCurrentTimeMillis(100);
+
+ sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
+
+ assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 1, 100);
+
+ sMockClock.setCurrentTimeMillis(111);
+ sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
+
+ assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 2, 111);
+
+ sMockClock.setCurrentTimeMillis(123);
+ sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1);
+
+ assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 3, 123);
+
+ final Uri dataUriWithUsageTypeLongText = Data.CONTENT_URI.buildUpon().appendQueryParameter(
+ DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_LONG_TEXT).build();
+
+ assertDataUsageCursorContains(dataUriWithUsageTypeLongText, "a@acme.com", 2, 111);
+
+ sMockClock.setCurrentTimeMillis(200);
+ sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
+ sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
+ sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
+
+ assertDataUsageCursorContains(Data.CONTENT_URI, "a@acme.com", 6, 200);
+
+ final Uri dataUriWithUsageTypeCall = Data.CONTENT_URI.buildUpon().appendQueryParameter(
+ DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_CALL).build();
+
+ assertDataUsageCursorContains(dataUriWithUsageTypeCall, "a@acme.com", 3, 200);
}
public void testQueryContactGroup() {