1 /* 2 * Copyright (C) 2010 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 17 package com.android.providers.media; 18 19 import android.app.KeyguardManager; 20 import android.app.Service; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.hardware.usb.UsbManager; 26 import android.mtp.MtpDatabase; 27 import android.mtp.MtpServer; 28 import android.mtp.MtpStorage; 29 import android.os.Environment; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.storage.StorageEventListener; 33 import android.os.storage.StorageManager; 34 import android.os.storage.StorageVolume; 35 import android.provider.Settings; 36 import android.util.Log; 37 38 import java.io.File; 39 import java.util.HashMap; 40 41 public class MtpService extends Service { 42 private static final String TAG = "MtpService"; 43 44 // We restrict PTP to these subdirectories 45 private static final String[] PTP_DIRECTORIES = new String[] { 46 Environment.DIRECTORY_DCIM, 47 Environment.DIRECTORY_PICTURES, 48 }; 49 50 private void addStorageDevicesLocked() { 51 if (mPtpMode) { 52 // In PTP mode we support only primary storage 53 addStorageLocked(mVolumeMap.get(mVolumes[0].getPath())); 54 } else { 55 for (StorageVolume volume : mVolumeMap.values()) { 56 addStorageLocked(volume); 57 } 58 } 59 } 60 61 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 62 @Override 63 public void onReceive(Context context, Intent intent) { 64 final String action = intent.getAction(); 65 if (Intent.ACTION_USER_PRESENT.equals(action)) { 66 synchronized (mBinder) { 67 // Unhide the storage units when the user has unlocked the lockscreen 68 if (mMtpDisabled) { 69 addStorageDevicesLocked(); 70 mMtpDisabled = false; 71 } 72 } 73 } 74 } 75 }; 76 77 private final StorageEventListener mStorageEventListener = new StorageEventListener() { 78 public void onStorageStateChanged(String path, String oldState, String newState) { 79 synchronized (mBinder) { 80 Log.d(TAG, "onStorageStateChanged " + path + " " + oldState + " -> " + newState); 81 if (Environment.MEDIA_MOUNTED.equals(newState)) { 82 volumeMountedLocked(path); 83 } else if (Environment.MEDIA_MOUNTED.equals(oldState)) { 84 StorageVolume volume = mVolumeMap.remove(path); 85 if (volume != null) { 86 removeStorageLocked(volume); 87 } 88 } 89 } 90 } 91 }; 92 93 private MtpDatabase mDatabase; 94 private MtpServer mServer; 95 private StorageManager mStorageManager; 96 private boolean mMtpDisabled; // true if MTP is disabled due to secure keyguard 97 private boolean mPtpMode; 98 private final HashMap<String, StorageVolume> mVolumeMap = new HashMap<String, StorageVolume>(); 99 private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>(); 100 private StorageVolume[] mVolumes; 101 102 @Override 103 public void onCreate() { 104 // lock MTP if the keyguard is locked and secure 105 KeyguardManager keyguardManager = 106 (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE); 107 mMtpDisabled = keyguardManager.isKeyguardLocked() && keyguardManager.isKeyguardSecure(); 108 registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_PRESENT)); 109 110 mStorageManager = (StorageManager)getSystemService(Context.STORAGE_SERVICE); 111 synchronized (mBinder) { 112 mStorageManager.registerListener(mStorageEventListener); 113 StorageVolume[] volumes = mStorageManager.getVolumeList(); 114 mVolumes = volumes; 115 for (int i = 0; i < volumes.length; i++) { 116 String path = volumes[i].getPath(); 117 String state = mStorageManager.getVolumeState(path); 118 if (Environment.MEDIA_MOUNTED.equals(state)) { 119 volumeMountedLocked(path); 120 } 121 } 122 } 123 } 124 125 @Override 126 public int onStartCommand(Intent intent, int flags, int startId) { 127 synchronized (mBinder) { 128 if (mServer == null) { 129 mPtpMode = (intent == null ? false 130 : intent.getBooleanExtra(UsbManager.USB_FUNCTION_PTP, false)); 131 Log.d(TAG, "starting MTP server in " + (mPtpMode ? "PTP mode" : "MTP mode")); 132 String[] subdirs = null; 133 if (mPtpMode) { 134 int count = PTP_DIRECTORIES.length; 135 subdirs = new String[count]; 136 for (int i = 0; i < count; i++) { 137 File file = 138 Environment.getExternalStoragePublicDirectory(PTP_DIRECTORIES[i]); 139 // make sure this directory exists 140 file.mkdirs(); 141 subdirs[i] = file.getPath(); 142 } 143 } 144 mDatabase = new MtpDatabase(this, MediaProvider.EXTERNAL_VOLUME, 145 mVolumes[0].getPath(), subdirs); 146 mServer = new MtpServer(mDatabase, mPtpMode); 147 if (!mMtpDisabled) { 148 addStorageDevicesLocked(); 149 } 150 mServer.start(); 151 } 152 } 153 154 return START_STICKY; 155 } 156 157 @Override 158 public void onDestroy() 159 { 160 unregisterReceiver(mReceiver); 161 mStorageManager.unregisterListener(mStorageEventListener); 162 } 163 164 private final IMtpService.Stub mBinder = 165 new IMtpService.Stub() { 166 public void sendObjectAdded(int objectHandle) { 167 synchronized (mBinder) { 168 if (mServer != null) { 169 mServer.sendObjectAdded(objectHandle); 170 } 171 } 172 } 173 174 public void sendObjectRemoved(int objectHandle) { 175 synchronized (mBinder) { 176 if (mServer != null) { 177 mServer.sendObjectRemoved(objectHandle); 178 } 179 } 180 } 181 }; 182 183 @Override 184 public IBinder onBind(Intent intent) 185 { 186 return mBinder; 187 } 188 189 private void volumeMountedLocked(String path) { 190 for (int i = 0; i < mVolumes.length; i++) { 191 StorageVolume volume = mVolumes[i]; 192 if (volume.getPath().equals(path)) { 193 mVolumeMap.put(path, volume); 194 if (!mMtpDisabled) { 195 // In PTP mode we support only primary storage 196 if (i == 0 || !mPtpMode) { 197 addStorageLocked(volume); 198 } 199 } 200 break; 201 } 202 } 203 } 204 205 private void addStorageLocked(StorageVolume volume) { 206 MtpStorage storage = new MtpStorage(volume, getApplicationContext()); 207 String path = storage.getPath(); 208 mStorageMap.put(path, storage); 209 210 Log.d(TAG, "addStorageLocked " + storage.getStorageId() + " " +path); 211 if (mDatabase != null) { 212 mDatabase.addStorage(storage); 213 } 214 if (mServer != null) { 215 mServer.addStorage(storage); 216 } 217 } 218 219 private void removeStorageLocked(StorageVolume volume) { 220 MtpStorage storage = mStorageMap.remove(volume.getPath()); 221 if (storage == null) { 222 Log.e(TAG, "no MtpStorage for " + volume.getPath()); 223 return; 224 } 225 226 Log.d(TAG, "removeStorageLocked " + storage.getStorageId() + " " + storage.getPath()); 227 if (mDatabase != null) { 228 mDatabase.removeStorage(storage); 229 } 230 if (mServer != null) { 231 mServer.removeStorage(storage); 232 } 233 } 234 } 235 236