blob: 2e430c849d973639a9634131e91099d31fa7b016 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
17package com.android.server;
18
19import android.net.LocalSocketAddress;
20import android.net.LocalSocket;
21import android.os.Environment;
22import android.os.SystemClock;
23import android.os.SystemProperties;
24import android.util.Config;
25import android.util.Log;
26
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
30import java.net.Socket;
31
32/**
33 * Thread for communicating with the vol service daemon via a local socket.
34 * Events received from the daemon are passed to the MountService instance,
35 * and the MountService instance calls MountListener to send commands to the daemon.
36 */
37final class MountListener implements Runnable {
38
39 private static final String TAG = "MountListener";
40
41 // ** THE FOLLOWING STRING CONSTANTS MUST MATCH VALUES IN system/vold/
42
43 // socket name for connecting to vold
44 private static final String VOLD_SOCKET = "vold";
45
46 // vold commands
47 private static final String VOLD_CMD_ENABLE_UMS = "enable_ums";
48 private static final String VOLD_CMD_DISABLE_UMS = "disable_ums";
49 private static final String VOLD_CMD_SEND_UMS_STATUS = "send_ums_status";
50 private static final String VOLD_CMD_MOUNT_VOLUME = "mount_volume:";
51 private static final String VOLD_CMD_EJECT_MEDIA = "eject_media:";
52 private static final String VOLD_CMD_FORMAT_MEDIA = "format_media:";
53
54 // vold events
55 private static final String VOLD_EVT_UMS_ENABLED = "ums_enabled";
56 private static final String VOLD_EVT_UMS_DISABLED = "ums_disabled";
57 private static final String VOLD_EVT_UMS_CONNECTED = "ums_connected";
58 private static final String VOLD_EVT_UMS_DISCONNECTED = "ums_disconnected";
59
60 private static final String VOLD_EVT_NOMEDIA = "volume_nomedia:";
61 private static final String VOLD_EVT_UNMOUNTED = "volume_unmounted:";
62 private static final String VOLD_EVT_MOUNTED = "volume_mounted:";
63 private static final String VOLD_EVT_MOUNTED_RO = "volume_mounted_ro:";
64 private static final String VOLD_EVT_UMS = "volume_ums";
65 private static final String VOLD_EVT_BAD_REMOVAL = "volume_badremoval:";
66 private static final String VOLD_EVT_DAMAGED = "volume_damaged:";
67 private static final String VOLD_EVT_CHECKING = "volume_checking:";
68 private static final String VOLD_EVT_NOFS = "volume_nofs:";
69 private static final String VOLD_EVT_EJECTING = "volume_ejecting:";
70
71 /**
72 * MountService that handles events received from the vol service daemon
73 */
74 private MountService mService;
75
76 /**
77 * Stream for sending commands to the vol service daemon.
78 */
79 private OutputStream mOutputStream;
80
81 /**
82 * Cached value indicating whether or not USB mass storage is enabled.
83 */
84 private boolean mUmsEnabled;
85
86 /**
87 * Cached value indicating whether or not USB mass storage is connected.
88 */
89 private boolean mUmsConnected;
90
91 /**
92 * Constructor for MountListener
93 *
94 * @param service The MountListener we are handling communication with USB
95 * daemon for.
96 */
97 MountListener(MountService service) {
98 mService = service;
99 }
100
101 /**
102 * Process and dispatches events received from the vol service daemon
103 *
104 * @param event An event received from the vol service daemon
105 */
106 private void handleEvent(String event) {
107 if (Config.LOGD) Log.d(TAG, "handleEvent " + event);
108
109 int colonIndex = event.indexOf(':');
110 String path = (colonIndex > 0 ? event.substring(colonIndex + 1) : null);
111
112 if (event.equals(VOLD_EVT_UMS_ENABLED)) {
113 mUmsEnabled = true;
114 } else if (event.equals(VOLD_EVT_UMS_DISABLED)) {
115 mUmsEnabled = false;
116 } else if (event.equals(VOLD_EVT_UMS_CONNECTED)) {
117 mUmsConnected = true;
118 mService.notifyUmsConnected();
119 } else if (event.equals(VOLD_EVT_UMS_DISCONNECTED)) {
120 mUmsConnected = false;
121 mService.notifyUmsDisconnected();
122 } else if (event.startsWith(VOLD_EVT_NOMEDIA)) {
123 mService.notifyMediaRemoved(path);
124 } else if (event.startsWith(VOLD_EVT_UNMOUNTED)) {
125 mService.notifyMediaUnmounted(path);
126 } else if (event.startsWith(VOLD_EVT_CHECKING)) {
127 mService.notifyMediaChecking(path);
128 } else if (event.startsWith(VOLD_EVT_NOFS)) {
129 mService.notifyMediaNoFs(path);
130 } else if (event.startsWith(VOLD_EVT_MOUNTED)) {
131 mService.notifyMediaMounted(path, false);
132 } else if (event.startsWith(VOLD_EVT_MOUNTED_RO)) {
133 mService.notifyMediaMounted(path, true);
134 } else if (event.startsWith(VOLD_EVT_UMS)) {
135 mService.notifyMediaShared(path);
136 } else if (event.startsWith(VOLD_EVT_BAD_REMOVAL)) {
137 mService.notifyMediaBadRemoval(path);
138 // also send media eject intent, to notify apps to close any open
139 // files on the media.
140 mService.notifyMediaEject(path);
141 } else if (event.startsWith(VOLD_EVT_DAMAGED)) {
142 mService.notifyMediaUnmountable(path);
143 } else if (event.startsWith(VOLD_EVT_EJECTING)) {
144 mService.notifyMediaEject(path);
145 }
146 }
147
148 /**
149 * Sends a command to the mount service daemon via a local socket
150 *
151 * @param command The command to send to the mount service daemon
152 */
153 private void writeCommand(String command) {
154 writeCommand2(command, null);
155 }
156
157 /**
158 * Sends a command to the mount service daemon via a local socket
159 * with a single argument
160 *
161 * @param command The command to send to the mount service daemon
162 * @param argument The argument to send with the command (or null)
163 */
164 private void writeCommand2(String command, String argument) {
165 synchronized (this) {
166 if (mOutputStream == null) {
167 Log.e(TAG, "No connection to vold", new IllegalStateException());
168 } else {
169 StringBuilder builder = new StringBuilder(command);
170 if (argument != null) {
171 builder.append(argument);
172 }
173 builder.append('\0');
174
175 try {
176 mOutputStream.write(builder.toString().getBytes());
177 } catch (IOException ex) {
178 Log.e(TAG, "IOException in writeCommand", ex);
179 }
180 }
181 }
182 }
183
184 /**
185 * Opens a socket to communicate with the mount service daemon and listens
186 * for events from the daemon.
187 *
188 */
189 private void listenToSocket() {
190 LocalSocket socket = null;
191
192 try {
193 socket = new LocalSocket();
194 LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET,
195 LocalSocketAddress.Namespace.RESERVED);
196
197 socket.connect(address);
198
199 InputStream inputStream = socket.getInputStream();
200 mOutputStream = socket.getOutputStream();
201
202 byte[] buffer = new byte[100];
203
204 writeCommand(VOLD_CMD_SEND_UMS_STATUS);
205
206 while (true) {
207 int count = inputStream.read(buffer);
208 if (count < 0) break;
209
210 int start = 0;
211 for (int i = 0; i < count; i++) {
212 if (buffer[i] == 0) {
213 String event = new String(buffer, start, i - start);
214 handleEvent(event);
215 start = i + 1;
216 }
217 }
218 }
219 } catch (IOException ex) {
220 // This exception is normal when running in desktop simulator
221 // where there is no mount daemon to talk to
222
223 // log("IOException in listenToSocket");
224 }
225
226 synchronized (this) {
227 if (mOutputStream != null) {
228 try {
229 mOutputStream.close();
230 } catch (IOException e) {
231 Log.w(TAG, "IOException closing output stream");
232 }
233
234 mOutputStream = null;
235 }
236 }
237
238 try {
239 if (socket != null) {
240 socket.close();
241 }
242 } catch (IOException ex) {
243 Log.w(TAG, "IOException closing socket");
244 }
245
246 /*
247 * Sleep before trying again.
248 * This should not happen except while debugging.
249 * Without this sleep, the emulator will spin and
250 * create tons of throwaway LocalSockets, making
251 * system_server GC constantly.
252 */
253 Log.e(TAG, "Failed to connect to vold", new IllegalStateException());
254 SystemClock.sleep(2000);
255 }
256
257 /**
258 * Main loop for MountListener thread.
259 */
260 public void run() {
261 // ugly hack for the simulator.
262 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
263 SystemProperties.set("EXTERNAL_STORAGE_STATE", Environment.MEDIA_MOUNTED);
264 // usbd does not run in the simulator, so send a fake device mounted event to trigger the Media Scanner
265 mService.notifyMediaMounted(Environment.getExternalStorageDirectory().getPath(), false);
266
267 // no usbd in the simulator, so no point in hanging around.
268 return;
269 }
270
271 try {
272 while (true) {
273 listenToSocket();
274 }
275 } catch (Throwable t) {
276 // catch all Throwables so we don't bring down the system process
277 Log.e(TAG, "Fatal error " + t + " in MountListener thread!");
278 }
279 }
280
281 /**
282 * @return true if USB mass storage is enabled
283 */
284 boolean getMassStorageEnabled() {
285 return mUmsEnabled;
286 }
287
288 /**
289 * Enables or disables USB mass storage support.
290 *
291 * @param enable true to enable USB mass storage support
292 */
293 void setMassStorageEnabled(boolean enable) {
294 writeCommand(enable ? VOLD_CMD_ENABLE_UMS : VOLD_CMD_DISABLE_UMS);
295 }
296
297 /**
298 * @return true if USB mass storage is connected
299 */
300 boolean getMassStorageConnected() {
301 return mUmsConnected;
302 }
303
304 /**
305 * Mount media at given mount point.
306 */
307 public void mountMedia(String mountPoint) {
308 writeCommand2(VOLD_CMD_MOUNT_VOLUME, mountPoint);
309 }
310
311 /**
312 * Unmount media at given mount point.
313 */
314 public void ejectMedia(String mountPoint) {
315 writeCommand2(VOLD_CMD_EJECT_MEDIA, mountPoint);
316 }
317
318 /**
319 * Format media at given mount point.
320 */
321 public void formatMedia(String mountPoint) {
322 writeCommand2(VOLD_CMD_FORMAT_MEDIA, mountPoint);
323 }
324}