summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-07-08 23:33:53 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-07-08 23:33:53 +0000
commit003bac8220af9bd78c6943e55962ee1f167b903f (patch)
tree2ce1c60a34e27ec2b58500eb8927071d9ccfe7b6
parent72783c7592e0ff582e5dede392d77809e0924e95 (diff)
parent9b6f2643661e4667493debdcccbba3ec518eb963 (diff)
downloadDocumentsUI-003bac8220af9bd78c6943e55962ee1f167b903f.tar.gz
Snap for 5622519 from 9b6f2643661e4667493debdcccbba3ec518eb963 to pi-platform-releasepie-platform-release
Change-Id: I8a8f866a21085bb92c452610b922e2651eb5ea15
-rw-r--r--src/com/android/documentsui/services/FileOperationService.java84
-rw-r--r--tests/common/com/android/documentsui/services/TestForegroundManager.java16
-rw-r--r--tests/common/com/android/documentsui/services/TestNotificationManager.java38
-rw-r--r--tests/unit/com/android/documentsui/services/FileOperationServiceTest.java30
4 files changed, 105 insertions, 63 deletions
diff --git a/src/com/android/documentsui/services/FileOperationService.java b/src/com/android/documentsui/services/FileOperationService.java
index 40f18d38a..fa97ebf17 100644
--- a/src/com/android/documentsui/services/FileOperationService.java
+++ b/src/com/android/documentsui/services/FileOperationService.java
@@ -37,13 +37,12 @@ import com.android.documentsui.base.Features;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.concurrent.GuardedBy;
@@ -99,9 +98,9 @@ public class FileOperationService extends Service implements Job.Listener {
private static final int POOL_SIZE = 2; // "pool size", not *max* "pool size".
- private static final int NOTIFICATION_ID_PROGRESS = 0;
- private static final int NOTIFICATION_ID_FAILURE = 1;
- private static final int NOTIFICATION_ID_WARNING = 2;
+ @VisibleForTesting static final int NOTIFICATION_ID_PROGRESS = 1;
+ private static final int NOTIFICATION_ID_FAILURE = 2;
+ private static final int NOTIFICATION_ID_WARNING = 3;
// The executor and job factory are visible for testing and non-final
// so we'll have a way to inject test doubles from the test. It's
@@ -124,10 +123,11 @@ public class FileOperationService extends Service implements Job.Listener {
@VisibleForTesting Features features;
@GuardedBy("mJobs")
- private final Map<String, JobRecord> mJobs = new HashMap<>();
+ private final Map<String, JobRecord> mJobs = new LinkedHashMap<>();
// The job whose notification is used to keep the service in foreground mode.
- private final AtomicReference<Job> mForegroundJob = new AtomicReference<>();
+ @GuardedBy("mJobs")
+ private Job mForegroundJob;
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock; // the wake lock, if held.
@@ -270,6 +270,7 @@ public class FileOperationService extends Service implements Job.Listener {
JobRecord record = mJobs.get(jobId);
if (record != null) {
record.job.cancel();
+ updateForegroundState(record.job);
}
}
@@ -343,18 +344,23 @@ public class FileOperationService extends Service implements Job.Listener {
Notification notification = job.getSetupNotification();
// If there is no foreground job yet, set this job to foreground job.
- if (mForegroundJob.compareAndSet(null, job)) {
- if (DEBUG) Log.d(TAG, "Set foreground job to " + job.id);
- foregroundManager.startForeground(NOTIFICATION_ID_PROGRESS, notification);
+ synchronized (mJobs) {
+ if (mForegroundJob == null) {
+ if (DEBUG) Log.d(TAG, "Set foreground job to " + job.id);
+ mForegroundJob = job;
+ foregroundManager.startForeground(NOTIFICATION_ID_PROGRESS, notification);
+ } else {
+ // Show start up notification
+ if (DEBUG) Log.d(TAG, "Posting notification for " + job.id);
+ notificationManager.notify(
+ mForegroundJob == job ? null : job.id,
+ NOTIFICATION_ID_PROGRESS,
+ notification);
+ }
}
- // Show start up notification
- if (DEBUG) Log.d(TAG, "Posting notification for " + job.id);
- notificationManager.notify(
- job.id, NOTIFICATION_ID_PROGRESS, notification);
-
// Set up related monitor
- JobMonitor monitor = new JobMonitor(job, notificationManager, handler, mJobs);
+ JobMonitor monitor = new JobMonitor(job);
monitor.start();
}
@@ -388,11 +394,12 @@ public class FileOperationService extends Service implements Job.Listener {
@GuardedBy("mJobs")
private void updateForegroundState(Job job) {
- Job candidate = mJobs.isEmpty() ? null : mJobs.values().iterator().next().job;
+ Job candidate = getCandidateForegroundJob();
// If foreground job is retiring and there is still work to do, we need to set it to a new
// job.
- if (mForegroundJob.compareAndSet(job, candidate)) {
+ if (mForegroundJob == job) {
+ mForegroundJob = candidate;
if (candidate == null) {
if (DEBUG) Log.d(TAG, "Stop foreground");
// Remove the notification here just in case we're torn down before we have the
@@ -401,12 +408,11 @@ public class FileOperationService extends Service implements Job.Listener {
} else {
if (DEBUG) Log.d(TAG, "Switch foreground job to " + candidate.id);
+ notificationManager.cancel(candidate.id, NOTIFICATION_ID_PROGRESS);
Notification notification = (candidate.getState() == Job.STATE_STARTED)
? candidate.getSetupNotification()
: candidate.getProgressNotification();
- foregroundManager.startForeground(NOTIFICATION_ID_PROGRESS, notification);
- notificationManager.notify(candidate.id, NOTIFICATION_ID_PROGRESS,
- notification);
+ notificationManager.notify(NOTIFICATION_ID_PROGRESS, notification);
}
}
}
@@ -435,6 +441,19 @@ public class FileOperationService extends Service implements Job.Listener {
}
}
+ @GuardedBy("mJobs")
+ private Job getCandidateForegroundJob() {
+ if (mJobs.isEmpty()) {
+ return null;
+ }
+ for (JobRecord rec : mJobs.values()) {
+ if (!rec.job.isFinished()) {
+ return rec.job;
+ }
+ }
+ return null;
+ }
+
private static final class JobRecord {
private final Job job;
private final Future<?> future;
@@ -452,29 +471,22 @@ public class FileOperationService extends Service implements Job.Listener {
* still need to update notifications if jobs hang, so instead of jobs pushing their states,
* we poll states of jobs.
*/
- private static final class JobMonitor implements Runnable {
+ private final class JobMonitor implements Runnable {
private static final long PROGRESS_INTERVAL_MILLIS = 500L;
private final Job mJob;
- private final NotificationManager mNotificationManager;
- private final Handler mHandler;
- private final Object mJobsLock;
- private JobMonitor(Job job, NotificationManager notificationManager, Handler handler,
- Object jobsLock) {
+ private JobMonitor(Job job) {
mJob = job;
- mNotificationManager = notificationManager;
- mHandler = handler;
- mJobsLock = jobsLock;
}
private void start() {
- mHandler.post(this);
+ handler.post(this);
}
@Override
public void run() {
- synchronized (mJobsLock) {
+ synchronized (mJobs) {
if (mJob.isFinished()) {
// Finish notification is already shown. Progress notification is removed.
// Just finish itself.
@@ -483,11 +495,13 @@ public class FileOperationService extends Service implements Job.Listener {
// Only job in set up state has progress bar
if (mJob.getState() == Job.STATE_SET_UP) {
- mNotificationManager.notify(
- mJob.id, NOTIFICATION_ID_PROGRESS, mJob.getProgressNotification());
+ notificationManager.notify(
+ mForegroundJob == mJob ? null : mJob.id,
+ NOTIFICATION_ID_PROGRESS,
+ mJob.getProgressNotification());
}
- mHandler.postDelayed(this, PROGRESS_INTERVAL_MILLIS);
+ handler.postDelayed(this, PROGRESS_INTERVAL_MILLIS);
}
}
}
diff --git a/tests/common/com/android/documentsui/services/TestForegroundManager.java b/tests/common/com/android/documentsui/services/TestForegroundManager.java
index f4ba1c191..8b226a0aa 100644
--- a/tests/common/com/android/documentsui/services/TestForegroundManager.java
+++ b/tests/common/com/android/documentsui/services/TestForegroundManager.java
@@ -23,17 +23,25 @@ import android.app.Notification;
class TestForegroundManager implements FileOperationService.ForegroundManager {
+ private final TestNotificationManager mNotificationManager;
private int mForegroundId = -1;
private Notification mForegroundNotification;
+ TestForegroundManager(TestNotificationManager notificationManager) {
+ assert(notificationManager != null);
+ mNotificationManager = notificationManager;
+ }
+
@Override
public void startForeground(int id, Notification notification) {
mForegroundId = id;
mForegroundNotification = notification;
+ mNotificationManager.notify(null, mForegroundId, mForegroundNotification);
}
@Override
public void stopForeground(boolean cancelNotification) {
+ mNotificationManager.cancel(null, mForegroundId);
mForegroundId = -1;
mForegroundNotification = null;
}
@@ -45,12 +53,4 @@ class TestForegroundManager implements FileOperationService.ForegroundManager {
void assertInBackground() {
assertNull(mForegroundNotification);
}
-
- int getForegroundId() {
- return mForegroundId;
- }
-
- Notification getForegroundNotification() {
- return mForegroundNotification;
- }
}
diff --git a/tests/common/com/android/documentsui/services/TestNotificationManager.java b/tests/common/com/android/documentsui/services/TestNotificationManager.java
index 463f2d425..5adde6563 100644
--- a/tests/common/com/android/documentsui/services/TestNotificationManager.java
+++ b/tests/common/com/android/documentsui/services/TestNotificationManager.java
@@ -17,6 +17,7 @@
package com.android.documentsui.services;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
@@ -32,21 +33,10 @@ import java.util.HashMap;
class TestNotificationManager {
- private final TestForegroundManager mForegroundManager;
private final SparseArray<HashMap<String, Notification>> mNotifications = new SparseArray<>();
private final Answer<Void> mAnswer = this::invoke;
- TestNotificationManager(TestForegroundManager foregroundManager) {
- assert(foregroundManager != null);
- mForegroundManager = foregroundManager;
- }
-
- private void notify(String tag, int id, Notification notification) {
- if (notification == mForegroundManager.getForegroundNotification()
- && id != mForegroundManager.getForegroundId()) {
- throw new IllegalStateException("Mismatching ID and notification.");
- }
-
+ void notify(String tag, int id, Notification notification) {
if (mNotifications.get(id) == null) {
mNotifications.put(id, new HashMap<>());
}
@@ -54,14 +44,10 @@ class TestNotificationManager {
mNotifications.get(id).put(tag, notification);
}
- private void cancel(String tag, int id) {
+ void cancel(String tag, int id) {
final HashMap<String, Notification> idMap = mNotifications.get(id);
if (idMap != null && idMap.containsKey(tag)) {
- final Notification notification = idMap.get(tag);
- // Only cancel non-foreground notification
- if (mForegroundManager.getForegroundNotification() != notification) {
- idMap.remove(tag);
- }
+ idMap.remove(tag);
}
}
@@ -88,6 +74,14 @@ class TestNotificationManager {
return null;
}
+ private boolean hasNotification(int id, String jobId) {
+ if (mNotifications.get(id) == null) {
+ return false;
+ }
+ Notification notification = mNotifications.get(id).get(jobId);
+ return notification != null;
+ }
+
NotificationManager createNotificationManager() {
return Mockito.mock(NotificationManager.class, mAnswer);
}
@@ -100,4 +94,12 @@ class TestNotificationManager {
assertEquals(expect, count);
}
+
+ void assertHasNotification(int id, String jobid) {
+ assertTrue(hasNotification(id, jobid));
+ }
+
+ void assertNoNotification(int id, String jobid) {
+ assertFalse(hasNotification(id, jobid));
+ }
}
diff --git a/tests/unit/com/android/documentsui/services/FileOperationServiceTest.java b/tests/unit/com/android/documentsui/services/FileOperationServiceTest.java
index a210c0ee1..fbaafe803 100644
--- a/tests/unit/com/android/documentsui/services/FileOperationServiceTest.java
+++ b/tests/unit/com/android/documentsui/services/FileOperationServiceTest.java
@@ -78,8 +78,8 @@ public class FileOperationServiceTest extends ServiceTestCase<FileOperationServi
mExecutor = new TestScheduledExecutorService();
mDeletionExecutor = new TestScheduledExecutorService();
mHandler = new TestHandler();
- mForegroundManager = new TestForegroundManager();
- mTestNotificationManager = new TestNotificationManager(mForegroundManager);
+ mTestNotificationManager = new TestNotificationManager();
+ mForegroundManager = new TestForegroundManager(mTestNotificationManager);
TestFeatures features = new TestFeatures();
features.notificationChannel = InstrumentationRegistry.getTargetContext()
.getResources().getBoolean(R.bool.feature_notification_channel);
@@ -294,6 +294,32 @@ public class FileOperationServiceTest extends ServiceTestCase<FileOperationServi
mTestNotificationManager.assertNumberOfNotifications(0);
}
+ public void testNotificationUpdateAfterForegroundJobSwitch() throws Exception {
+ startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
+ startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
+ Job job1 = mCopyJobs.get(0);
+ Job job2 = mCopyJobs.get(1);
+
+ mService.onStart(job1);
+ mTestNotificationManager.assertHasNotification(
+ FileOperationService.NOTIFICATION_ID_PROGRESS, null);
+
+ mService.onStart(job2);
+ mTestNotificationManager.assertHasNotification(
+ FileOperationService.NOTIFICATION_ID_PROGRESS, job2.id);
+
+ job1.cancel();
+ mService.onFinished(job1);
+ mTestNotificationManager.assertHasNotification(
+ FileOperationService.NOTIFICATION_ID_PROGRESS, null);
+ mTestNotificationManager.assertNoNotification(
+ FileOperationService.NOTIFICATION_ID_PROGRESS, job2.id);
+
+ job2.cancel();
+ mService.onFinished(job2);
+ mTestNotificationManager.assertNumberOfNotifications(0);
+ }
+
private Intent createCopyIntent(ArrayList<DocumentInfo> files, DocumentInfo dest)
throws Exception {
DocumentStack stack = new DocumentStack();