Merge tag 'android-11.0.0_r48' into int/11/fp3

Android 11.0.0 Release 48 (RD2A.211001.002)

* tag 'android-11.0.0_r48':

Change-Id: I6e38400b92008149f6db6a1ba459d5f647f3deb5
diff --git a/java/com/android/vcard/VCardComposer.java b/java/com/android/vcard/VCardComposer.java
index 7b59c7d..e9c230e 100644
--- a/java/com/android/vcard/VCardComposer.java
+++ b/java/com/android/vcard/VCardComposer.java
@@ -139,6 +139,8 @@
     private boolean mFirstVCardEmittedInDoCoMoCase;
 
     private Cursor mCursor;
+    private EntityIterator mEntityIterator;
+    private Entity mEntity;
     private boolean mCursorSuppliedFromOutside;
     private int mIdColumn;
     private Uri mContentUriForRawContactsEntity;
@@ -366,6 +368,8 @@
         if (!initInterMainPart()) {
             return false;
         }
+        initEntityIterator();
+
         return initInterLastPart();
     }
 
@@ -439,6 +443,22 @@
         return mIdColumn >= 0;
     }
 
+    private void initEntityIterator() {
+        StringBuilder selection = new StringBuilder();
+        selection.append(Data.CONTACT_ID).append(" IN (");
+        do {
+            selection.append(mCursor.getString(mIdColumn));
+            if (!mCursor.isLast()) {
+                selection.append(",");
+            }
+        } while (mCursor.moveToNext());
+        selection.append(")");
+        mEntityIterator = RawContacts
+                .newEntityIterator(mContentResolver.query(mContentUriForRawContactsEntity, null,
+                        selection.toString(), null, Data.CONTACT_ID));
+        mCursor.moveToFirst();
+    }
+
     private boolean initInterLastPart() {
         mInitDone = true;
         mTerminateCalled = false;
@@ -465,8 +485,9 @@
             // return createOneEntryInternal("-1", getEntityIteratorMethod);
         }
 
-        final String vcard = createOneEntryInternal(mCursor.getLong(mIdColumn),
-                getEntityIteratorMethod);
+        final String vcard = (mEntityIterator == null || getEntityIteratorMethod != null)
+                ?createOneEntryInternal(mCursor.getLong(mIdColumn),getEntityIteratorMethod)
+                        :createOneEntryInternalByIterator();
         if (!mCursor.moveToNext()) {
             Log.e(LOG_TAG, "Cursor#moveToNext() returned false");
         }
@@ -498,6 +519,57 @@
         RawContactEntitlesInfo getRawContactEntitlesInfo(long contactId);
     }
 
+    private String createOneEntryInternalByIterator() {
+        if (!mEntityIterator.hasNext()) {
+            if (mEntity == null) {
+                Log.e(LOG_TAG, "EntityIterator#hasNext() returned false");
+                return null;
+            }
+        }
+        final Map<String, List<ContentValues>> contentValuesListMap = new HashMap<String, List<ContentValues>>();
+        int lastContactId = -1;
+        if (mEntity != null) {
+            lastContactId = mEntity.getEntityValues().getAsInteger(Data.CONTACT_ID);
+            for (NamedContentValues namedContentValues : mEntity.getSubValues()) {
+                ContentValues contentValues = namedContentValues.values;
+                String key = contentValues.getAsString(Data.MIMETYPE);
+                if (key != null) {
+                    List<ContentValues> contentValuesList = contentValuesListMap.get(key);
+                    if (contentValuesList == null) {
+                        contentValuesList = new ArrayList<ContentValues>();
+                        contentValuesListMap.put(key, contentValuesList);
+                    }
+                    contentValuesList.add(contentValues);
+                }
+            }
+            mEntity = null;
+        }
+        while (mEntityIterator.hasNext()) {
+            Entity entity = mEntityIterator.next();
+            int contactId = entity.getEntityValues().getAsInteger(Data.CONTACT_ID);
+            if ((lastContactId == -1 || contactId == lastContactId)) {
+                for (NamedContentValues namedContentValues : entity.getSubValues()) {
+                    ContentValues contentValues = namedContentValues.values;
+                    String key = contentValues.getAsString(Data.MIMETYPE);
+                    if (key != null) {
+                        List<ContentValues> contentValuesList = contentValuesListMap.get(key);
+                        if (contentValuesList == null) {
+                            contentValuesList = new ArrayList<ContentValues>();
+                            contentValuesListMap.put(key, contentValuesList);
+                        }
+                        contentValuesList.add(contentValues);
+                    }
+                }
+                lastContactId = contactId;
+            } else {
+                lastContactId = contactId;
+                mEntity = entity;
+                break;
+            }
+        }
+        return buildVCard(contentValuesListMap);
+    }
+
     private String createOneEntryInternal(long contactId,
             final Method getEntityIteratorMethod) {
         final Map<String, List<ContentValues>> contentValuesListMap =
@@ -633,6 +705,14 @@
             }
             mCursor = null;
         }
+        if(mEntityIterator != null){
+            try{
+                mEntityIterator.close();
+            } catch (SQLiteException e) {
+                Log.e(LOG_TAG, "SQLiteException on EntityIterator#close(): " + e.getMessage());
+            }
+            mEntityIterator = null;
+        }
     }
 
     @Override
@@ -668,6 +748,9 @@
             Log.w(LOG_TAG, "This object is not ready yet.");
             return false;
         }
+        if(mEntityIterator != null){
+            return ((!mEntityIterator.hasNext()) && mEntity == null) || mCursor.isAfterLast();
+        }
         return mCursor.isAfterLast();
     }
 
diff --git a/java/com/android/vcard/VCardEntryCommitter.java b/java/com/android/vcard/VCardEntryCommitter.java
index 7f8e885..dbb5d91 100644
--- a/java/com/android/vcard/VCardEntryCommitter.java
+++ b/java/com/android/vcard/VCardEntryCommitter.java
@@ -70,7 +70,15 @@
         final long start = System.currentTimeMillis();
         mOperationList = vcardEntry.constructInsertOperations(mContentResolver, mOperationList);
         mCounter++;
-        if (mCounter >= 20) {
+        //because the max batch size is 500 defined in ContactsProvider,so we can enlarge this batch
+        //size to reduce db open/close times. From testing results, we can see performance better
+        //when batch size is more bigger.And also each vcardEntry may have some operation records.
+        //So we set threshold as 450, batch operations will be executed when threshold reached.
+        //Testing result.
+        //batch size                  : 100    200    300    400    450    490    20(mCounter)
+        //consume time(10000 contacts): 178s   143s   127s   124s   119s   117s   195s
+        //consume time (1000 contacts): 17.3s  13.9s  12.6s  12.2s  11.8s  11.6s  19.8s
+        if (mOperationList != null && mOperationList.size() >= 450) {
             mCreatedUris.add(pushIntoContentResolver(mOperationList));
             mCounter = 0;
             mOperationList = null;
@@ -104,4 +112,4 @@
    public ArrayList<Uri> getCreatedUris() {
         return mCreatedUris;
     }
-}
\ No newline at end of file
+}