Put some useful information in User-Agent Header

Put information in User-Agent:
* Build fingerprint
* Calendar or CalendarSyncAdapter package name + version code
* Package name of app making changes to provider for uphill syncs

Example:
google/mantaray/manta:K/MASTER/eng.aalbert.20121217.140040:eng/dev-keys:com.google.android.calendar:201212060:com.google.android.calendar Google-HTTP-Java-Client/1.11.0-beta (gzip)

Change-Id: I729c64ba9b922523d35903fcaf2a51cc234799a8
diff --git a/src/com/android/providers/calendar/CalendarDatabaseHelper.java b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
index 7db2594..dc3deba 100644
--- a/src/com/android/providers/calendar/CalendarDatabaseHelper.java
+++ b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
@@ -73,7 +73,7 @@
     // 5xx for JB MR1
     // 6xx for K
     // Bump this to the next hundred at each major release.
-    static final int DATABASE_VERSION = 502;
+    static final int DATABASE_VERSION = 600;
 
     private static final int PRE_FROYO_SYNC_STATE_VERSION = 3;
 
@@ -522,6 +522,7 @@
                 CalendarContract.Events._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
                 CalendarContract.Events._SYNC_ID + " TEXT," +
                 CalendarContract.Events.DIRTY + " INTEGER," +
+                CalendarContract.Events.MUTATORS + " TEXT," +
                 CalendarContract.Events.LAST_SYNCED + " INTEGER DEFAULT 0," +
                 CalendarContract.Events.CALENDAR_ID + " INTEGER NOT NULL," +
                 CalendarContract.Events.TITLE + " TEXT," +
@@ -767,6 +768,7 @@
                 Calendars.ACCOUNT_TYPE + " TEXT," +
                 Calendars._SYNC_ID + " TEXT," +
                 Calendars.DIRTY + " INTEGER," +
+                Calendars.MUTATORS + " TEXT," +
                 Calendars.NAME + " TEXT," +
                 Calendars.CALENDAR_DISPLAY_NAME + " TEXT," +
                 Calendars.CALENDAR_COLOR + " INTEGER," +
@@ -1404,6 +1406,11 @@
                 createEventsView = true; // This is needed if the calendars or events schema changed
                 oldVersion = 502;
             }
