Only send DOWNLOAD_COMPLETE broadcast once.

Apps might end up confused if we tell them a download was completed
multiple times, so only send the broadcast exactly once when we
transition it into a "completed" state, either during an update() or
a delete() operation.

Test: verified single broadcast with test app
Bug: 31619480
Change-Id: I0b9139ea0e37f6d212b84314048692cd0c4f9cdf
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index b2b2c08..7297558 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -1077,10 +1077,14 @@
 
         Helpers.validateSelection(where, sAppReadableColumnsSet);
 
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        final Context context = getContext();
+        final ContentResolver resolver = context.getContentResolver();
+
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
 
         int count;
         boolean updateSchedule = false;
+        boolean isCompleting = false;
 
         ContentValues filteredValues;
         if (Binder.getCallingPid() != Process.myPid()) {
@@ -1121,6 +1125,7 @@
             if (isRestart || isUserBypassingSizeLimit) {
                 updateSchedule = true;
             }
+            isCompleting = status != null && Downloads.Impl.isStatusCompleted(status);
         }
 
         int match = sURIMatcher.match(uri);
@@ -1137,14 +1142,20 @@
                 final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
                 count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
                         selection.getParameters());
-                if (updateSchedule) {
+                if (updateSchedule || isCompleting) {
                     final long token = Binder.clearCallingIdentity();
-                    try {
-                        try (Cursor cursor = db.query(DB_TABLE, new String[] { _ID },
-                                selection.getSelection(), selection.getParameters(),
-                                null, null, null)) {
-                            while (cursor.moveToNext()) {
-                                Helpers.scheduleJob(getContext(), cursor.getInt(0));
+                    try (Cursor cursor = db.query(DB_TABLE, null, selection.getSelection(),
+                            selection.getParameters(), null, null, null)) {
+                        final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver,
+                                cursor);
+                        final DownloadInfo info = new DownloadInfo(context);
+                        while (cursor.moveToNext()) {
+                            reader.updateFromDatabase(info);
+                            if (updateSchedule) {
+                                Helpers.scheduleJob(context, info);
+                            }
+                            if (isCompleting) {
+                                info.sendIntentIfRequested();
                             }
                         }
                     } finally {
@@ -1257,8 +1268,12 @@
                             }
                         }
 
-                        // Tell requester that download is finished
-                        info.sendIntentIfRequested();
+                        // If the download wasn't completed yet, we're
+                        // effectively completing it now, and we need to send
+                        // any requested broadcasts
+                        if (!Downloads.Impl.isStatusCompleted(info.mStatus)) {
+                            info.sendIntentIfRequested();
+                        }
                     }
                 }
 
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index 33436f5..c6b4c71 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -382,11 +382,6 @@
         }
 
         if (Downloads.Impl.isStatusCompleted(mInfoDelta.mStatus)) {
-            // If download was canceled, we already sent requested intent when
-            // deleted in the provider
-            if (mInfoDelta.mStatus != STATUS_CANCELED) {
-                mInfo.sendIntentIfRequested();
-            }
             if (mInfo.shouldScanFile(mInfoDelta.mStatus)) {
                 DownloadScanner.requestScanBlocking(mContext, mInfo.mId, mInfoDelta.mFileName,
                         mInfoDelta.mMimeType);