Home | History | Annotate | Download | only in media
      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