+            if (oldVersion < 600) {
+                upgradeToVersion600(db);
+                createEventsView = true; // This is needed if the calendars or events schema changed
+                oldVersion = 600;
+            }
 
             if (createEventsView) {
                 createEventsView(db);
@@ -1480,6 +1487,15 @@
     /* 6xx db version is for K release
     /**********************************************************/
 
+    private void upgradeToVersion600(SQLiteDatabase db) {
+        /*
+         * Changes from version 5xx to 600:
+         * - add mutator columns to Events & calendars
+         */
+        db.execSQL("ALTER TABLE Events ADD COLUMN mutators TEXT;");
+        db.execSQL("ALTER TABLE Calendars ADD COLUMN mutators TEXT;");
+    }
+
     /**********************************************************/
     /* 5xx db version is for JB MR1 release
     /**********************************************************/
@@ -3198,6 +3214,8 @@
                 + " AS " + CalendarContract.Events._SYNC_ID + ","
                 + Tables.EVENTS + "." + CalendarContract.Events.DIRTY
                 + " AS " + CalendarContract.Events.DIRTY + ","
+                + Tables.EVENTS + "." + Events.MUTATORS
+                + " AS " + Events.MUTATORS + ","
                 + CalendarContract.Events.LAST_SYNCED + ","
                 + Tables.CALENDARS + "." + Calendars.ACCOUNT_NAME
                 + " AS " + CalendarContract.Events.ACCOUNT_NAME + ","
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index d2027ee..35653e9 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -28,12 +28,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.UriMatcher;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Process;
@@ -195,9 +197,14 @@
             " WHERE " +
             CalendarContract.EventsRawTimes.EVENT_ID + " = " + Tables.EVENTS + "." + Events._ID;
 
-    private static final String SQL_UPDATE_EVENT_SET_DIRTY = "UPDATE " +
-            Tables.EVENTS +
-            " SET " + Events.DIRTY + "=1" +
+    private static final String SQL_UPDATE_EVENT_SET_DIRTY_AND_MUTATORS = "UPDATE " +
+            Tables.EVENTS + " SET " +
+            Events.DIRTY + "=1," +
+            Events.MUTATORS + "=? " +
+            " WHERE " + Events._ID + "=?";
+
+    private static final String SQL_QUERY_EVENT_MUTATORS = "SELECT " + Events.MUTATORS +
+            " FROM " + Tables.EVENTS +
             " WHERE " + Events._ID + "=?";
 
     private static final String SQL_WHERE_CALENDAR_COLOR = Calendars.ACCOUNT_NAME + "=? AND "
@@ -1712,6 +1719,7 @@
         // If this isn't the sync adapter, set the "dirty" flag in any Event we modify.
         if (!callerIsSyncAdapter) {
             modValues.put(Events.DIRTY, true);
+            addMutator(modValues, Events.MUTATORS);
         }
 
         // Wrap all database accesses in a transaction.
@@ -2088,6 +2096,7 @@
             case EVENTS:
                 if (!callerIsSyncAdapter) {
                     values.put(Events.DIRTY, 1);
+                    addMutator(values, Events.MUTATORS);
                 }
                 if (!values.containsKey(Events.DTSTART)) {
                     if (values.containsKey(Events.ORIGINAL_SYNC_ID)
@@ -2561,7 +2570,31 @@
     }
 
     private void setEventDirty(long eventId) {
-        mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY, new Object[] {eventId});
+        final String mutators = DatabaseUtils.stringForQuery(
+                mDb,
+                SQL_QUERY_EVENT_MUTATORS,
+                new String[]{String.valueOf(eventId)});
+        final String packageName = getCallingPackageName();
+        final String newMutators;
+        if (TextUtils.isEmpty(mutators)) {
+            newMutators = packageName;
+        } else  {
+            final String[] strings = mutators.split(",");
+            boolean found = false;
+            for (String string : strings) {
+                if (string.equals(packageName)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                newMutators = mutators + "," + packageName;
+            } else {
+                newMutators = mutators;
+            }
+        }
+        mDb.execSQL(SQL_UPDATE_EVENT_SET_DIRTY_AND_MUTATORS,
+                new Object[] {newMutators, eventId});
     }
 
     private long getOriginalId(String originalSyncId, String calendarId) {
@@ -3199,6 +3232,7 @@
                     ContentValues values = new ContentValues();
                     values.put(Events.DELETED, 1);
                     values.put(Events.DIRTY, 1);
+                    addMutator(values, Events.MUTATORS);
                     mDb.update(Tables.EVENTS, values, SQL_WHERE_ID, selectionArgs);
 
                     // Exceptions that have been synced shouldn't be deleted -- the sync
@@ -3257,6 +3291,7 @@
 
         ContentValues dirtyValues = new ContentValues();
         dirtyValues.put(Events.DIRTY, "1");
+        addMutator(dirtyValues, Events.MUTATORS);
 
         /*
          * Re-issue the delete URI as a query.  Note that, if this is a by-ID request, the ID
@@ -3340,6 +3375,7 @@
         if (!callerIsSyncAdapter) {
             ContentValues dirtyValues = new ContentValues();
             dirtyValues.put(Events.DIRTY, "1");
+            addMutator(dirtyValues, Events.MUTATORS);
 
             Iterator<Long> iter = eventIdSet.iterator();
             while (iter.hasNext()) {
@@ -3448,6 +3484,7 @@
             if (!callerIsSyncAdapter) {
                 dirtyValues = new ContentValues();
                 dirtyValues.put(Events.DIRTY, "1");
+                addMutator(dirtyValues, Events.MUTATORS);
             }
 
             final int idIndex = c.getColumnIndex(GENERIC_ID);
@@ -3774,6 +3811,7 @@
 
             if (!callerIsSyncAdapter) {
                 modValues.put(Events.DIRTY, 1);
+                addMutator(modValues, Events.MUTATORS);
             }
 
             // Disallow updating the attendee status in the Events
@@ -3808,6 +3846,7 @@
                 } else {
                     if (modValues.containsKey(Events.DIRTY)
                             && modValues.getAsInteger(Events.DIRTY) == 0) {
+                        modValues.put(Events.MUTATORS, (String) null);
                         mDbHelper.removeDuplicateEvent(id);
                     }
                 }
@@ -3919,6 +3958,12 @@
                 }
                 if (!callerIsSyncAdapter) {
                     values.put(Calendars.DIRTY, 1);
+                    addMutator(values, Calendars.MUTATORS);
+                } else {
+                    if (values.containsKey(Calendars.DIRTY)
+                            && values.getAsInteger(Calendars.DIRTY) == 0) {
+                        values.put(Calendars.MUTATORS, (String) null);
+                    }
                 }
                 Integer syncEvents = values.getAsInteger(Calendars.SYNC_EVENTS);
                 if (syncEvents != null) {
@@ -4624,6 +4669,7 @@
         sCalendarsProjectionMap.put(Calendars.ACCOUNT_TYPE, Calendars.ACCOUNT_TYPE);
         sCalendarsProjectionMap.put(Calendars._SYNC_ID, Calendars._SYNC_ID);
         sCalendarsProjectionMap.put(Calendars.DIRTY, Calendars.DIRTY);
+        sCalendarsProjectionMap.put(Calendars.MUTATORS, Calendars.MUTATORS);
         sCalendarsProjectionMap.put(Calendars.NAME, Calendars.NAME);
         sCalendarsProjectionMap.put(
                 Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME);
@@ -4752,6 +4798,7 @@
         sEventsProjectionMap.put(Calendars.CAL_SYNC9, Calendars.CAL_SYNC9);
         sEventsProjectionMap.put(Calendars.CAL_SYNC10, Calendars.CAL_SYNC10);
         sEventsProjectionMap.put(Events.DIRTY, Events.DIRTY);
+        sEventsProjectionMap.put(Events.MUTATORS, Events.MUTATORS);
         sEventsProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED);
 
         sEventEntitiesProjectionMap = new HashMap<String, String>();
@@ -4807,6 +4854,7 @@
         sEventEntitiesProjectionMap.put(Events.SYNC_DATA9, Events.SYNC_DATA9);
         sEventEntitiesProjectionMap.put(Events.SYNC_DATA10, Events.SYNC_DATA10);
         sEventEntitiesProjectionMap.put(Events.DIRTY, Events.DIRTY);
+        sEventEntitiesProjectionMap.put(Events.MUTATORS, Events.MUTATORS);
         sEventEntitiesProjectionMap.put(Events.LAST_SYNCED, Events.LAST_SYNCED);
         sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC1, Calendars.CAL_SYNC1);
         sEventEntitiesProjectionMap.put(Calendars.CAL_SYNC2, Calendars.CAL_SYNC2);
@@ -4992,4 +5040,28 @@
             return newSelectionArgs;
         }
     }
+
+    private String getCallingPackageName() {
+        final PackageManager pm = getContext().getPackageManager();
+        final int uid = Binder.getCallingUid();
+        final String[] packages = pm.getPackagesForUid(uid);
+        if (packages != null && packages.length == 1) {
+            return packages[0];
+        }
+        final String name = pm.getNameForUid(uid);
+        if (name != null) {
+            return name;
+        }
+        return String.valueOf(uid);
+    }
+
+    private void addMutator(ContentValues values, String columnName) {
+        final String packageName = getCallingPackageName();
+        final String mutators = values.getAsString(columnName);
+        if (TextUtils.isEmpty(mutators)) {
+            values.put(columnName, packageName);
+        } else {
+            values.put(columnName, mutators + "," + packageName);
+        }
+    }
 }