OSDN Git Service

Start MtpDocumentsService as foreground service.
authorDaichi Hirono <hirono@google.com>
Thu, 26 Jan 2017 05:40:19 +0000 (14:40 +0900)
committerDaichi Hirono <hirono@google.com>
Thu, 2 Feb 2017 00:13:08 +0000 (00:13 +0000)
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

packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
packages/MtpDocumentsProvider/src/com/android/mtp/ServiceIntentSender.java
packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestServiceIntentSender.java

index 8b0e610..b8c10a6 100644 (file)
@@ -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() {
index c8846ce..664d3c9 100644 (file)
@@ -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<Integer> 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();
     }
 }
index a1bb2c1..d8c3d35 100644 (file)
@@ -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);
+        }
     }
 }
index d4a4a48..74dd429 100644 (file)
 
 package com.android.mtp;
 
+import android.annotation.Nullable;
+
 class TestServiceIntentSender extends ServiceIntentSender {
     TestServiceIntentSender() {
         super(null);
     }
 
     @Override
-    void sendUpdateNotificationIntent() {}
+    void sendUpdateNotificationIntent(@Nullable MtpDeviceRecord record) {}
 }