VolumeManager.cpp revision 0cde53ce7b44ce189d0bc6fa81c0036e096deb51
1/*
2 * Copyright (C) 2008 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#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <linux/kdev_t.h>
23
24#define LOG_TAG "Vold"
25
26#include <cutils/log.h>
27
28#include <sysutils/NetlinkEvent.h>
29
30#include "VolumeManager.h"
31#include "DirectVolume.h"
32#include "ResponseCode.h"
33
34VolumeManager *VolumeManager::sInstance = NULL;
35
36VolumeManager *VolumeManager::Instance() {
37    if (!sInstance)
38        sInstance = new VolumeManager();
39    return sInstance;
40}
41
42VolumeManager::VolumeManager() {
43    mBlockDevices = new BlockDeviceCollection();
44    mVolumes = new VolumeCollection();
45    mBroadcaster = NULL;
46    mUsbMassStorageConnected = false;
47}
48
49VolumeManager::~VolumeManager() {
50    delete mBlockDevices;
51}
52
53int VolumeManager::start() {
54    return 0;
55}
56
57int VolumeManager::stop() {
58    return 0;
59}
60
61int VolumeManager::addVolume(Volume *v) {
62    mVolumes->push_back(v);
63    return 0;
64}
65
66void VolumeManager::notifyUmsConnected(bool connected) {
67    char msg[255];
68
69    if (connected) {
70        mUsbMassStorageConnected = true;
71    } else {
72        mUsbMassStorageConnected = false;
73    }
74    snprintf(msg, sizeof(msg), "Share method ums now %s",
75             (connected ? "available" : "unavailable"));
76
77    getBroadcaster()->sendBroadcast(ResponseCode::ShareAvailabilityChange,
78                                    msg, false);
79}
80
81void VolumeManager::handleSwitchEvent(NetlinkEvent *evt) {
82    const char *devpath = evt->findParam("DEVPATH");
83    const char *name = evt->findParam("SWITCH_NAME");
84    const char *state = evt->findParam("SWITCH_STATE");
85
86    if (!name || !state) {
87        LOGW("Switch %s event missing name/state info", devpath);
88        return;
89    }
90
91    if (!strcmp(name, "usb_mass_storage")) {
92
93        if (!strcmp(state, "online"))  {
94            notifyUmsConnected(true);
95        } else {
96            notifyUmsConnected(false);
97        }
98    } else {
99        LOGW("Ignoring unknown switch '%s'", name);
100    }
101}
102
103void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
104    const char *devpath = evt->findParam("DEVPATH");
105
106    /* Lookup a volume to handle this device */
107    VolumeCollection::iterator it;
108    bool hit = false;
109    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
110        if (!(*it)->handleBlockEvent(evt)) {
111#ifdef NETLINK_DEBUG
112            LOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
113#endif
114            hit = true;
115            break;
116        }
117    }
118
119    if (!hit) {
120#ifdef NETLINK_DEBUG
121        LOGW("No volumes handled block event for '%s'", devpath);
122#endif
123    }
124}
125
126int VolumeManager::listVolumes(SocketClient *cli) {
127    VolumeCollection::iterator i;
128
129    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
130        char *buffer;
131        asprintf(&buffer, "%s %s %d",
132                 (*i)->getLabel(), (*i)->getMountpoint(),
133                 (*i)->getState());
134        cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
135        free(buffer);
136    }
137    cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
138    return 0;
139}
140
141int VolumeManager::formatVolume(const char *label) {
142    Volume *v = lookupVolume(label);
143
144    if (!v) {
145        errno = ENOENT;
146        return -1;
147    }
148
149    return v->formatVol();
150}
151
152int VolumeManager::mountVolume(const char *label) {
153    Volume *v = lookupVolume(label);
154
155    if (!v) {
156        errno = ENOENT;
157        return -1;
158    }
159
160    return v->mountVol();
161}
162
163int VolumeManager::shareAvailable(const char *method, bool *avail) {
164
165    if (strcmp(method, "ums")) {
166        errno = ENOSYS;
167        return -1;
168    }
169
170    if (mUsbMassStorageConnected)
171        *avail = true;
172    else
173        *avail = false;
174    return 0;
175}
176
177int VolumeManager::simulate(const char *cmd, const char *arg) {
178
179    if (!strcmp(cmd, "ums")) {
180        if (!strcmp(arg, "connect")) {
181            notifyUmsConnected(true);
182        } else if (!strcmp(arg, "disconnect")) {
183            notifyUmsConnected(false);
184        } else {
185            errno = EINVAL;
186            return -1;
187        }
188    } else {
189        errno = EINVAL;
190        return -1;
191    }
192    return 0;
193}
194
195int VolumeManager::shareVolume(const char *label, const char *method) {
196    Volume *v = lookupVolume(label);
197
198    if (!v) {
199        errno = ENOENT;
200        return -1;
201    }
202
203    /*
204     * Eventually, we'll want to support additional share back-ends,
205     * some of which may work while the media is mounted. For now,
206     * we just support UMS
207     */
208    if (strcmp(method, "ums")) {
209        errno = ENOSYS;
210        return -1;
211    }
212
213    if (v->getState() == Volume::State_NoMedia) {
214        errno = ENODEV;
215        return -1;
216    }
217
218    if (v->getState() != Volume::State_Idle) {
219        // You need to unmount manually befoe sharing
220        errno = EBUSY;
221        return -1;
222    }
223
224    dev_t d = v->getDiskDevice();
225    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
226        // This volume does not support raw disk access
227        errno = EINVAL;
228        return -1;
229    }
230
231    int fd;
232    char nodepath[255];
233    snprintf(nodepath,
234             sizeof(nodepath), "/dev/block/vold/%d:%d",
235             MAJOR(d), MINOR(d));
236
237    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
238                   O_WRONLY)) < 0) {
239        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
240        return -1;
241    }
242
243    if (write(fd, nodepath, strlen(nodepath)) < 0) {
244        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
245        close(fd);
246        return -1;
247    }
248
249    close(fd);
250    v->handleVolumeShared();
251    return 0;
252}
253
254int VolumeManager::unshareVolume(const char *label, const char *method) {
255    Volume *v = lookupVolume(label);
256
257    if (!v) {
258        errno = ENOENT;
259        return -1;
260    }
261
262    if (strcmp(method, "ums")) {
263        errno = ENOSYS;
264        return -1;
265    }
266
267    if (v->getState() != Volume::State_Shared) {
268        errno = EINVAL;
269        return -1;
270    }
271
272    dev_t d = v->getDiskDevice();
273
274    int fd;
275    char nodepath[255];
276    snprintf(nodepath,
277             sizeof(nodepath), "/dev/block/vold/%d:%d",
278             MAJOR(d), MINOR(d));
279
280    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
281        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
282        return -1;
283    }
284
285    char ch = 0;
286    if (write(fd, &ch, 1) < 0) {
287        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
288        close(fd);
289        return -1;
290    }
291
292    close(fd);
293    v->handleVolumeUnshared();
294    return 0;
295}
296
297int VolumeManager::unmountVolume(const char *label) {
298    Volume *v = lookupVolume(label);
299
300    if (!v) {
301        errno = ENOENT;
302        return -1;
303    }
304
305    if (v->getState() == Volume::State_NoMedia) {
306        errno = ENODEV;
307        return -1;
308    }
309
310    if (v->getState() != Volume::State_Mounted) {
311        LOGW("Attempt to unmount volume which isn't mounted (%d)\n",
312             v->getState());
313        errno = EBUSY;
314        return -1;
315    }
316
317    return v->unmountVol();
318}
319
320/*
321 * Looks up a volume by it's label or mount-point
322 */
323Volume *VolumeManager::lookupVolume(const char *label) {
324    VolumeCollection::iterator i;
325
326    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
327        if (label[0] == '/') {
328            if (!strcmp(label, (*i)->getMountpoint()))
329                return (*i);
330        } else {
331            if (!strcmp(label, (*i)->getLabel()))
332                return (*i);
333        }
334    }
335    return NULL;
336}
337