18182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood/*
28182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * Copyright (C) 2010 The Android Open Source Project
38182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood *
48182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License");
58182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * you may not use this file except in compliance with the License.
68182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * You may obtain a copy of the License at
78182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood *
88182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood *      http://www.apache.org/licenses/LICENSE-2.0
98182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood *
108182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * Unless required by applicable law or agreed to in writing, software
118182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS,
128182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * See the License for the specific language governing permissions and
148182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * limitations under the License.
158182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood */
168182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
17c72eb9113a2f3f7886afcecf5c29507e0a3a8266Mike Lockwoodpackage com.android.camerabrowser;
188182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
193a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwoodimport android.app.PendingIntent;
208182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport android.content.BroadcastReceiver;
218182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport android.content.Context;
228182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport android.content.Intent;
238182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport android.content.IntentFilter;
24c4308f01c965571dc2354107c3574df113e397eeMike Lockwoodimport android.hardware.usb.UsbConstants;
25c4308f01c965571dc2354107c3574df113e397eeMike Lockwoodimport android.hardware.usb.UsbDevice;
26acc29cc91be634070c92a807df412ced97b9b375Mike Lockwoodimport android.hardware.usb.UsbDeviceConnection;
27c4308f01c965571dc2354107c3574df113e397eeMike Lockwoodimport android.hardware.usb.UsbInterface;
28c4308f01c965571dc2354107c3574df113e397eeMike Lockwoodimport android.hardware.usb.UsbManager;
29c72eb9113a2f3f7886afcecf5c29507e0a3a8266Mike Lockwoodimport android.mtp.MtpDevice;
30c72eb9113a2f3f7886afcecf5c29507e0a3a8266Mike Lockwoodimport android.mtp.MtpDeviceInfo;
31c72eb9113a2f3f7886afcecf5c29507e0a3a8266Mike Lockwoodimport android.mtp.MtpObjectInfo;
32c72eb9113a2f3f7886afcecf5c29507e0a3a8266Mike Lockwoodimport android.mtp.MtpStorageInfo;
338182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport android.os.ParcelFileDescriptor;
348182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport android.util.Log;
358182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
368182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport java.io.IOException;
378182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport java.util.ArrayList;
382b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwoodimport java.util.HashMap;
398182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodimport java.util.List;
408182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
418182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood/**
42540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood * This class helps an application manage a list of connected MTP or PTP devices.
438182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * It listens for MTP devices being attached and removed from the USB host bus
448182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood * and notifies the application when the MTP device list changes.
458182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood */
468182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwoodpublic class MtpClient {
478182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
488182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    private static final String TAG = "MtpClient";
498182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
503a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood    private static final String ACTION_USB_PERMISSION =
513a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood            "android.mtp.MtpClient.action.USB_PERMISSION";
523a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood
538182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    private final Context mContext;
548182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    private final UsbManager mUsbManager;
558182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
562b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood    // mDevices contains all MtpDevices that have been seen by our client,
572b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood    // so we can inform when the device has been detached.
582b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood    // mDevices is also used for synchronization in this class.
592b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood    private final HashMap<String, MtpDevice> mDevices = new HashMap<String, MtpDevice>();
608182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
613a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood    private final PendingIntent mPermissionIntent;
623a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood
638182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
648182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        @Override
658182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        public void onReceive(Context context, Intent intent) {
663a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood            String action = intent.getAction();
67188d00b07ef0b8968868b3489e80dd9f53d3bafaMike Lockwood            UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
68188d00b07ef0b8968868b3489e80dd9f53d3bafaMike Lockwood            String deviceName = usbDevice.getDeviceName();
698182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
702b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            synchronized (mDevices) {
712b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood                MtpDevice mtpDevice = mDevices.get(deviceName);
728182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
733a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
748182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                    if (mtpDevice == null) {
752b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood                        mtpDevice = openDeviceLocked(usbDevice);
768182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                    }
778182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                    if (mtpDevice != null) {
788182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                        for (Listener listener : mListeners) {
798182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                            listener.deviceAdded(mtpDevice);
808182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                        }
818182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                    }
823a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
833a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                    if (mtpDevice != null) {
843a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                        mDevices.remove(deviceName);
853a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                        for (Listener listener : mListeners) {
863a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                            listener.deviceRemoved(mtpDevice);
873a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                        }
883a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                    }
893a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                } else if (ACTION_USB_PERMISSION.equals(action)) {
903a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                    boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED,
913a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                            false);
923a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                    Log.d(TAG, "ACTION_USB_PERMISSION: " + permission);
933a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                    if (permission) {
943a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                        if (mtpDevice == null) {
953a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                            mtpDevice = openDeviceLocked(usbDevice);
963a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                        }
973a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                        if (mtpDevice != null) {
983a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                            for (Listener listener : mListeners) {
993a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                                listener.deviceAdded(mtpDevice);
1003a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                            }
1013a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                        }
1028182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                    }
1038182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                }
1048182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            }
1058182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
1068182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    };
1078182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
108540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
109540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * An interface for being notified when MTP or PTP devices are attached
110540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * or removed.  In the current implementation, only PTP devices are supported.
111540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
1128182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public interface Listener {
113540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood        /**
114540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         * Called when a new device has been added
115540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         *
116540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         * @param device the new device that was added
117540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         */
1188182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        public void deviceAdded(MtpDevice device);
119540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood
120540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood        /**
121540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         * Called when a new device has been removed
122540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         *
123540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         * @param device the device that was removed
124540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood         */
1258182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        public void deviceRemoved(MtpDevice device);
1268182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
1278182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
128540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
129c4308f01c965571dc2354107c3574df113e397eeMike Lockwood     * Tests to see if a {@link android.hardware.usb.UsbDevice}
130540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * supports the PTP protocol (typically used by digital cameras)
131540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
132540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param device the device to test
133540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return true if the device is a PTP device.
134540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
135540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    static public boolean isCamera(UsbDevice device) {
1368182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        int count = device.getInterfaceCount();
1378182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        for (int i = 0; i < count; i++) {
1388182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            UsbInterface intf = device.getInterface(i);
1398182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            if (intf.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
1408182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                    intf.getInterfaceSubclass() == 1 &&
1418182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                    intf.getInterfaceProtocol() == 1) {
1428182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                return true;
1438182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            }
1448182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
1458182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return false;
1468182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
1478182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
148540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
149540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * MtpClient constructor
150540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
151540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param context the {@link android.content.Context} to use for the MtpClient
152540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
1538182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public MtpClient(Context context) {
1548182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        mContext = context;
1558182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
1563a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood        mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
1578182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        IntentFilter filter = new IntentFilter();
1588182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
1598182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
1603a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood        filter.addAction(ACTION_USB_PERMISSION);
1618182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        context.registerReceiver(mUsbReceiver, filter);
1628182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
1638182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
164540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
165c4308f01c965571dc2354107c3574df113e397eeMike Lockwood     * Opens the {@link android.hardware.usb.UsbDevice} for an MTP or PTP
166540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * device and return an {@link android.mtp.MtpDevice} for it.
167540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
168540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param device the device to open
169540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return an MtpDevice for the device.
170540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
1712b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood    private MtpDevice openDeviceLocked(UsbDevice usbDevice) {
172540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood        if (isCamera(usbDevice)) {
1733a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood            if (!mUsbManager.hasPermission(usbDevice)) {
1743a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                mUsbManager.requestPermission(usbDevice, mPermissionIntent);
1753a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood            } else {
176acc29cc91be634070c92a807df412ced97b9b375Mike Lockwood                UsbDeviceConnection connection = mUsbManager.openDevice(usbDevice);
177acc29cc91be634070c92a807df412ced97b9b375Mike Lockwood                if (connection != null) {
178acc29cc91be634070c92a807df412ced97b9b375Mike Lockwood                    MtpDevice mtpDevice = new MtpDevice(usbDevice);
179acc29cc91be634070c92a807df412ced97b9b375Mike Lockwood                    if (mtpDevice.open(connection)) {
180acc29cc91be634070c92a807df412ced97b9b375Mike Lockwood                        mDevices.put(usbDevice.getDeviceName(), mtpDevice);
181acc29cc91be634070c92a807df412ced97b9b375Mike Lockwood                        return mtpDevice;
182acc29cc91be634070c92a807df412ced97b9b375Mike Lockwood                    }
1833a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                }
184540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood            }
185540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood        }
186540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood        return null;
187540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    }
188540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood
189540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
190540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Closes all resources related to the MtpClient object
191540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
1928182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public void close() {
1938182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        mContext.unregisterReceiver(mUsbReceiver);
1948182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
1958182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
196540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
197540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Registers a {@link android.mtp.MtpClient.Listener} interface to receive
198540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * notifications when MTP or PTP devices are added or removed.
199540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
200540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param listener the listener to register
201540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
2028182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public void addListener(Listener listener) {
2032b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood        synchronized (mDevices) {
2048182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            if (!mListeners.contains(listener)) {
2058182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                mListeners.add(listener);
2068182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            }
2078182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2088182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
2098182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
210540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
211540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Unregisters a {@link android.mtp.MtpClient.Listener} interface.
212540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
213540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param listener the listener to unregister
214540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
2158182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public void removeListener(Listener listener) {
2162b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood        synchronized (mDevices) {
2178182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            mListeners.remove(listener);
2188182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2198182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
2208182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
221540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
222540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Retrieves an {@link android.mtp.MtpDevice} object for the USB device
223540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * with the given name.
224540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
225540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device
226540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the MtpDevice, or null if it does not exist
227540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
2288182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public MtpDevice getDevice(String deviceName) {
2292b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood        synchronized (mDevices) {
2302b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            return mDevices.get(deviceName);
2318182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2328182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
2338182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
234540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
235540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Retrieves an {@link android.mtp.MtpDevice} object for the USB device
236540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * with the given ID.
237540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
238540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param id the ID of the USB device
239540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the MtpDevice, or null if it does not exist
240540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
2418182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public MtpDevice getDevice(int id) {
2422b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood        synchronized (mDevices) {
2432b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            return mDevices.get(UsbDevice.getDeviceName(id));
2448182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2458182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
2468182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
247540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
248540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Retrieves a list of all currently connected {@link android.mtp.MtpDevice}.
249540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
250540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the list of MtpDevices
251540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
2528182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public List<MtpDevice> getDeviceList() {
2532b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood        synchronized (mDevices) {
2542b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            // Query the USB manager since devices might have attached
2552b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            // before we added our listener.
2562b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
2573a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                if (mDevices.get(usbDevice.getDeviceName()) == null) {
2583a68b8338b431eb15d28e92f06452efbbda9493eMike Lockwood                    openDeviceLocked(usbDevice);
2592b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood                }
2602b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            }
2612b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood
2622b8a1ee0578e81b6600d5b2b03db9446d63e4616Mike Lockwood            return new ArrayList<MtpDevice>(mDevices.values());
2638182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2648182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
2658182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
266540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
267540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Retrieves a list of all {@link android.mtp.MtpStorageInfo}
268540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * for the MTP or PTP device with the given USB device name
269540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
270540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device
271540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the list of MtpStorageInfo
272540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
2738182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public List<MtpStorageInfo> getStorageList(String deviceName) {
2748182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        MtpDevice device = getDevice(deviceName);
2758182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (device == null) {
2768182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return null;
2778182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2788182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        int[] storageIds = device.getStorageIds();
2798182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (storageIds == null) {
2808182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return null;
2818182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2828182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
2838182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        int length = storageIds.length;
2848182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        ArrayList<MtpStorageInfo> storageList = new ArrayList<MtpStorageInfo>(length);
2858182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        for (int i = 0; i < length; i++) {
2868182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            MtpStorageInfo info = device.getStorageInfo(storageIds[i]);
2878182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            if (info == null) {
2888182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                Log.w(TAG, "getStorageInfo failed");
2898182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            } else {
2908182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                storageList.add(info);
2918182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            }
2928182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
2938182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return storageList;
2948182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
2958182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
296540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
297540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Retrieves the {@link android.mtp.MtpObjectInfo} for an object on
298540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * the MTP or PTP device with the given USB device name with the given
299540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * object handle
300540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
301540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device
302540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param objectHandle handle of the object to query
303540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the MtpObjectInfo
304540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
3058182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public MtpObjectInfo getObjectInfo(String deviceName, int objectHandle) {
3068182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        MtpDevice device = getDevice(deviceName);
3078182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (device == null) {
3088182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return null;
3098182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3108182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return device.getObjectInfo(objectHandle);
3118182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
3128182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
313540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
314540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Deletes an object on the MTP or PTP device with the given USB device name.
315540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
316540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device
317540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param objectHandle handle of the object to delete
318540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return true if the deletion succeeds
319540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
3208182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public boolean deleteObject(String deviceName, int objectHandle) {
3218182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        MtpDevice device = getDevice(deviceName);
3228182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (device == null) {
3238182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return false;
3248182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3258182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return device.deleteObject(objectHandle);
3268182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
3278182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
328540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
329540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Retrieves a list of {@link android.mtp.MtpObjectInfo} for all objects
330540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * on the MTP or PTP device with the given USB device name and given storage ID
331540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * and/or object handle.
332540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * If the object handle is zero, then all objects in the root of the storage unit
333540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * will be returned. Otherwise, all immediate children of the object will be returned.
334540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * If the storage ID is also zero, then all objects on all storage units will be returned.
335540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
336540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device
337540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param storageId the ID of the storage unit to query, or zero for all
338540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param objectHandle the handle of the parent object to query, or zero for the storage root
339540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the list of MtpObjectInfo
340540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
3418182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public List<MtpObjectInfo> getObjectList(String deviceName, int storageId, int objectHandle) {
3428182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        MtpDevice device = getDevice(deviceName);
3438182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (device == null) {
3448182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return null;
3458182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3468182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (objectHandle == 0) {
3478182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            // all objects in root of storage
3488182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            objectHandle = 0xFFFFFFFF;
3498182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3508182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        int[] handles = device.getObjectHandles(storageId, 0, objectHandle);
3518182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (handles == null) {
3528182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return null;
3538182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3548182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
3558182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        int length = handles.length;
3568182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        ArrayList<MtpObjectInfo> objectList = new ArrayList<MtpObjectInfo>(length);
3578182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        for (int i = 0; i < length; i++) {
3588182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            MtpObjectInfo info = device.getObjectInfo(handles[i]);
3598182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            if (info == null) {
3608182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                Log.w(TAG, "getObjectInfo failed");
3618182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            } else {
3628182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood                objectList.add(info);
3638182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            }
3648182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3658182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return objectList;
3668182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
3678182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
368540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
369540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Returns the data for an object as a byte array.
370540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
371540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device containing the object
372540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param objectHandle handle of the object to read
373540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param objectSize the size of the object (this should match
374540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *      {@link android.mtp.MtpObjectInfo#getCompressedSize}
375540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the object's data, or null if reading fails
376540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
3778182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public byte[] getObject(String deviceName, int objectHandle, int objectSize) {
3788182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        MtpDevice device = getDevice(deviceName);
3798182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (device == null) {
3808182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return null;
3818182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3828182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return device.getObject(objectHandle, objectSize);
3838182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
3848182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
385540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
386540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Returns the thumbnail data for an object as a byte array.
387540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
388540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device containing the object
389540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param objectHandle handle of the object to read
390540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return the object's thumbnail, or null if reading fails
391540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
3928182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public byte[] getThumbnail(String deviceName, int objectHandle) {
3938182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        MtpDevice device = getDevice(deviceName);
3948182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (device == null) {
3958182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return null;
3968182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
3978182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return device.getThumbnail(objectHandle);
3988182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
3998182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood
400540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood    /**
401540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * Copies the data for an object to a file in external storage.
402540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *
403540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param deviceName the name of the USB device containing the object
404540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param objectHandle handle of the object to read
405540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @param destPath path to destination for the file transfer.
406540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *      This path should be in the external storage as defined by
407540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     *      {@link android.os.Environment#getExternalStorageDirectory}
408540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     * @return true if the file transfer succeeds
409540380fb0a45b12af3970f9ea1ed041607451f46Mike Lockwood     */
4108182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    public boolean importFile(String deviceName, int objectHandle, String destPath) {
4118182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        MtpDevice device = getDevice(deviceName);
4128182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        if (device == null) {
4138182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood            return false;
4148182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        }
4158182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood        return device.importFile(objectHandle, destPath);
4168182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood    }
4178182e72479a8b0d832df9c392890b25bfa6f97b5Mike Lockwood}
418