1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.data;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19d43ea8bb1b1afcb27c1c6c673c8ed2707bc70ff0Owen Linimport android.annotation.TargetApi;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.app.PendingIntent;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.BroadcastReceiver;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Intent;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.IntentFilter;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.usb.UsbConstants;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.usb.UsbDevice;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.usb.UsbDeviceConnection;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.usb.UsbInterface;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.usb.UsbManager;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.mtp.MtpDevice;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.mtp.MtpObjectInfo;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.mtp.MtpStorageInfo;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.util.Log;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
35d43ea8bb1b1afcb27c1c6c673c8ed2707bc70ff0Owen Linimport com.android.gallery3d.common.ApiHelper;
36d43ea8bb1b1afcb27c1c6c673c8ed2707bc70ff0Owen Lin
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.ArrayList;
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.HashMap;
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.List;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/**
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * This class helps an application manage a list of connected MTP or PTP devices.
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * It listens for MTP devices being attached and removed from the USB host bus
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * and notifies the application when the MTP device list changes.
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
46d43ea8bb1b1afcb27c1c6c673c8ed2707bc70ff0Owen Lin@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB_MR1)
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class MtpClient {
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "MtpClient";
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String ACTION_USB_PERMISSION =
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            "android.mtp.MtpClient.action.USB_PERMISSION";
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final Context mContext;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final UsbManager mUsbManager;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // mDevices contains all MtpDevices that have been seen by our client,
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // so we can inform when the device has been detached.
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // mDevices is also used for synchronization in this class.
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final HashMap<String, MtpDevice> mDevices = new HashMap<String, MtpDevice>();
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // List of MTP devices we should not try to open for which we are currently
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // asking for permission to open.
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ArrayList<String> mRequestPermissionDevices = new ArrayList<String>();
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // List of MTP devices we should not try to open.
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // We add devices to this list if the user canceled a permission request or we were
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // unable to open the device.
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ArrayList<String> mIgnoredDevices = new ArrayList<String>();
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final PendingIntent mPermissionIntent;
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onReceive(Context context, Intent intent) {
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            String action = intent.getAction();
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            String deviceName = usbDevice.getDeviceName();
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            synchronized (mDevices) {
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MtpDevice mtpDevice = mDevices.get(deviceName);
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mtpDevice == null) {
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mtpDevice = openDeviceLocked(usbDevice);
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mtpDevice != null) {
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        for (Listener listener : mListeners) {
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            listener.deviceAdded(mtpDevice);
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mtpDevice != null) {
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mDevices.remove(deviceName);
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mRequestPermissionDevices.remove(deviceName);
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mIgnoredDevices.remove(deviceName);
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        for (Listener listener : mListeners) {
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            listener.deviceRemoved(mtpDevice);
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                } else if (ACTION_USB_PERMISSION.equals(action)) {
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mRequestPermissionDevices.remove(deviceName);
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED,
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            false);
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    Log.d(TAG, "ACTION_USB_PERMISSION: " + permission);
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (permission) {
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        if (mtpDevice == null) {
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            mtpDevice = openDeviceLocked(usbDevice);
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        if (mtpDevice != null) {
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            for (Listener listener : mListeners) {
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                                listener.deviceAdded(mtpDevice);
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            }
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    } else {
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        // so we don't ask for permission again
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mIgnoredDevices.add(deviceName);
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    };
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * An interface for being notified when MTP or PTP devices are attached
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * or removed.  In the current implementation, only PTP devices are supported.
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public interface Listener {
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        /**
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * Called when a new device has been added
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * @param device the new device that was added
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         */
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void deviceAdded(MtpDevice device);
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        /**
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * Called when a new device has been removed
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         *
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         * @param device the device that was removed
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin         */
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void deviceRemoved(MtpDevice device);
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Tests to see if a {@link android.hardware.usb.UsbDevice}
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * supports the PTP protocol (typically used by digital cameras)
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param device the device to test
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return true if the device is a PTP device.
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    static public boolean isCamera(UsbDevice device) {
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int count = device.getInterfaceCount();
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < count; i++) {
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UsbInterface intf = device.getInterface(i);
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (intf.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    intf.getInterfaceSubclass() == 1 &&
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    intf.getInterfaceProtocol() == 1) {
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return true;
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return false;
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * MtpClient constructor
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param context the {@link android.content.Context} to use for the MtpClient
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MtpClient(Context context) {
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContext = context;
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        IntentFilter filter = new IntentFilter();
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        filter.addAction(ACTION_USB_PERMISSION);
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        context.registerReceiver(mUsbReceiver, filter);
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Opens the {@link android.hardware.usb.UsbDevice} for an MTP or PTP
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * device and return an {@link android.mtp.MtpDevice} for it.
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
1827817979db0c52ffeacb951625b1e821eba303285Ahbong Chang     * @param usbDevice the device to open
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return an MtpDevice for the device.
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private MtpDevice openDeviceLocked(UsbDevice usbDevice) {
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        String deviceName = usbDevice.getDeviceName();
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // don't try to open devices that we have decided to ignore
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // or are currently asking permission for
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (isCamera(usbDevice) && !mIgnoredDevices.contains(deviceName)
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && !mRequestPermissionDevices.contains(deviceName)) {
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (!mUsbManager.hasPermission(usbDevice)) {
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mUsbManager.requestPermission(usbDevice, mPermissionIntent);
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mRequestPermissionDevices.add(deviceName);
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                UsbDeviceConnection connection = mUsbManager.openDevice(usbDevice);
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (connection != null) {
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    MtpDevice mtpDevice = new MtpDevice(usbDevice);
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mtpDevice.open(connection)) {
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mDevices.put(usbDevice.getDeviceName(), mtpDevice);
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return mtpDevice;
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    } else {
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        // so we don't try to open it again
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mIgnoredDevices.add(deviceName);
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                } else {
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    // so we don't try to open it again
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mIgnoredDevices.add(deviceName);
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return null;
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Closes all resources related to the MtpClient object
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void close() {
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContext.unregisterReceiver(mUsbReceiver);
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
2237817979db0c52ffeacb951625b1e821eba303285Ahbong Chang     * Registers a {@link com.android.gallery3d.data.MtpClient.Listener} interface to receive
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * notifications when MTP or PTP devices are added or removed.
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param listener the listener to register
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void addListener(Listener listener) {
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mDevices) {
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (!mListeners.contains(listener)) {
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mListeners.add(listener);
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
2377817979db0c52ffeacb951625b1e821eba303285Ahbong Chang     * Unregisters a {@link com.android.gallery3d.data.MtpClient.Listener} interface.
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param listener the listener to unregister
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void removeListener(Listener listener) {
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mDevices) {
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mListeners.remove(listener);
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Retrieves an {@link android.mtp.MtpDevice} object for the USB device
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * with the given name.
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the MtpDevice, or null if it does not exist
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MtpDevice getDevice(String deviceName) {
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mDevices) {
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return mDevices.get(deviceName);
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Retrieves an {@link android.mtp.MtpDevice} object for the USB device
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * with the given ID.
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param id the ID of the USB device
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the MtpDevice, or null if it does not exist
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MtpDevice getDevice(int id) {
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mDevices) {
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return mDevices.get(UsbDevice.getDeviceName(id));
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Retrieves a list of all currently connected {@link android.mtp.MtpDevice}.
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the list of MtpDevices
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public List<MtpDevice> getDeviceList() {
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mDevices) {
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Query the USB manager since devices might have attached
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // before we added our listener.
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mDevices.get(usbDevice.getDeviceName()) == null) {
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    openDeviceLocked(usbDevice);
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return new ArrayList<MtpDevice>(mDevices.values());
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Retrieves a list of all {@link android.mtp.MtpStorageInfo}
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * for the MTP or PTP device with the given USB device name
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the list of MtpStorageInfo
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public List<MtpStorageInfo> getStorageList(String deviceName) {
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MtpDevice device = getDevice(deviceName);
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (device == null) {
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int[] storageIds = device.getStorageIds();
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (storageIds == null) {
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int length = storageIds.length;
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ArrayList<MtpStorageInfo> storageList = new ArrayList<MtpStorageInfo>(length);
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < length; i++) {
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            MtpStorageInfo info = device.getStorageInfo(storageIds[i]);
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (info == null) {
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                Log.w(TAG, "getStorageInfo failed");
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                storageList.add(info);
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return storageList;
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Retrieves the {@link android.mtp.MtpObjectInfo} for an object on
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * the MTP or PTP device with the given USB device name with the given
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * object handle
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param objectHandle handle of the object to query
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the MtpObjectInfo
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MtpObjectInfo getObjectInfo(String deviceName, int objectHandle) {
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MtpDevice device = getDevice(deviceName);
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (device == null) {
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return device.getObjectInfo(objectHandle);
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Deletes an object on the MTP or PTP device with the given USB device name.
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param objectHandle handle of the object to delete
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return true if the deletion succeeds
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean deleteObject(String deviceName, int objectHandle) {
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MtpDevice device = getDevice(deviceName);
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (device == null) {
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return false;
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return device.deleteObject(objectHandle);
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Retrieves a list of {@link android.mtp.MtpObjectInfo} for all objects
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * on the MTP or PTP device with the given USB device name and given storage ID
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * and/or object handle.
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * If the object handle is zero, then all objects in the root of the storage unit
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * will be returned. Otherwise, all immediate children of the object will be returned.
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * If the storage ID is also zero, then all objects on all storage units will be returned.
361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param storageId the ID of the storage unit to query, or zero for all
364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param objectHandle the handle of the parent object to query, or zero for the storage root
365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the list of MtpObjectInfo
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public List<MtpObjectInfo> getObjectList(String deviceName, int storageId, int objectHandle) {
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MtpDevice device = getDevice(deviceName);
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (device == null) {
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (objectHandle == 0) {
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // all objects in root of storage
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            objectHandle = 0xFFFFFFFF;
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int[] handles = device.getObjectHandles(storageId, 0, objectHandle);
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (handles == null) {
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int length = handles.length;
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ArrayList<MtpObjectInfo> objectList = new ArrayList<MtpObjectInfo>(length);
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < length; i++) {
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            MtpObjectInfo info = device.getObjectInfo(handles[i]);
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (info == null) {
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                Log.w(TAG, "getObjectInfo failed");
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                objectList.add(info);
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return objectList;
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Returns the data for an object as a byte array.
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device containing the object
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param objectHandle handle of the object to read
399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param objectSize the size of the object (this should match
400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *      {@link android.mtp.MtpObjectInfo#getCompressedSize}
401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the object's data, or null if reading fails
402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public byte[] getObject(String deviceName, int objectHandle, int objectSize) {
404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MtpDevice device = getDevice(deviceName);
405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (device == null) {
406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return device.getObject(objectHandle, objectSize);
409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Returns the thumbnail data for an object as a byte array.
413f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device containing the object
415f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param objectHandle handle of the object to read
416f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return the object's thumbnail, or null if reading fails
417f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
418f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public byte[] getThumbnail(String deviceName, int objectHandle) {
419f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MtpDevice device = getDevice(deviceName);
420f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (device == null) {
421f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
422f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
423f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return device.getThumbnail(objectHandle);
424f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
425f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
426f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
427f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Copies the data for an object to a file in external storage.
428f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
429f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param deviceName the name of the USB device containing the object
430f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param objectHandle handle of the object to read
431f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @param destPath path to destination for the file transfer.
432f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *      This path should be in the external storage as defined by
433f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *      {@link android.os.Environment#getExternalStorageDirectory}
434f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * @return true if the file transfer succeeds
435f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
436f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean importFile(String deviceName, int objectHandle, String destPath) {
437f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MtpDevice device = getDevice(deviceName);
438f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (device == null) {
439f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return false;
440f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
441f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return device.importFile(objectHandle, destPath);
442f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
443f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
444