Run VCardService in foreground

- VCardService will be killed if app targets to O and it runs in
  background.

Bug: 37253794

Test: manually imported/exported 20k contacts while screen is locked

Change-Id: I17d8aa7cf3a248c5d27a41bcb7ddf18cbe42d696
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index c1171d8..13d80ca 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -269,8 +269,7 @@
                 NotificationImportExportListener.constructProgressNotification(mService,
                         VCardService.TYPE_EXPORT, description, tickerText, mJobId, displayName,
                         totalCount, currentCount);
-        mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
-                mJobId, notification);
+        mService.startForeground(mJobId, notification);
     }
 
     private void doCancelNotification() {
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index d6483b8..c6fcccb 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -16,6 +16,7 @@
 package com.android.contacts.vcard;
 
 import android.accounts.Account;
+import android.app.Notification;
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.util.Log;
@@ -88,7 +89,11 @@
     public void onEntryCreated(VCardEntry entry) {
         mCurrentCount++;
         if (mListener != null) {
-            mListener.onImportParsed(mImportRequest, mJobId, entry, mCurrentCount, mTotalCount);
+            final Notification notification = mListener.onImportParsed(mImportRequest, mJobId,
+                    entry, mCurrentCount, mTotalCount);
+            if (notification != null) {
+                mService.startForeground(mJobId, notification);
+            }
         }
     }
 
diff --git a/src/com/android/contacts/vcard/NfcImportVCardActivity.java b/src/com/android/contacts/vcard/NfcImportVCardActivity.java
index b8b76da..88fa760 100644
--- a/src/com/android/contacts/vcard/NfcImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/NfcImportVCardActivity.java
@@ -230,14 +230,14 @@
     }
 
     @Override
-    public void onImportProcessed(ImportRequest request, int jobId, int sequence) {
-        // do nothing
+    public Notification onImportProcessed(ImportRequest request, int jobId, int sequence) {
+        return null;
     }
 
     @Override
-    public void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
-            int totalCount) {
-        // do nothing
+    public Notification onImportParsed(ImportRequest request, int jobId, VCardEntry entry,
+            int currentCount, int totalCount) {
+        return null;
     }
 
     @Override
@@ -271,8 +271,8 @@
     }
 
     @Override
-    public void onExportProcessed(ExportRequest request, int jobId) {
-        // do nothing
+    public Notification onExportProcessed(ExportRequest request, int jobId) {
+        return null;
     }
 
     @Override
@@ -285,11 +285,6 @@
         // do nothing
     }
 
-    @Override
-    public void onComplete() {
-        // do nothing
-    }
-
     /* package */ void showFailureNotification(int reasonId) {
         final NotificationManager notificationManager =
                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/src/com/android/contacts/vcard/NotificationImportExportListener.java b/src/com/android/contacts/vcard/NotificationImportExportListener.java
index cec96d4..46a3365 100644
--- a/src/com/android/contacts/vcard/NotificationImportExportListener.java
+++ b/src/com/android/contacts/vcard/NotificationImportExportListener.java
@@ -68,7 +68,7 @@
     }
 
     @Override
-    public void onImportProcessed(ImportRequest request, int jobId, int sequence) {
+    public Notification onImportProcessed(ImportRequest request, int jobId, int sequence) {
         // Show a notification about the status
         final String displayName;
         final String message;
@@ -90,16 +90,15 @@
         }
 
         ContactsNotificationChannelsUtil.createDefaultChannel(mContext);
-        final Notification notification = constructProgressNotification(mContext,
-                VCardService.TYPE_IMPORT, message, message, jobId, displayName, -1, 0);
-        mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
+        return constructProgressNotification(mContext, VCardService.TYPE_IMPORT, message, message,
+                jobId, displayName, -1, 0);
     }
 
     @Override
-    public void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
+    public Notification onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
             int totalCount) {
         if (entry.isIgnorable()) {
-            return;
+            return null;
         }
 
         final String totalCountString = String.valueOf(totalCount);
@@ -111,10 +110,9 @@
         final String description = mContext.getString(R.string.importing_vcard_description,
                 entry.getDisplayName());
 
-        final Notification notification = constructProgressNotification(
-                mContext.getApplicationContext(), VCardService.TYPE_IMPORT, description, tickerText,
-                jobId, request.displayName, totalCount, currentCount);
-        mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
+        return constructProgressNotification(mContext.getApplicationContext(),
+                VCardService.TYPE_IMPORT, description, tickerText, jobId, request.displayName,
+                totalCount, currentCount);
     }
 
     @Override
