Open MTP device on demand.
Previously MtpDocumentsProvider opens a device just after device is connected to Android. But MtpDocumentsProvider should open MTP device on demand so that other applications can open device if user starts to use the application before using MtpDocumentsProvider. BUG=26625708 Change-Id: I6083b8c7cef49ee6e9fb0d15ca4adc129734f3eb
This commit is contained in:
@@ -30,16 +30,12 @@
|
||||
</activity>
|
||||
|
||||
<receiver android:name=".UsbIntentReceiver" android:exported="true">
|
||||
<!-- TODO: Remove an intent-filter for USB_DEVICE_ATTACHED after the provider becomes to
|
||||
open devices on demand. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||
android:resource="@xml/device_filter" />
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -70,6 +70,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
private Resources mResources;
|
||||
private MtpDatabase mDatabase;
|
||||
private AppFuse mAppFuse;
|
||||
private ServiceIntentSender mIntentSender;
|
||||
|
||||
/**
|
||||
* Provides singleton instance to MtpDocumentsService.
|
||||
@@ -88,6 +89,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
|
||||
mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
|
||||
mAppFuse = new AppFuse(TAG, new AppFuseCallback());
|
||||
mIntentSender = new ServiceIntentSender(getContext());
|
||||
// TODO: Mount AppFuse on demands.
|
||||
try {
|
||||
mAppFuse.mount(getContext().getSystemService(StorageManager.class));
|
||||
@@ -105,7 +107,8 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
MtpManager mtpManager,
|
||||
ContentResolver resolver,
|
||||
MtpDatabase database,
|
||||
StorageManager storageManager) {
|
||||
StorageManager storageManager,
|
||||
ServiceIntentSender intentSender) {
|
||||
mResources = resources;
|
||||
mMtpManager = mtpManager;
|
||||
mResolver = resolver;
|
||||
@@ -113,6 +116,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
mDatabase = database;
|
||||
mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
|
||||
mAppFuse = new AppFuse(TAG, new AppFuseCallback());
|
||||
mIntentSender = intentSender;
|
||||
// TODO: Mount AppFuse on demands.
|
||||
try {
|
||||
mAppFuse.mount(storageManager);
|
||||
@@ -152,6 +156,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
}
|
||||
Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId);
|
||||
try {
|
||||
openDevice(parentIdentifier.mDeviceId);
|
||||
if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) {
|
||||
final Identifier singleStorageIdentifier =
|
||||
mDatabase.getSingleStorageIdentifier(parentDocumentId);
|
||||
@@ -176,11 +181,12 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
throws FileNotFoundException {
|
||||
final Identifier identifier = mDatabase.createIdentifier(documentId);
|
||||
try {
|
||||
openDevice(identifier.mDeviceId);
|
||||
switch (mode) {
|
||||
case "r":
|
||||
final long fileSize = getFileSize(documentId);
|
||||
// MTP getPartialObject operation does not support files that are larger than 4GB.
|
||||
// Fallback to non-seekable file descriptor.
|
||||
// MTP getPartialObject operation does not support files that are larger than
|
||||
// 4GB. Fallback to non-seekable file descriptor.
|
||||
// TODO: Use getPartialObject64 for MTP devices that support Android vendor
|
||||
// extension.
|
||||
if (fileSize <= 0xffffffffl) {
|
||||
@@ -213,6 +219,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
CancellationSignal signal) throws FileNotFoundException {
|
||||
final Identifier identifier = mDatabase.createIdentifier(documentId);
|
||||
try {
|
||||
openDevice(identifier.mDeviceId);
|
||||
return new AssetFileDescriptor(
|
||||
getPipeManager(identifier).readThumbnail(mMtpManager, identifier),
|
||||
0, // Start offset.
|
||||
@@ -227,6 +234,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
public void deleteDocument(String documentId) throws FileNotFoundException {
|
||||
try {
|
||||
final Identifier identifier = mDatabase.createIdentifier(documentId);
|
||||
openDevice(identifier.mDeviceId);
|
||||
final Identifier parentIdentifier = mDatabase.getParentIdentifier(documentId);
|
||||
mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
|
||||
mDatabase.deleteDocument(documentId);
|
||||
@@ -259,6 +267,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
throws FileNotFoundException {
|
||||
try {
|
||||
final Identifier parentId = mDatabase.createIdentifier(parentDocumentId);
|
||||
openDevice(parentId.mDeviceId);
|
||||
final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe();
|
||||
pipe[0].close(); // 0 bytes for a new document.
|
||||
final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ?
|
||||
@@ -286,11 +295,19 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
|
||||
void openDevice(int deviceId) throws IOException {
|
||||
synchronized (mDeviceListLock) {
|
||||
if (mDeviceToolkits.containsKey(deviceId)) {
|
||||
return;
|
||||
}
|
||||
mMtpManager.openDevice(deviceId);
|
||||
mDeviceToolkits.put(
|
||||
deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase));
|
||||
mIntentSender.sendUpdateNotificationIntent();
|
||||
try {
|
||||
mRootScanner.resume().await();
|
||||
} catch (InterruptedException error) {
|
||||
Log.e(TAG, "openDevice", error);
|
||||
}
|
||||
}
|
||||
mRootScanner.resume();
|
||||
}
|
||||
|
||||
void closeDevice(int deviceId) throws IOException, InterruptedException {
|
||||
@@ -298,6 +315,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
closeDeviceInternal(deviceId);
|
||||
}
|
||||
mRootScanner.resume();
|
||||
mIntentSender.sendUpdateNotificationIntent();
|
||||
}
|
||||
|
||||
int[] getOpenedDeviceIds() {
|
||||
@@ -317,6 +335,13 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes root scanner to handle the update of device list.
|
||||
*/
|
||||
void resumeRootScanner() {
|
||||
mRootScanner.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize the content provider for unit tests.
|
||||
*/
|
||||
@@ -356,6 +381,9 @@ public class MtpDocumentsProvider extends DocumentsProvider {
|
||||
|
||||
private void closeDeviceInternal(int deviceId) throws IOException, InterruptedException {
|
||||
// TODO: Flush the device before closing (if not closed externally).
|
||||
if (!mDeviceToolkits.containsKey(deviceId)) {
|
||||
return;
|
||||
}
|
||||
getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
|
||||
mDeviceToolkits.remove(deviceId);
|
||||
mMtpManager.closeDevice(deviceId);
|
||||
|
||||
@@ -19,13 +19,12 @@ package com.android.mtp;
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@@ -36,6 +35,7 @@ import java.io.IOException;
|
||||
public class MtpDocumentsService extends Service {
|
||||
static final String ACTION_OPEN_DEVICE = "com.android.mtp.OPEN_DEVICE";
|
||||
static final String ACTION_CLOSE_DEVICE = "com.android.mtp.CLOSE_DEVICE";
|
||||
static final String ACTION_UPDATE_NOTIFICATION = "com.android.mtp.UPDATE_NOTIFICATION";
|
||||
static final String EXTRA_DEVICE = "device";
|
||||
|
||||
NotificationManager mNotificationManager;
|
||||
@@ -55,32 +55,10 @@ public class MtpDocumentsService extends Service {
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
// If intent is null, the service was restarted.
|
||||
if (intent != null) {
|
||||
final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
|
||||
final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE);
|
||||
try {
|
||||
Preconditions.checkNotNull(device);
|
||||
switch (intent.getAction()) {
|
||||
case ACTION_OPEN_DEVICE:
|
||||
provider.openDevice(device.getDeviceId());
|
||||
break;
|
||||
|
||||
case ACTION_CLOSE_DEVICE:
|
||||
mNotificationManager.cancel(device.getDeviceId());
|
||||
provider.closeDevice(device.getDeviceId());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Received unknown intent action.");
|
||||
}
|
||||
} catch (IOException | InterruptedException | IllegalArgumentException error) {
|
||||
logErrorMessage(error);
|
||||
}
|
||||
} else {
|
||||
// TODO: Fetch devices again.
|
||||
if (intent == null || ACTION_UPDATE_NOTIFICATION.equals(intent.getAction())) {
|
||||
return updateForegroundState() ? START_STICKY : START_NOT_STICKY;
|
||||
}
|
||||
|
||||
return updateForegroundState() ? START_STICKY : START_NOT_STICKY;
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +70,7 @@ public class MtpDocumentsService extends Service {
|
||||
final int[] deviceIds = provider.getOpenedDeviceIds();
|
||||
int notificationId = 0;
|
||||
Notification notification = null;
|
||||
// TODO: Hide notification if the device has already been removed.
|
||||
for (final int deviceId : deviceIds) {
|
||||
try {
|
||||
final String title = getResources().getString(
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.mtp;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
@@ -7,6 +23,7 @@ import android.os.Process;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
@@ -64,9 +81,8 @@ final class RootScanner {
|
||||
|
||||
/**
|
||||
* Starts to check new changes right away.
|
||||
* If the background thread has already gone, it restarts another background thread.
|
||||
*/
|
||||
synchronized void resume() {
|
||||
synchronized CountDownLatch resume() {
|
||||
if (mExecutor == null) {
|
||||
// Only single thread updates the database.
|
||||
mExecutor = Executors.newSingleThreadExecutor();
|
||||
@@ -75,8 +91,10 @@ final class RootScanner {
|
||||
// Cancel previous task.
|
||||
mCurrentTask.cancel(true);
|
||||
}
|
||||
mCurrentTask = new FutureTask<Void>(new UpdateRootsRunnable(), null);
|
||||
final UpdateRootsRunnable runnable = new UpdateRootsRunnable();
|
||||
mCurrentTask = new FutureTask<Void>(runnable, null);
|
||||
mExecutor.submit(mCurrentTask);
|
||||
return runnable.mFirstScanCompleted;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,6 +116,8 @@ final class RootScanner {
|
||||
* Runnable to scan roots and update the database information.
|
||||
*/
|
||||
private final class UpdateRootsRunnable implements Runnable {
|
||||
final CountDownLatch mFirstScanCompleted = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
@@ -136,6 +156,7 @@ final class RootScanner {
|
||||
if (changed) {
|
||||
notifyChange();
|
||||
}
|
||||
mFirstScanCompleted.countDown();
|
||||
pollingCount++;
|
||||
try {
|
||||
// Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.mtp;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Sends intent to MtpDocumentsService.
|
||||
*/
|
||||
class ServiceIntentSender {
|
||||
private Context mContext;
|
||||
|
||||
ServiceIntentSender(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
void sendUpdateNotificationIntent() {
|
||||
final Intent intent = new Intent(MtpDocumentsService.ACTION_UPDATE_NOTIFICATION);
|
||||
intent.setComponent(new ComponentName(mContext, MtpDocumentsService.class));
|
||||
mContext.startService(intent);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class UsbIntentReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
@@ -29,17 +31,15 @@ public class UsbIntentReceiver extends BroadcastReceiver {
|
||||
final UsbDevice device = intent.getExtras().getParcelable(UsbManager.EXTRA_DEVICE);
|
||||
switch (intent.getAction()) {
|
||||
case UsbManager.ACTION_USB_DEVICE_ATTACHED:
|
||||
startService(context, MtpDocumentsService.ACTION_OPEN_DEVICE, device);
|
||||
MtpDocumentsProvider.getInstance().resumeRootScanner();
|
||||
break;
|
||||
case UsbManager.ACTION_USB_DEVICE_DETACHED:
|
||||
startService(context, MtpDocumentsService.ACTION_CLOSE_DEVICE, device);
|
||||
try {
|
||||
MtpDocumentsProvider.getInstance().closeDevice(device.getDeviceId());
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.e(MtpDocumentsProvider.TAG, "Failed to close device", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startService(Context context, String action, UsbDevice device) {
|
||||
final Intent intent = new Intent(action, Uri.EMPTY, context, MtpDocumentsService.class);
|
||||
intent.putExtra(MtpDocumentsService.EXTRA_DEVICE, device);
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,11 @@ import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -79,11 +77,14 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
null,
|
||||
null));
|
||||
|
||||
mProvider.openDevice(0);
|
||||
mProvider.resumeRootScanner();
|
||||
mResolver.waitForNotification(ROOTS_URI, 1);
|
||||
|
||||
mProvider.closeDevice(0);
|
||||
mProvider.openDevice(0);
|
||||
mResolver.waitForNotification(ROOTS_URI, 2);
|
||||
|
||||
mProvider.closeDevice(0);
|
||||
mResolver.waitForNotification(ROOTS_URI, 3);
|
||||
}
|
||||
|
||||
public void testOpenAndCloseErrorDevice() throws Exception {
|
||||
@@ -94,13 +95,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
} catch (Throwable error) {
|
||||
assertTrue(error instanceof IOException);
|
||||
}
|
||||
|
||||
try {
|
||||
mProvider.closeDevice(1);
|
||||
fail();
|
||||
} catch (Throwable error) {
|
||||
assertTrue(error instanceof IOException);
|
||||
}
|
||||
assertEquals(0, mProvider.getOpenedDeviceIds().length);
|
||||
|
||||
// Check if the following notification is the first one or not.
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
@@ -119,8 +114,55 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
},
|
||||
null,
|
||||
null));
|
||||
mProvider.openDevice(0);
|
||||
mProvider.resumeRootScanner();
|
||||
mResolver.waitForNotification(ROOTS_URI, 1);
|
||||
mProvider.openDevice(0);
|
||||
mResolver.waitForNotification(ROOTS_URI, 2);
|
||||
}
|
||||
|
||||
public void testOpenDeviceOnDemand() throws Exception {
|
||||
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
0,
|
||||
"Device A",
|
||||
false /* unopened */,
|
||||
new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
0 /* deviceId */,
|
||||
1 /* storageId */,
|
||||
"Device A" /* device model name */,
|
||||
"Storage A" /* volume description */,
|
||||
1024 /* free space */,
|
||||
2048 /* total space */,
|
||||
"" /* no volume identifier */)
|
||||
},
|
||||
null,
|
||||
null));
|
||||
mMtpManager.setObjectHandles(0, 1, -1, new int[0]);
|
||||
mProvider.resumeRootScanner();
|
||||
mResolver.waitForNotification(ROOTS_URI, 1);
|
||||
final String[] columns = new String[] {
|
||||
DocumentsContract.Root.COLUMN_TITLE,
|
||||
DocumentsContract.Root.COLUMN_DOCUMENT_ID
|
||||
};
|
||||
try (final Cursor cursor = mProvider.queryRoots(columns)) {
|
||||
assertEquals(1, cursor.getCount());
|
||||
assertTrue(cursor.moveToNext());
|
||||
assertEquals("Device A", cursor.getString(0));
|
||||
assertEquals(1, cursor.getLong(1));
|
||||
}
|
||||
{
|
||||
final int [] openedDevice = mProvider.getOpenedDeviceIds();
|
||||
assertEquals(0, openedDevice.length);
|
||||
}
|
||||
// Device is opened automatically when querying its children.
|
||||
try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {}
|
||||
|
||||
{
|
||||
final int [] openedDevice = mProvider.getOpenedDeviceIds();
|
||||
assertEquals(1, openedDevice.length);
|
||||
assertEquals(0, openedDevice[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void testQueryRoots() throws Exception {
|
||||
@@ -194,7 +236,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
|
||||
mMtpManager.addValidDevice(new MtpDeviceRecord(
|
||||
1,
|
||||
"Device",
|
||||
"Device B",
|
||||
false /* unopened */,
|
||||
new MtpRoot[] {
|
||||
new MtpRoot(
|
||||
@@ -210,9 +252,13 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
null));
|
||||
{
|
||||
mProvider.openDevice(0);
|
||||
mProvider.openDevice(1);
|
||||
mProvider.resumeRootScanner();
|
||||
mResolver.waitForNotification(ROOTS_URI, 1);
|
||||
|
||||
mProvider.openDevice(1);
|
||||
mProvider.resumeRootScanner();
|
||||
mResolver.waitForNotification(ROOTS_URI, 2);
|
||||
|
||||
final Cursor cursor = mProvider.queryRoots(null);
|
||||
assertEquals(2, cursor.getCount());
|
||||
|
||||
@@ -492,7 +538,12 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
|
||||
mProvider = new MtpDocumentsProvider();
|
||||
final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
|
||||
assertTrue(mProvider.onCreateForTesting(
|
||||
mResources, mMtpManager, mResolver, mDatabase, storageManager));
|
||||
mResources,
|
||||
mMtpManager,
|
||||
mResolver,
|
||||
mDatabase,
|
||||
storageManager,
|
||||
new TestServiceIntentSender()));
|
||||
}
|
||||
|
||||
private String[] getStrings(Cursor cursor) {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.mtp;
|
||||
|
||||
class TestServiceIntentSender extends ServiceIntentSender {
|
||||
TestServiceIntentSender() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendUpdateNotificationIntent() {}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbInterface;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Manager for MTP storage notification.
|
||||
|
||||
Reference in New Issue
Block a user