1b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono/*
2b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * Copyright (C) 2015 The Android Open Source Project
3b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono *
4b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * Licensed under the Apache License, Version 2.0 (the "License");
5b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * you may not use this file except in compliance with the License.
6b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * You may obtain a copy of the License at
7b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono *
8b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono *      http://www.apache.org/licenses/LICENSE-2.0
9b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono *
10b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * Unless required by applicable law or agreed to in writing, software
11b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * distributed under the License is distributed on an "AS IS" BASIS,
12b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * See the License for the specific language governing permissions and
14b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * limitations under the License.
15b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono */
16b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono
17b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hironopackage com.android.mtp;
18b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono
19b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hironoimport android.hardware.usb.UsbDevice;
20b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hironoimport android.hardware.usb.UsbDeviceConnection;
21b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hironoimport android.hardware.usb.UsbManager;
220f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hironoimport android.mtp.MtpConstants;
231d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hironoimport android.os.SystemClock;
24b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono
25f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hironoimport java.io.FileNotFoundException;
26b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hironoimport java.io.IOException;
27b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hironoimport java.util.HashMap;
281d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hironoimport java.util.Objects;
291d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono
30b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono/**
31b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono * Static utility methods for testing.
32b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono */
3399b58052f85c18272e63047b471edfd8089c09d3Daichi Hironofinal class TestUtil {
34b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    private TestUtil() {}
35b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono
360f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono    static final int[] OPERATIONS_SUPPORTED = new int[] {
370f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono            MtpConstants.OPERATION_GET_PARTIAL_OBJECT,
380f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono            MtpConstants.OPERATION_SEND_OBJECT,
390f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono            MtpConstants.OPERATION_SEND_OBJECT_INFO,
4061ba923ca0cb5c928a16729d0aa67b6bf4b2f027Daichi Hirono            MtpConstants.OPERATION_DELETE_OBJECT,
4164111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono            MtpConstants.OPERATION_GET_OBJECT_PROP_DESC,
4264111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono            MtpConstants.OPERATION_GET_OBJECT_PROP_VALUE
430f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono    };
440f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono
45b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    /**
46b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono     * Requests permission for a MTP device and returns the first MTP device that has at least one
47b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono     * storage.
48b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono     */
49b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    static UsbDevice setupMtpDevice(
50b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            TestResultInstrumentation instrumentation,
51b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            UsbManager usbManager,
521d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono            MtpManager manager) {
531d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono        while (true) {
54b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            try {
55ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono                final UsbDevice device = findMtpDevice(usbManager, manager);
56b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                waitForStorages(instrumentation, manager, device.getDeviceId());
57b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                return device;
58b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            } catch (IOException exp) {
591d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono                instrumentation.show(Objects.toString(exp.getMessage()));
601d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono                SystemClock.sleep(1000);
61b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                // When the MTP device is Android, and it changes the USB device type from
62b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                // "Charging" to "MTP", the device ID will be updated. We need to find a device
63b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                // again.
64b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                continue;
65b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            }
66b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono        }
67b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    }
68b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono
69f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono    static void addTestDevice(MtpDatabase database) throws FileNotFoundException {
70f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono        database.getMapper().startAddingDocuments(null);
71f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono        database.getMapper().putDeviceDocument(new MtpDeviceRecord(
720f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono                0, "Device", "device_key", /* opened is */ true, new MtpRoot[0],
730f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono                OPERATIONS_SUPPORTED, null));
74f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono        database.getMapper().stopAddingDocuments(null);
75f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono    }
76f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono
77f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono    static void addTestStorage(MtpDatabase database, String parentId) throws FileNotFoundException {
78f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono        database.getMapper().startAddingDocuments(parentId);
790f32537e40ee2662d4f0b7b625ee280ca9c02066Daichi Hirono        database.getMapper().putStorageDocuments(parentId, OPERATIONS_SUPPORTED, new MtpRoot[] {
80f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono                new MtpRoot(0, 100, "Storage", 1024, 1024, ""),
81f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono        });
82f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono        database.getMapper().stopAddingDocuments(parentId);
83f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono    }
84f578fa275a535016f5322c88ad7a92e517d04a12Daichi Hirono
85b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    private static UsbDevice findMtpDevice(
86af5ea38b8c51f0878e4070671e240f693f3ad796Daichi Hirono            UsbManager usbManager,
87ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono            MtpManager manager) throws IOException {
88ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        final HashMap<String,UsbDevice> devices = usbManager.getDeviceList();
89ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        if (devices.size() == 0) {
90ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono            throw new IOException("Device not found.");
91ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        }
92ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        final UsbDevice device = devices.values().iterator().next();
93ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        // Tries to get ownership of the device in case that another application use it.
94ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        if (usbManager.hasPermission(device)) {
95ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono            final UsbDeviceConnection connection = usbManager.openDevice(device);
96ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono            for (int i = 0; i < device.getInterfaceCount(); i++) {
97ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono                // Since the test runs real environment, we need to call claim interface with
98ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono                // force = true to rob interfaces from other applications.
99ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono                connection.claimInterface(device.getInterface(i), true);
100ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono                connection.releaseInterface(device.getInterface(i));
101b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            }
102ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono            connection.close();
103b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono        }
104ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        manager.openDevice(device.getDeviceId());
105ab03cb1b469940ab672850e0d2de3c78025260d3Daichi Hirono        return device;
106b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    }
107b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono
108b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    private static void waitForStorages(
109b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            TestResultInstrumentation instrumentation,
110b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            MtpManager manager,
1111d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono            int deviceId) throws IOException {
112b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono        while (true) {
11320754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono            MtpDeviceRecord device = null;
11420754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono            for (final MtpDeviceRecord deviceCandidate : manager.getDevices()) {
11520754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono                if (deviceCandidate.deviceId == deviceId) {
11620754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono                    device = deviceCandidate;
11720754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono                    break;
11820754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono                }
11920754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono            }
12020754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono            if (device == null) {
12120754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono                throw new IOException("Device was detached.");
12220754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono            }
12320754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono            if (device.roots.length == 0) {
124b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                instrumentation.show("Wait for storages.");
1251d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono                SystemClock.sleep(1000);
126b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono                continue;
127b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            }
128b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono            return;
129b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono        }
130b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    }
131b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono}
132