Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58ea989a authored by Daichi Hirono's avatar Daichi Hirono Committed by Android (Google) Code Review
Browse files

Merge "Turn MtpDocumentsService foreground when devices are opend."

parents c2f262bc a57d9ed0
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -15,17 +15,14 @@
                <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>
        </receiver>
    </application>
</manifest>
+9 −3
Original line number Diff line number Diff line
@@ -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();
        }
    }
+74 −43
Original line number Diff line number Diff line
@@ -16,16 +16,16 @@

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 MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
            final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE);
            try {
                    final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
                Preconditions.checkNotNull(device);
                switch (intent.getAction()) {
                    case ACTION_OPEN_DEVICE:
                        provider.openDevice(device.getDeviceId());
                    return START_STICKY;
                } catch (IOException error) {
                    Log.e(MtpDocumentsProvider.TAG, error.getMessage());
                }
            } else {
                Log.e(MtpDocumentsProvider.TAG, "Received unknown intent action.");
                        break;

                    case ACTION_CLOSE_DEVICE:
                        provider.closeDevice(device.getDeviceId());
                        break;

                    default:
                        throw new IllegalArgumentException("Received unknown intent action.");
                }
            } catch (IOException | InterruptedException | IllegalArgumentException error) {
                logErrorMessage(error);
            }
        stopSelfIfNeeded();
        return Service.START_NOT_STICKY;
        } else {
            // TODO: Fetch devices again.
        }

    @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()) {
            stopSelf();
        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);
                    }
                }

    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();
        }
        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 static void logErrorMessage(Exception exp) {
        if (exp.getMessage() != null) {
            Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
        } else {
            Log.e(MtpDocumentsProvider.TAG, exp.toString());
        }
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -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) {
+45 −0
Original line number Diff line number Diff line
@@ -16,32 +16,30 @@

package com.android.mtp;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.net.Uri;

/**
 * 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 {
public class UsbIntentReceiver extends BroadcastReceiver {
    @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);
    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;
        }
    }
        finish();

    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);
    }
}
Loading