@@ -159,17 +157,15 @@
     }
 
     @Override
-    public void onExportProcessed(ExportRequest request, int jobId) {
+    public Notification onExportProcessed(ExportRequest request, int jobId) {
         final String displayName = ExportVCardActivity.getOpenableUriDisplayName(mContext,
                 request.destUri);
         final String message = mContext.getString(R.string.contacts_export_will_start_message);
 
         mHandler.obtainMessage(0, message).sendToTarget();
         ContactsNotificationChannelsUtil.createDefaultChannel(mContext);
-        final Notification notification =
-                NotificationImportExportListener.constructProgressNotification(mContext,
-                        VCardService.TYPE_EXPORT, message, message, jobId, displayName, -1, 0);
-        mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
+        return constructProgressNotification(mContext, VCardService.TYPE_EXPORT, message, message,
+                jobId, displayName, -1, 0);
     }
 
     @Override
@@ -321,9 +317,4 @@
                         .getActivity(context, 0, new Intent(context.getPackageName(), null), 0))
                 .getNotification();
     }
-
-    @Override
-    public void onComplete() {
-        mContext.finish();
-    }
 }
diff --git a/src/com/android/contacts/vcard/VCardImportExportListener.java b/src/com/android/contacts/vcard/VCardImportExportListener.java
index 82d7c21..fa256ad 100644
--- a/src/com/android/contacts/vcard/VCardImportExportListener.java
+++ b/src/com/android/contacts/vcard/VCardImportExportListener.java
@@ -16,21 +16,21 @@
 
 package com.android.contacts.vcard;
 
+import android.app.Notification;
 import android.net.Uri;
 
 import com.android.vcard.VCardEntry;
 
 interface VCardImportExportListener {
-    void onImportProcessed(ImportRequest request, int jobId, int sequence);
-    void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
+    Notification onImportProcessed(ImportRequest request, int jobId, int sequence);
+    Notification onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
             int totalCount);
     void onImportFinished(ImportRequest request, int jobId, Uri uri);
     void onImportFailed(ImportRequest request);
     void onImportCanceled(ImportRequest request, int jobId);
 
-    void onExportProcessed(ExportRequest request, int jobId);
+    Notification onExportProcessed(ExportRequest request, int jobId);
     void onExportFailed(ExportRequest request);
 
     void onCancelRequest(CancelRequest request, int type);
-    void onComplete();
 }
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index f5387a1..075d6bb 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts.vcard;
 
+import android.app.Notification;
 import android.app.Service;
 import android.content.Intent;
 import android.media.MediaScannerConnection;
@@ -89,7 +90,7 @@
     // requests.
     private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
 
-    private int mCurrentJobId;
+    private int mCurrentJobId = 1;
 
     // Stores all unfinished import/export jobs which will be executed by mExecutorService.
     // Key is jobId.
@@ -142,6 +143,7 @@
         if (DEBUG) Log.d(LOG_TAG, "VCardService is being destroyed.");
         cancelAllRequestsAndShutdown();
         clearCache();
+        stopForeground(/* removeNotification */ false);
         super.onDestroy();
     }
 
@@ -164,7 +166,11 @@
 
             if (tryExecute(new ImportProcessor(this, listener, request, mCurrentJobId))) {
                 if (listener != null) {
-                    listener.onImportProcessed(request, mCurrentJobId, i);
+                    final Notification notification =
+                            listener.onImportProcessed(request, mCurrentJobId, i);
+                    if (notification != null) {
+                        startForeground(mCurrentJobId, notification);
+                    }
                 }
                 mCurrentJobId++;
             } else {
@@ -193,7 +199,10 @@
             }
 
             if (listener != null) {
-                listener.onExportProcessed(request, mCurrentJobId);
+                final Notification notification = listener.onExportProcessed(request,mCurrentJobId);
+                if (notification != null) {
+                    startForeground(mCurrentJobId, notification);
+                }
             }
             mCurrentJobId++;
         } else {