VolumeManager.cpp revision a2677e4ad01f250b0765f04adf0acfa6627efc98
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 *name = evt->findParam("SWITCH_NAME");
83    const char *state = evt->findParam("SWITCH_STATE");
84
85    if (!strcmp(name, "usb_mass_storage")) {
86
87        if (!strcmp(state, "online"))  {
88            notifyUmsConnected(true);
89        } else {
90            notifyUmsConnected(false);
91        }
92    } else {
93        LOGW("Ignoring unknown switch '%s'", name);
94    }
95}
96
97void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
98    const char *devpath = evt->findParam("DEVPATH");
99
100    /* Lookup a volume to handle this device */
101    VolumeCollection::iterator it;
102    bool hit = false;
103    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
104        if (!(*it)->handleBlockEvent(evt)) {
105#ifdef NETLINK_DEBUG
106            LOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
107#endif
108            hit = true;
109            break;
110        }
111    }
112
113    if (!hit) {
114#ifdef NETLINK_DEBUG
115        LOGW("No volumes handled block event for '%s'", devpath);
116#endif
117    }
118}
119
120int VolumeManager::listVolumes(SocketClient *cli) {
121    VolumeCollection::iterator i;
122
123    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
124        char *buffer;
125        asprintf(&buffer, "%s %s %d",
126                 (*i)->getLabel(), (*i)->getMountpoint(),
127                 (*i)->getState());
128        cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
129        free(buffer);
130    }
131    cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
132    return 0;
133}
134
135int VolumeManager::formatVolume(const char *label) {
136    Volume *v = lookupVolume(label);
137
138    if (!v) {
139        errno = ENOENT;
140        return -1;
141    }
142
143    return v->formatVol();
144}
145
146int VolumeManager::mountVolume(const char *label) {
147    Volume *v = lookupVolume(label);
148
149    if (!v) {
150        errno = ENOENT;
151        return -1;
152    }
153
154    return v->mountVol();
155}
156
157int VolumeManager::shareAvailable(const char *method, bool *avail) {
158
159    if (strcmp(method, "ums")) {
160        errno = ENOSYS;
161        return -1;
162    }
163
164    if (mUsbMassStorageConnected)
165        *avail = true;
166    else
167        *avail = false;
168    return 0;
169}
170
171int VolumeManager::simulate(const char *cmd, const char *arg) {
172
173    if (!strcmp(cmd, "ums")) {
174        if (!strcmp(arg, "connect")) {
175            notifyUmsConnected(true);
176        } else if (!strcmp(arg, "disconnect")) {
177            notifyUmsConnected(false);
178        } else {
179            errno = EINVAL;
180            return -1;
181        }
182    } else {
183        errno = EINVAL;
184        return -1;
185    }
186    return 0;
187}
188
189int VolumeManager::shareVolume(const char *label, const char *method) {
190    Volume *v = lookupVolume(label);
191
192    if (!v) {
193        errno = ENOENT;
194        return -1;
195    }
196
197    /*
198     * Eventually, we'll want to support additional share back-ends,
199     * some of which may work while the media is mounted. For now,
200     * we just support UMS
201     */
202    if (strcmp(method, "ums")) {
203        errno = ENOSYS;
204        return -1;
205    }
206
207    if (v->getState() == Volume::State_NoMedia) {
208        errno = ENODEV;
209        return -1;
210    }
211
212    if (v->getState() != Volume::State_Idle) {
213        // You need to unmount manually befoe sharing
214        errno = EBUSY;
215        return -1;
216    }
217
218    dev_t d = v->getDiskDevice();
219    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
220        // This volume does not support raw disk access
221        errno = EINVAL;
222        return -1;
223    }
224
225    int fd;
226    char nodepath[255];
227    snprintf(nodepath,
228             sizeof(nodepath), "/dev/block/vold/%d:%d",
229             MAJOR(d), MINOR(d));
230
231    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0", O_WRONLY)) < 0) {
232        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
233        return -1;
234    }
235
236    if (write(fd, nodepath, strlen(nodepath)) < 0) {
237        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
238        close(fd);
239        return -1;
240    }
241
242    close(fd);
243    v->handleVolumeShared();
244    return 0;
245}
246
247int VolumeManager::unshareVolume(const char *label, const char *method) {
248    Volume *v = lookupVolume(label);
249
250    if (!v) {
251        errno = ENOENT;
252        return -1;
253    }
254
255    if (strcmp(method, "ums")) {
256        errno = ENOSYS;
257        return -1;
258    }
259
260    if (v->getState() != Volume::State_Shared) {
261        errno = EINVAL;
262        return -1;
263    }
264
265    dev_t d = v->getDiskDevice();
266
267    int fd;
268    char nodepath[255];
269    snprintf(nodepath,
270             sizeof(nodepath), "/dev/block/vold/%d:%d",
271             MAJOR(d), MINOR(d));
272
273    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0", O_WRONLY)) < 0) {
274        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
275        return -1;
276    }
277
278    char ch = 0;
279    if (write(fd, &ch, 1) < 0) {
280        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
281        close(fd);
282        return -1;
283    }
284
285    close(fd);
286    v->handleVolumeUnshared();
287    return 0;
288}
289
290int VolumeManager::unmountVolume(const char *label) {
291    Volume *v = lookupVolume(label);
292
293    if (!v) {
294        errno = ENOENT;
295        return -1;
296    }
297
298    if (v->getState() == Volume::State_NoMedia) {
299        errno = ENODEV;
300        return -1;
301    }
302
303    if (v->getState() != Volume::State_Mounted) {
304        LOGW("Attempt to unmount volume which isn't mounted (%d)\n",
305             v->getState());
306        errno = EBUSY;
307        return -1;
308    }
309
310    return v->unmountVol();
311}
312
313/*
314 * Looks up a volume by it's label or mount-point
315 */
316Volume *VolumeManager::lookupVolume(const char *label) {
317    VolumeCollection::iterator i;
318
319    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
320        if (label[0] == '/') {
321            if (!strcmp(label, (*i)->getMountpoint()))
322                return (*i);
323        } else {
324            if (!strcmp(label, (*i)->getLabel()))
325                return (*i);
326        }
327    }
328    return NULL;
329}
330