OSDN Git Service

Turn MtpDocumentsService foreground when devices are opend.
authorDaichi Hirono <hirono@google.com>
Mon, 7 Dec 2015 01:52:42 +0000 (10:52 +0900)
committerDaichi Hirono <hirono@google.com>
Tue, 8 Dec 2015 08:27:04 +0000 (17:27 +0900)
BUG=26047393

Change-Id: I70f69c5ddec500ed61d1a76fb9e2e296813b2a4e

packages/MtpDocumentsProvider/AndroidManifest.xml
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java [deleted file]
packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java [new file with mode: 0644]

index 0172a4f..3861d78 100644 (file)
                 <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
             </intent-filter>
         </provider>
-        <activity android:name=".ReceiverActivity"
-                  android:theme="@android:style/Theme.NoDisplay"
-                  android:screenOrientation="locked"
-                  android:excludeFromRecents="true"
-                  android:enabled="false">
+        <service android:name=".MtpDocumentsService" />
+        <receiver android:name=".UsbIntentReceiver" android:exported="true">
             <intent-filter>
                 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
             </intent-filter>
             <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
-                       android:resource="@xml/device_filter" />
-        </activity>
-        <service android:name=".MtpDocumentsService"></service>
+                    android:resource="@xml/device_filter" />
+        </receiver>
     </application>
 </manifest>
index 9511e15..d5f00e6 100644 (file)
@@ -253,9 +253,15 @@ public class MtpDocumentsProvider extends DocumentsProvider {
         mRootScanner.notifyChange();
     }
 
