From d3c6dd1522dc9b08476b70c4f6923f4debff78ee Mon Sep 17 00:00:00 2001 From: Daichi Hirono Date: Thu, 26 Jan 2017 14:40:19 +0900 Subject: [PATCH] Start MtpDocumentsService as foreground service. Prevously MtpDocumentsService was started as background service, then it turns into a foreground service by calling Service#startForeground. The workflow did not work until this, because now background activity cannot launch a background service. The CL starts using NotificationManager#startForegroundService to launch MtpDocumentsService so that the service can be started as foreground service directly. Bug: 34468813 Test: MtpDocumentsProviderTests Test: manual testing with connecting MTP device to Android Change-Id: Ic35d3e92f234881846e5d82ed04d6681a83035f7 --- .../com/android/mtp/MtpDocumentsProvider.java | 4 +- .../com/android/mtp/MtpDocumentsService.java | 75 +++++++++++-------- .../com/android/mtp/ServiceIntentSender.java | 19 ++++- .../android/mtp/TestServiceIntentSender.java | 4 +- 4 files changed, 65 insertions(+), 37 deletions(-) diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index 8b0e610c9af8c..b8c10a6a33666 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -509,7 +509,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { final DeviceToolkit toolkit = new DeviceToolkit(mMtpManager, mResolver, mDatabase, device); mDeviceToolkits.put(deviceId, toolkit); - mIntentSender.sendUpdateNotificationIntent(); + mIntentSender.sendUpdateNotificationIntent(device); try { mRootScanner.resume().await(); } catch (InterruptedException error) { @@ -526,7 +526,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { closeDeviceInternal(deviceId); } mRootScanner.resume(); - mIntentSender.sendUpdateNotificationIntent(); + mIntentSender.sendUpdateNotificationIntent(null); } MtpDeviceRecord[] getOpenedDeviceRecordsCache() { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java index c8846ce3fc1eb..664d3c99344ba 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java @@ -19,13 +19,12 @@ package com.android.mtp; import android.app.Notification; import android.app.Service; import android.app.NotificationManager; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.IBinder; -import android.util.Log; - -import java.io.IOException; +import android.service.notification.StatusBarNotification; +import java.util.HashSet; +import java.util.Set; /** * Service to manage lifetime of DocumentsProvider's process. @@ -33,12 +32,10 @@ import java.io.IOException; * starts to run when the first MTP device is opened, and stops when the last MTP device is closed. */ public class MtpDocumentsService extends Service { - static final String ACTION_OPEN_DEVICE = "com.android.mtp.OPEN_DEVICE"; - static final String ACTION_CLOSE_DEVICE = "com.android.mtp.CLOSE_DEVICE"; static final String ACTION_UPDATE_NOTIFICATION = "com.android.mtp.UPDATE_NOTIFICATION"; static final String EXTRA_DEVICE = "device"; - NotificationManager mNotificationManager; + private NotificationManager mNotificationManager; @Override public IBinder onBind(Intent intent) { @@ -67,39 +64,53 @@ public class MtpDocumentsService extends Service { */ private boolean updateForegroundState() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); - int notificationId = 0; - Notification notification = null; - // TODO: Hide notification if the device has already been removed. + final Set openedNotification = new HashSet<>(); + boolean hasForegroundNotification = false; + + final StatusBarNotification[] activeNotifications = + mNotificationManager.getActiveNotifications(); + for (final MtpDeviceRecord record : provider.getOpenedDeviceRecordsCache()) { - final String title = getResources().getString( - R.string.accessing_notification_title, - record.name); - notificationId = record.deviceId; - notification = new Notification.Builder(this) - .setLocalOnly(true) - .setContentTitle(title) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) - .setCategory(Notification.CATEGORY_SYSTEM) - .setPriority(Notification.PRIORITY_LOW) - .build(); - mNotificationManager.notify(record.deviceId, notification); + openedNotification.add(record.deviceId); + if (!hasForegroundNotification) { + // Mark this service as foreground with the notification so that the process is not + // killed by the system while a MTP device is opened. + startForeground(record.deviceId, createNotification(this, record)); + hasForegroundNotification = true; + } else { + // Only one notification can be shown as a foreground notification. We need to show + // the rest as normal notification. + mNotificationManager.notify(record.deviceId, createNotification(this, record)); + } } - if (notification != null) { - startForeground(notificationId, notification); - return true; - } else { + for (final StatusBarNotification notification : activeNotifications) { + if (!openedNotification.contains(notification.getId())) { + mNotificationManager.cancel(notification.getId()); + } + } + + if (!hasForegroundNotification) { + // There is no opened device. stopForeground(true /* removeNotification */); stopSelf(); return false; } + + return true; } - private static void logErrorMessage(Exception exp) { - if (exp.getMessage() != null) { - Log.e(MtpDocumentsProvider.TAG, exp.getMessage()); - } else { - Log.e(MtpDocumentsProvider.TAG, exp.toString()); - } + public static Notification createNotification(Context context, MtpDeviceRecord device) { + final String title = context.getResources().getString( + R.string.accessing_notification_title, + device.name); + return new Notification.Builder(context) + .setLocalOnly(true) + .setContentTitle(title) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) + .setCategory(Notification.CATEGORY_SYSTEM) + .setPriority(Notification.PRIORITY_LOW) + .setFlag(Notification.FLAG_NO_CLEAR, true) + .build(); } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java index a1bb2c13d3860..d8c3d357b563f 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java @@ -16,6 +16,8 @@ package com.android.mtp; +import android.annotation.Nullable; +import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,9 +32,22 @@ class ServiceIntentSender { mContext = context; } - void sendUpdateNotificationIntent() { + /** + * Notify the change of opened device set. + * @param record If a new device is opened, pass the device record. If a device is closed, pass + * null. + */ + void sendUpdateNotificationIntent(@Nullable MtpDeviceRecord record) { final Intent intent = new Intent(MtpDocumentsService.ACTION_UPDATE_NOTIFICATION); intent.setComponent(new ComponentName(mContext, MtpDocumentsService.class)); - mContext.startService(intent); + final NotificationManager manager = mContext.getSystemService(NotificationManager.class); + if (record != null) { + manager.startServiceInForeground( + intent, + record.deviceId, + MtpDocumentsService.createNotification(mContext, record)); + } else { + mContext.startService(intent); + } } } diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java index d4a4a487d8ee1..74dd429d8af54 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java @@ -16,11 +16,13 @@ package com.android.mtp; +import android.annotation.Nullable; + class TestServiceIntentSender extends ServiceIntentSender { TestServiceIntentSender() { super(null); } @Override - void sendUpdateNotificationIntent() {} + void sendUpdateNotificationIntent(@Nullable MtpDeviceRecord record) {} }