15bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono/*
25bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * Copyright (C) 2015 The Android Open Source Project
35bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono *
45bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * Licensed under the Apache License, Version 2.0 (the "License");
55bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * you may not use this file except in compliance with the License.
65bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * You may obtain a copy of the License at
75bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono *
85bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono *      http://www.apache.org/licenses/LICENSE-2.0
95bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono *
105bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * Unless required by applicable law or agreed to in writing, software
115bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * distributed under the License is distributed on an "AS IS" BASIS,
125bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * See the License for the specific language governing permissions and
145bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono * limitations under the License.
155bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono */
165bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono
175bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hironopackage com.android.mtp;
185bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono
195bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hironoimport android.content.Context;
205bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hironoimport android.hardware.usb.UsbDevice;
215bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hironoimport android.hardware.usb.UsbManager;
222a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hironoimport android.mtp.MtpConstants;
232a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hironoimport android.mtp.MtpEvent;
24d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hironoimport android.mtp.MtpObjectInfo;
250b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hironoimport android.os.CancellationSignal;
260b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hironoimport android.os.OperationCanceledException;
27d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hironoimport android.os.ParcelFileDescriptor;
281d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hironoimport android.os.SystemClock;
295bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hironoimport android.test.InstrumentationTestCase;
305bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono
310b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hironoimport java.io.IOException;
321d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hironoimport java.util.Arrays;
3399b58052f85c18272e63047b471edfd8089c09d3Daichi Hironoimport java.util.concurrent.Callable;
3499b58052f85c18272e63047b471edfd8089c09d3Daichi Hironoimport java.util.concurrent.FutureTask;
3599b58052f85c18272e63047b471edfd8089c09d3Daichi Hironoimport java.util.concurrent.TimeUnit;
365bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono
370b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono@RealDeviceTest
385bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hironopublic class MtpManagerTest extends InstrumentationTestCase {
390b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    private static final int TIMEOUT_MS = 1000;
400b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    UsbManager mUsbManager;
410b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    MtpManager mManager;
420b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    UsbDevice mUsbDevice;
430b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono
440b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    @Override
450b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    public void setUp() throws Exception {
460b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono        mUsbManager = getContext().getSystemService(UsbManager.class);
47b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono        mManager = new MtpManager(getContext());
48b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono        mUsbDevice = TestUtil.setupMtpDevice(getInstrumentation(), mUsbManager, mManager);
490b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    }
500b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono
510b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    @Override
520b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    public void tearDown() throws IOException {
530b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono        mManager.closeDevice(mUsbDevice.getDeviceId());
540b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    }
550b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono
56b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    @Override
57b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    public TestResultInstrumentation getInstrumentation() {
58b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono        return (TestResultInstrumentation) super.getInstrumentation();
59b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono    }
60b255f58904d7a2aa64ba9f03ed0ede3759fd03c5Daichi Hirono
610b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    public void testCancelEvent() throws Exception {
620b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono        final CancellationSignal signal = new CancellationSignal();
6399b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono        final FutureTask<Boolean> future = new FutureTask<Boolean>(
6499b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                new Callable<Boolean>() {
6599b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                    @Override
6699b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                    public Boolean call() throws IOException {
6799b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                        try {
684c4061210e52b7b1786ecb26a2dd7f8c1298e752Daichi Hirono                            while (true) {
694c4061210e52b7b1786ecb26a2dd7f8c1298e752Daichi Hirono                                mManager.readEvent(mUsbDevice.getDeviceId(), signal);
704c4061210e52b7b1786ecb26a2dd7f8c1298e752Daichi Hirono                            }
7199b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                        } catch (OperationCanceledException exception) {
7299b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                            return true;
7399b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                        }
7499b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                    }
7599b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono                });
7699b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono        final Thread thread = new Thread(future);
770b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono        thread.start();
781d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono        SystemClock.sleep(TIMEOUT_MS);
790b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono        signal.cancel();
8099b58052f85c18272e63047b471edfd8089c09d3Daichi Hirono        assertTrue(future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
810b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono    }
820b494663a4cb177fc6f05988c9bda2ef6277333dDaichi Hirono
831d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono    public void testOperationsSupported() {
841d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono        final MtpDeviceRecord[] records = mManager.getDevices();
851d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono        assertEquals(1, records.length);
861d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono        assertNotNull(records[0].operationsSupported);
871d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono        getInstrumentation().show(Arrays.toString(records[0].operationsSupported));
881d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono    }
891d4779c29a95114c89ec353a8899c0cc8eee3ba5Daichi Hirono
90148954a657941ea95ef17da5b3ce40b9145f8755Daichi Hirono    public void testEventsSupported() {
91148954a657941ea95ef17da5b3ce40b9145f8755Daichi Hirono        final MtpDeviceRecord[] records = mManager.getDevices();
92148954a657941ea95ef17da5b3ce40b9145f8755Daichi Hirono        assertEquals(1, records.length);
93148954a657941ea95ef17da5b3ce40b9145f8755Daichi Hirono        assertNotNull(records[0].eventsSupported);
94148954a657941ea95ef17da5b3ce40b9145f8755Daichi Hirono        getInstrumentation().show(Arrays.toString(records[0].eventsSupported));
95148954a657941ea95ef17da5b3ce40b9145f8755Daichi Hirono    }
96148954a657941ea95ef17da5b3ce40b9145f8755Daichi Hirono
97637a2010f4a0c0484b13c4cb87aa2858bdf079b2Daichi Hirono    public void testDeviceKey() {
98637a2010f4a0c0484b13c4cb87aa2858bdf079b2Daichi Hirono        final MtpDeviceRecord[] records = mManager.getDevices();
99637a2010f4a0c0484b13c4cb87aa2858bdf079b2Daichi Hirono        assertEquals(1, records.length);
100637a2010f4a0c0484b13c4cb87aa2858bdf079b2Daichi Hirono        assertNotNull(records[0].deviceKey);
101637a2010f4a0c0484b13c4cb87aa2858bdf079b2Daichi Hirono        getInstrumentation().show("deviceKey: " + records[0].deviceKey);
102637a2010f4a0c0484b13c4cb87aa2858bdf079b2Daichi Hirono    }
103637a2010f4a0c0484b13c4cb87aa2858bdf079b2Daichi Hirono
1042a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono    public void testEventObjectAdded() throws Exception {
1052a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono        while (true) {
1062a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono            getInstrumentation().show("Please take a photo by using connected MTP device.");
1072a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono            final CancellationSignal signal = new CancellationSignal();
1082a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono            MtpEvent event = mManager.readEvent(mUsbDevice.getDeviceId(), signal);
10986c1a9bf6081d23c82f05f7293aded3266f61368Daichi Hirono            if (event.getEventCode() != MtpEvent.EVENT_OBJECT_ADDED) {
1102a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono                continue;
1112a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono            }
1122a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono            assertTrue(event.getObjectHandle() != 0);
1132a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono            break;
1142a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono        }
1152a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono    }
1162a9a43369b4717bcf6b372f6798f72e80e938e30Daichi Hirono
117d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono    public void testCreateDocumentAndGetPartialObject() throws Exception {
118d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        int storageId = 0;
119d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        for (final MtpDeviceRecord record : mManager.getDevices()) {
120d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono            if (record.deviceId == mUsbDevice.getDeviceId()) {
121d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                storageId = record.roots[0].mStorageId;
122d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                break;
123d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono            }
124d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        }
125d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        assertTrue("Valid storage not found.", storageId != 0);
1263a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono
127d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        final String testFileName = "MtpManagerTest_testFile.txt";
128d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        for (final int handle : mManager.getObjectHandles(
129d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                mUsbDevice.getDeviceId(), storageId, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN)) {
130d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono            if (mManager.getObjectInfo(mUsbDevice.getDeviceId(), handle)
131d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                    .getName().equals(testFileName)) {
132d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                mManager.deleteDocument(mUsbDevice.getDeviceId(), handle);
133d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                break;
134d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono            }
135d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        }
1363a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono
1373a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
138d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        final byte[] expectedBytes = "Hello Android!".getBytes("ascii");
1393a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
1403a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                new ParcelFileDescriptor.AutoCloseOutputStream(fds[1])) {
141d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono            stream.write(expectedBytes);
142d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        }
1433a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono        final int objectHandle = mManager.createDocument(
1443a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                mUsbDevice.getDeviceId(),
1453a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                new MtpObjectInfo.Builder()
1463a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                        .setStorageId(storageId)
1473a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                        .setName(testFileName)
1483a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                        .setCompressedSize(expectedBytes.length)
1493a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                        .setFormat(MtpConstants.FORMAT_TEXT)
1503a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                        .build(),
1513a6212ae558c945d6f24ace8fd38ab043a7697d1Daichi Hirono                fds[0]);
152d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        final byte[] bytes = new byte[100];
153d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        assertEquals(5, mManager.getPartialObject(
154d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                mUsbDevice.getDeviceId(), objectHandle, 0, 5, bytes));
155d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        assertEquals("Hello", new String(bytes, 0, 5, "ascii"));
156d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        assertEquals(8, mManager.getPartialObject(
157d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono                mUsbDevice.getDeviceId(), objectHandle, 6, 100, bytes));
158d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono        assertEquals("Android!", new String(bytes, 0, 8, "ascii"));
159d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono    }
160d426ed27857a444c5328613769e4c9b1e7a24c81Daichi Hirono
1615bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono    private Context getContext() {
1625bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono        return getInstrumentation().getContext();
1635bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono    }
1645bc41d18544d983d913bd42e067bc8b7db0378cdDaichi Hirono}
165