MtpDocumentsService.java revision a57d9ed09003acd8b2beb0494a2bd32f7030cc11
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.mtp; 18 19import android.app.Notification; 20import android.app.Service; 21import android.app.NotificationManager; 22import android.content.Intent; 23import android.hardware.usb.UsbDevice; 24import android.os.IBinder; 25import android.util.Log; 26 27import com.android.internal.util.Preconditions; 28 29import java.io.IOException; 30 31/** 32 * Service to manage lifetime of DocumentsProvider's process. 33 * The service prevents the system from killing the process that holds USB connections. The service 34 * starts to run when the first MTP device is opened, and stops when the last MTP device is closed. 35 */ 36public class MtpDocumentsService extends Service { 37 static final String ACTION_OPEN_DEVICE = "com.android.mtp.OPEN_DEVICE"; 38 static final String ACTION_CLOSE_DEVICE = "com.android.mtp.CLOSE_DEVICE"; 39 static final String EXTRA_DEVICE = "device"; 40 private static final int FOREGROUND_NOTIFICATION_ID = 1; 41 42 NotificationManager mNotificationManager; 43 44 @Override 45 public IBinder onBind(Intent intent) { 46 // The service is used via intents. 47 return null; 48 } 49 50 @Override 51 public void onCreate() { 52 super.onCreate(); 53 mNotificationManager = getSystemService(NotificationManager.class); 54 } 55 56 @Override 57 public int onStartCommand(Intent intent, int flags, int startId) { 58 // If intent is null, the service was restarted. 59 if (intent != null) { 60 final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); 61 final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE); 62 try { 63 Preconditions.checkNotNull(device); 64 switch (intent.getAction()) { 65 case ACTION_OPEN_DEVICE: 66 provider.openDevice(device.getDeviceId()); 67 break; 68 69 case ACTION_CLOSE_DEVICE: 70 provider.closeDevice(device.getDeviceId()); 71 break; 72 73 default: 74 throw new IllegalArgumentException("Received unknown intent action."); 75 } 76 } catch (IOException | InterruptedException | IllegalArgumentException error) { 77 logErrorMessage(error); 78 } 79 } else { 80 // TODO: Fetch devices again. 81 } 82 83 return updateForegroundState() ? START_STICKY : START_NOT_STICKY; 84 } 85 86 /** 87 * Updates the foreground state of the service. 88 * @return Whether the service is foreground or not. 89 */ 90 private boolean updateForegroundState() { 91 final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); 92 final int[] deviceIds = provider.getOpenedDeviceIds(); 93 String message = null; 94 if (deviceIds.length != 0) { 95 // TODO: Localize the message. 96 // TODO: Add buttons "Open in Files" and "Open in Apps" if needed. 97 if (deviceIds.length > 1) { 98 message = deviceIds.length + " devices are being connected."; 99 } else { 100 try { 101 message = provider.getDeviceName(deviceIds[0]) + " is being connected."; 102 } catch (IOException exp) { 103 logErrorMessage(exp); 104 // If we failed to obtain device name, it looks the device is unusable. 105 // Because this is the last device we opened, we should hide the notification 106 // for the case. 107 try { 108 provider.closeDevice(deviceIds[0]); 109 } catch (IOException | InterruptedException closeError) { 110 logErrorMessage(closeError); 111 } 112 } 113 } 114 } 115 if (message != null) { 116 final Notification notification = new Notification.Builder(this) 117 .setContentTitle(message) 118 .setSmallIcon(android.R.drawable.ic_menu_camera) 119 .setCategory(Notification.CATEGORY_SYSTEM) 120 .setPriority(Notification.PRIORITY_LOW) 121 .build(); 122 startForeground(FOREGROUND_NOTIFICATION_ID, notification); 123 return true; 124 } else { 125 stopForeground(true /* removeNotification */); 126 stopSelf(); 127 return false; 128 } 129 } 130 131 private static void logErrorMessage(Exception exp) { 132 if (exp.getMessage() != null) { 133 Log.e(MtpDocumentsProvider.TAG, exp.getMessage()); 134 } else { 135 Log.e(MtpDocumentsProvider.TAG, exp.toString()); 136 } 137 } 138} 139