-    boolean hasOpenedDevices() {
+    int[] getOpenedDeviceIds() {
         synchronized (mDeviceListLock) {
-            return mMtpManager.getOpenedDeviceIds().length != 0;
+            return mMtpManager.getOpenedDeviceIds();
+        }
+    }
+
+    String getDeviceName(int deviceId) throws IOException {
+        synchronized (mDeviceListLock) {
+            return mMtpManager.getDeviceName(deviceId);
         }
     }
 
@@ -308,7 +314,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
         getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
         mDeviceToolkits.remove(deviceId);
         mMtpManager.closeDevice(deviceId);
-        if (!hasOpenedDevices()) {
+        if (getOpenedDeviceIds().length == 0) {
             mRootScanner.pause();
         }
     }
index 9b3c20f..b0cff83 100644 (file)
 
 package com.android.mtp;
 
+import android.app.Notification;
 import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.app.NotificationManager;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbManager;
 import android.os.IBinder;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
 import java.io.IOException;
 
 /**
@@ -34,10 +34,12 @@ 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.action.ACTION_OPEN_DEVICE";
+    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 EXTRA_DEVICE = "device";
+    private static final int FOREGROUND_NOTIFICATION_ID = 1;
 
-    Receiver mReceiver;
+    NotificationManager mNotificationManager;
 
     @Override
     public IBinder onBind(Intent intent) {
@@ -48,60 +50,89 @@ public class MtpDocumentsService extends Service {
     @Override
     public void onCreate() {
         super.onCreate();
-        final IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
-        mReceiver = new Receiver();
-        registerReceiver(mReceiver, filter);
+        mNotificationManager = getSystemService(NotificationManager.class);
     }
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         // If intent is null, the service was restarted.
         if (intent != null) {
-            if (intent.getAction().equals(ACTION_OPEN_DEVICE)) {
-                final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE);
-                try {
-                    final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-                    provider.openDevice(device.getDeviceId());
-                    return START_STICKY;
-                } catch (IOException error) {
-                    Log.e(MtpDocumentsProvider.TAG, error.getMessage());
+            final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
+            final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE);
+            try {
+                Preconditions.checkNotNull(device);
+                switch (intent.getAction()) {
+                    case ACTION_OPEN_DEVICE:
+                        provider.openDevice(device.getDeviceId());
+                        break;
+
+                    case ACTION_CLOSE_DEVICE:
+                        provider.closeDevice(device.getDeviceId());
+                        break;
+
+                    default:
+                        throw new IllegalArgumentException("Received unknown intent action.");
                 }
-            } else {
-                Log.e(MtpDocumentsProvider.TAG, "Received unknown intent action.");
+            } catch (IOException | InterruptedException | IllegalArgumentException error) {
+                logErrorMessage(error);
             }
+        } else {
+            // TODO: Fetch devices again.
         }
-        stopSelfIfNeeded();
-        return Service.START_NOT_STICKY;
-    }
 
-    @Override
-    public void onDestroy() {
-        unregisterReceiver(mReceiver);
-        mReceiver = null;
-        super.onDestroy();
+        return updateForegroundState() ? START_STICKY : START_NOT_STICKY;
     }
 
-    private void stopSelfIfNeeded() {
+    /**
+     * Updates the foreground state of the service.
+     * @return Whether the service is foreground or not.
+     */
+    private boolean updateForegroundState() {
         final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-        if (!provider.hasOpenedDevices()) {
+        final int[] deviceIds = provider.getOpenedDeviceIds();
+        String message = null;
+        if (deviceIds.length != 0) {
+            // TODO: Localize the message.
+            // TODO: Add buttons "Open in Files" and "Open in Apps" if needed.
+            if (deviceIds.length > 1) {
+                message = deviceIds.length + " devices are being connected.";
+            } else {
+                try {
+                    message = provider.getDeviceName(deviceIds[0]) + " is being connected.";
+                } catch (IOException exp) {
+                    logErrorMessage(exp);
+                    // If we failed to obtain device name, it looks the device is unusable.
+                    // Because this is the last device we opened, we should hide the notification
+                    // for the case.
+                    try {
+                        provider.closeDevice(deviceIds[0]);
+                    } catch (IOException | InterruptedException closeError) {
+                        logErrorMessage(closeError);
+                    }
+                }
+            }
+        }
+        if (message != null) {
+            final Notification notification = new Notification.Builder(this)
+                    .setContentTitle(message)
+                    .setSmallIcon(android.R.drawable.ic_menu_camera)
+                    .setCategory(Notification.CATEGORY_SYSTEM)
+                    .setPriority(Notification.PRIORITY_LOW)
+                    .build();
+            startForeground(FOREGROUND_NOTIFICATION_ID, notification);
+            return true;
+        } else {
+            stopForeground(true /* removeNotification */);
             stopSelf();
+            return false;
         }
     }
 
-    private class Receiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
-                final UsbDevice device =
-                        (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
-                final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-                try {
-                    provider.closeDevice(device.getDeviceId());
-                } catch (IOException | InterruptedException error) {
-                    Log.e(MtpDocumentsProvider.TAG, error.getMessage());
-                }
-                stopSelfIfNeeded();
-            }
+    private static void logErrorMessage(Exception exp) {
+        if (exp.getMessage() != null) {
+            Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+        } else {
+            Log.e(MtpDocumentsProvider.TAG, exp.toString());
         }
     }
 }
index cd52f31..78a56c1 100644 (file)
@@ -26,6 +26,7 @@ import android.mtp.MtpEvent;
 import android.mtp.MtpObjectInfo;
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -63,7 +64,7 @@ class MtpManager {
 
         if (!mManager.hasPermission(rawDevice)) {
             // Permission should be obtained via app selection dialog for intent.
-            throw new IOException("No parmission to operate USB device.");
+            throw new IOException("No permission to operate USB device.");
         }
 
         final MtpDevice device = new MtpDevice(rawDevice);
@@ -99,6 +100,10 @@ class MtpManager {
         return result;
     }
 
+    String getDeviceName(int deviceId) throws IOException {
+        return getDevice(deviceId).getDeviceInfo().getModel();
+    }
+
     MtpRoot[] getRoots(int deviceId) throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
deleted file mode 100644 (file)
index 3ad2397..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mtp;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
-
-/**
- * Invisible activity to receive intents.
- * To show the application chooser for the UsbManager.ACTION_USB_DEVICE_ATTACHED intent, the intent
- * should be received by activity. The activity has NoDisplay theme and immediately terminate after
- * routing intent to MtpDocumentsService.
- */
-public class ReceiverActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(getIntent().getAction())) {
-            final Intent serviceIntent = new Intent(
-                    MtpDocumentsService.ACTION_OPEN_DEVICE,
-                    null,
-                    this,
-                    MtpDocumentsService.class);
-            serviceIntent.putExtra(
-                    UsbManager.EXTRA_DEVICE,
-                    getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE));
-            startService(serviceIntent);
-        }
-        finish();
-    }
-}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java b/packages/MtpDocumentsProvider/src/com/android/mtp/UsbIntentReceiver.java
new file mode 100644 (file)
index 0000000..0ac130e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.net.Uri;
+
+public class UsbIntentReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final UsbDevice device = intent.getExtras().getParcelable(UsbManager.EXTRA_DEVICE);
+        switch (intent.getAction()) {
+            case UsbManager.ACTION_USB_DEVICE_ATTACHED:
+                startService(context, MtpDocumentsService.ACTION_OPEN_DEVICE, device);
+                break;
+            case UsbManager.ACTION_USB_DEVICE_DETACHED:
+                startService(context, MtpDocumentsService.ACTION_CLOSE_DEVICE, device);
+                break;
+        }
+    }
+
+    private void startService(Context context, String action, UsbDevice device) {
+        final Intent intent = new Intent(action, Uri.EMPTY, context, MtpDocumentsService.class);
+        intent.putExtra(MtpDocumentsService.EXTRA_DEVICE, device);
+        context.startService(intent);
+    }
+}