1abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood/*
2abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * Copyright (C) 2010 The Android Open Source Project
3abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood *
4abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License");
5abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * you may not use this file except in compliance with the License.
6abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * You may obtain a copy of the License at
7abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood *
8abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood *      http://www.apache.org/licenses/LICENSE-2.0
9abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood *
10abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * Unless required by applicable law or agreed to in writing, software
11abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS,
12abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * See the License for the specific language governing permissions and
14abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood * limitations under the License.
15abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood */
16abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood
17abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwoodpackage com.android.providers.media;
18abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood
198efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkeyimport android.app.ActivityManager;
20868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwoodimport android.app.KeyguardManager;
21abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwoodimport android.app.Service;
22868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwoodimport android.content.BroadcastReceiver;
23abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwoodimport android.content.Context;
24abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwoodimport android.content.Intent;
25abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwoodimport android.content.IntentFilter;
266fcef4f5098c35a94cf0f17b6dd2b99149d2296cMike Lockwoodimport android.hardware.usb.UsbManager;
2790345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpDatabase;
2890345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpServer;
29d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwoodimport android.mtp.MtpStorage;
30abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwoodimport android.os.Environment;
31abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwoodimport android.os.IBinder;
328efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkeyimport android.os.UserHandle;
33d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwoodimport android.os.storage.StorageEventListener;
34d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwoodimport android.os.storage.StorageManager;
35c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageVolume;
36abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwoodimport android.util.Log;
37abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood
381e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwoodimport java.io.File;
39d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwoodimport java.util.HashMap;
40d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood
41d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwoodpublic class MtpService extends Service {
42abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood    private static final String TAG = "MtpService";
438efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    private static final boolean LOGD = true;
44abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood
451e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood    // We restrict PTP to these subdirectories
461e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood    private static final String[] PTP_DIRECTORIES = new String[] {
471e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood        Environment.DIRECTORY_DCIM,
481e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood        Environment.DIRECTORY_PICTURES,
491e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood    };
501e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood
511e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood    private void addStorageDevicesLocked() {
521e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood        if (mPtpMode) {
531e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood            // In PTP mode we support only primary storage
54395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
5557b65f10aaafc4bf42a0fa59eb2bbe6c8371c2e6Jeff Sharkey            final String path = primary.getPath();
568d13ca8552efd8292099a7bf5a30c0e0d18a60e7Chuanxiao Dong            if (path != null) {
578d13ca8552efd8292099a7bf5a30c0e0d18a60e7Chuanxiao Dong                String state = mStorageManager.getVolumeState(path);
588d13ca8552efd8292099a7bf5a30c0e0d18a60e7Chuanxiao Dong                if (Environment.MEDIA_MOUNTED.equals(state)) {
5957b65f10aaafc4bf42a0fa59eb2bbe6c8371c2e6Jeff Sharkey                    addStorageLocked(mVolumeMap.get(path));
608d13ca8552efd8292099a7bf5a30c0e0d18a60e7Chuanxiao Dong                }
618d13ca8552efd8292099a7bf5a30c0e0d18a60e7Chuanxiao Dong            }
621e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood        } else {
6355bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood            for (StorageVolume volume : mVolumeMap.values()) {
6455bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood                addStorageLocked(volume);
651e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood            }
661e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood        }
671e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood    }
681e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood
69868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
70868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood        @Override
71868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood        public void onReceive(Context context, Intent intent) {
72868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood            final String action = intent.getAction();
73868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood            if (Intent.ACTION_USER_PRESENT.equals(action)) {
74e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                // If the media scanner is running, it may currently be calling
75e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                // sendObjectAdded/Removed, which also synchronizes on mBinder
76e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                // (and in addition to that, all the native MtpServer methods
77e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                // lock the same Mutex). If it happens to be in an mtp device
78e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                // write(), it may block for some time, so process this broadcast
79e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                // in a thread.
80e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                new Thread(new Runnable() {
81e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                    @Override
82e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                    public void run() {
83e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                        synchronized (mBinder) {
84e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                            // Unhide the storage units when the user has unlocked the lockscreen
85e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                            if (mMtpDisabled) {
86e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                                addStorageDevicesLocked();
87e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                                mMtpDisabled = false;
88e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                            }
89e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                        }
90e89c3f8afe178cf31457917e13b017e8ff76dc52Marco Nelissen                    }}, "addStorageDevices").start();
91d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            }
92d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        }
93d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    };
94d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood
95d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    private final StorageEventListener mStorageEventListener = new StorageEventListener() {
968efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        @Override
97d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        public void onStorageStateChanged(String path, String oldState, String newState) {
98d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            synchronized (mBinder) {
99d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                Log.d(TAG, "onStorageStateChanged " + path + " " + oldState + " -> " + newState);
100d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                if (Environment.MEDIA_MOUNTED.equals(newState)) {
101d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                    volumeMountedLocked(path);
102d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                } else if (Environment.MEDIA_MOUNTED.equals(oldState)) {
10355bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood                    StorageVolume volume = mVolumeMap.remove(path);
10455bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood                    if (volume != null) {
10555bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood                        removeStorageLocked(volume);
106868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood                    }
107868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood                }
108868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood            }
109868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood        }
110868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood    };
111868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood
112d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    private MtpDatabase mDatabase;
113abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood    private MtpServer mServer;
114d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    private StorageManager mStorageManager;
1158efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    /** Flag indicating if MTP is disabled due to keyguard */
1168efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    private boolean mMtpDisabled;
1171e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood    private boolean mPtpMode;
11855bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood    private final HashMap<String, StorageVolume> mVolumeMap = new HashMap<String, StorageVolume>();
119d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
120c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood    private StorageVolume[] mVolumes;
121685aac2d437ef229f81b167ffa1dae95fe2d1a97Mike Lockwood
122685aac2d437ef229f81b167ffa1dae95fe2d1a97Mike Lockwood    @Override
123685aac2d437ef229f81b167ffa1dae95fe2d1a97Mike Lockwood    public void onCreate() {
124868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood        registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_PRESENT));
125d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood
1268efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        mStorageManager = StorageManager.from(this);
127d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        synchronized (mBinder) {
1288efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            updateDisabledStateLocked();
129d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            mStorageManager.registerListener(mStorageEventListener);
130c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood            StorageVolume[] volumes = mStorageManager.getVolumeList();
131c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood            mVolumes = volumes;
132d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            for (int i = 0; i < volumes.length; i++) {
133c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood                String path = volumes[i].getPath();
134d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                String state = mStorageManager.getVolumeState(path);
135d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                if (Environment.MEDIA_MOUNTED.equals(state)) {
1368efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    volumeMountedLocked(path);
137d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                }
138d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            }
139d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        }
140685aac2d437ef229f81b167ffa1dae95fe2d1a97Mike Lockwood    }
141abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood
142abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood    @Override
143819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    public int onStartCommand(Intent intent, int flags, int startId) {
144abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood        synchronized (mBinder) {
1458efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            updateDisabledStateLocked();
1468efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            mPtpMode = (intent == null ? false
1478efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    : intent.getBooleanExtra(UsbManager.USB_FUNCTION_PTP, false));
1488efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            String[] subdirs = null;
1498efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            if (mPtpMode) {
1508efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                int count = PTP_DIRECTORIES.length;
1518efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                subdirs = new String[count];
1528efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                for (int i = 0; i < count; i++) {
1538efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    File file =
1548efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                            Environment.getExternalStoragePublicDirectory(PTP_DIRECTORIES[i]);
1558efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    // make sure this directory exists
1568efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    file.mkdirs();
1578efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    subdirs[i] = file.getPath();
1581e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood                }
159abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood            }
1608efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            final StorageVolume primary = StorageManager.getPrimaryVolume(mVolumes);
1611e45d525d1e521a0b1d425d53f1cda773fd203d3Marco Nelissen            if (mDatabase != null) {
1621e45d525d1e521a0b1d425d53f1cda773fd203d3Marco Nelissen                mDatabase.setServer(null);
1631e45d525d1e521a0b1d425d53f1cda773fd203d3Marco Nelissen            }
1648efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            mDatabase = new MtpDatabase(this, MediaProvider.EXTERNAL_VOLUME,
1658efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    primary.getPath(), subdirs);
1668efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            manageServiceLocked();
167abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood        }
168819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
169819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        return START_STICKY;
170abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood    }
171abf5daf6a8c2f9441af98b489398a476e7c6564cMike Lockwood
1728efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    private void updateDisabledStateLocked() {
1738efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        final boolean isCurrentUser = UserHandle.myUserId() == ActivityManager.getCurrentUser();
1748efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        final KeyguardManager keyguardManager = (KeyguardManager) getSystemService(
1758efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                Context.KEYGUARD_SERVICE);
1768efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        mMtpDisabled = (keyguardManager.isKeyguardLocked() && keyguardManager.isKeyguardSecure())
1778efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                || !isCurrentUser;
1788efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        if (LOGD) {
1798efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            Log.d(TAG, "updating state; isCurrentUser=" + isCurrentUser + ", mMtpLocked="
1808efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                    + mMtpDisabled);
1818efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        }
1828efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    }
1838efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey
1848efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    /**
1858efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey     * Manage {@link #mServer}, creating only when running as the current user.
1868efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey     */
1878efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    private void manageServiceLocked() {
1888efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        final boolean isCurrentUser = UserHandle.myUserId() == ActivityManager.getCurrentUser();
1898efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        if (mServer == null && isCurrentUser) {
1908efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            Log.d(TAG, "starting MTP server in " + (mPtpMode ? "PTP mode" : "MTP mode"));
1918efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            mServer = new MtpServer(mDatabase, mPtpMode);
192bcbcb91c4cac69658faebd6e45ca6490d944778aMike Lockwood            mDatabase.setServer(mServer);
1938efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            if (!mMtpDisabled) {
1948efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey                addStorageDevicesLocked();
1958efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            }
1968efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            mServer.start();
1978efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        } else if (mServer != null && !isCurrentUser) {
1988efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            Log.d(TAG, "no longer current user; shutting down MTP server");
1998efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            // Internally, kernel will close our FD, and server thread will
2008efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            // handle cleanup.
2018efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey            mServer = null;
202bcbcb91c4cac69658faebd6e45ca6490d944778aMike Lockwood            mDatabase.setServer(null);
2038efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        }
2048efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    }
2058efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey
206819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    @Override
2078efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    public void onDestroy() {
208868c8a91b9fe8aba9e3c1f17514c3aa1022307d5Mike Lockwood        unregisterReceiver(mReceiver);
209d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        mStorageManager.unregisterListener(mStorageEventListener);
2101e45d525d1e521a0b1d425d53f1cda773fd203d3Marco Nelissen        if (mDatabase != null) {
2111e45d525d1e521a0b1d425d53f1cda773fd203d3Marco Nelissen            mDatabase.setServer(null);
2121e45d525d1e521a0b1d425d53f1cda773fd203d3Marco Nelissen        }
213abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood    }
214abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood
215d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final IMtpService.Stub mBinder =
216d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            new IMtpService.Stub() {
217d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void sendObjectAdded(int objectHandle) {
2183d8bf05677c11764474df82ec5dc912c9e746b0aMike Lockwood            synchronized (mBinder) {
219d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                if (mServer != null) {
220d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    mServer.sendObjectAdded(objectHandle);
221d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
222d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
223d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
224d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
225d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void sendObjectRemoved(int objectHandle) {
2263d8bf05677c11764474df82ec5dc912c9e746b0aMike Lockwood            synchronized (mBinder) {
227d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                if (mServer != null) {
228d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    mServer.sendObjectRemoved(objectHandle);
229d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
230d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
231d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
232d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
233d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
234abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood    @Override
2358efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey    public IBinder onBind(Intent intent) {
236d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        return mBinder;
237abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood    }
238d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood
239d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    private void volumeMountedLocked(String path) {
240c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        for (int i = 0; i < mVolumes.length; i++) {
241c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood            StorageVolume volume = mVolumes[i];
242c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood            if (volume.getPath().equals(path)) {
24355bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood                mVolumeMap.put(path, volume);
244d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                if (!mMtpDisabled) {
2451e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood                    // In PTP mode we support only primary storage
246395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                    if (volume.isPrimary() || !mPtpMode) {
24755bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood                        addStorageLocked(volume);
2481e950b8090d90bbf92e528a974ecb8c5c48dff84Mike Lockwood                    }
249d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                }
250d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood                break;
251d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            }
252d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        }
253d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    }
254d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood
25555bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood    private void addStorageLocked(StorageVolume volume) {
2569681e238cd39c3cfbbd63d47d26a1d9bff9f09e5Fabrice Di Meglio        MtpStorage storage = new MtpStorage(volume, getApplicationContext());
25755bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood        String path = storage.getPath();
25855bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood        mStorageMap.put(path, storage);
2598efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey
2608efd65fe64c7978534bb549b2329068a2f8c5075Jeff Sharkey        Log.d(TAG, "addStorageLocked " + storage.getStorageId() + " " + path);
261d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        if (mDatabase != null) {
262d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            mDatabase.addStorage(storage);
263d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        }
264d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        if (mServer != null) {
265d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            mServer.addStorage(storage);
266d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        }
267d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    }
268d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood
26955bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood    private void removeStorageLocked(StorageVolume volume) {
27055bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood        MtpStorage storage = mStorageMap.remove(volume.getPath());
27155bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood        if (storage == null) {
27255bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood            Log.e(TAG, "no MtpStorage for " + volume.getPath());
27355bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood            return;
27455bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood        }
27555bf981b5231b0831a146ba67dc16d9f55c67154Mike Lockwood
276d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        Log.d(TAG, "removeStorageLocked " + storage.getStorageId() + " " + storage.getPath());
277d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        if (mDatabase != null) {
278d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            mDatabase.removeStorage(storage);
279d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        }
280d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        if (mServer != null) {
281d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood            mServer.removeStorage(storage);
282d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood        }
283d3709e80446eb8abc3bb9c60db0d5c9473930611Mike Lockwood    }
284abf8d09064cdc380d4996f5022d3a20eb4448ed1Mike Lockwood}
285