blob: efc783948a2856ebd3946aa188bb1b0e25b9ae32 [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.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
27import android.content.res.Resources;
28import android.net.Uri;
29import android.os.IMountService;
30import android.os.Environment;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080031import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.os.SystemProperties;
33import android.os.UEventObserver;
San Mehat1f6301e2010-01-07 22:40:27 -080034import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.text.TextUtils;
36import android.util.Log;
San Mehat22dd86e2010-01-12 12:21:18 -080037import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
San Mehat1f6301e2010-01-07 22:40:27 -080039import android.provider.Settings;
40import android.content.ContentResolver;
41import android.database.ContentObserver;
42
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import java.io.File;
44import java.io.FileReader;
San Mehat36972292010-01-06 11:06:32 -080045import java.lang.IllegalStateException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046
47/**
48 * MountService implements an to the mount service daemon
49 * @hide
50 */
San Mehat22dd86e2010-01-12 12:21:18 -080051class MountService extends IMountService.Stub
52 implements INativeDaemonConnectorCallbacks {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
54 private static final String TAG = "MountService";
55
San Mehat7fd0fee2009-12-17 07:12:23 -080056 class VolumeState {
57 public static final int Init = -1;
58 public static final int NoMedia = 0;
59 public static final int Idle = 1;
60 public static final int Pending = 2;
61 public static final int Checking = 3;
62 public static final int Mounted = 4;
63 public static final int Unmounting = 5;
64 public static final int Formatting = 6;
65 public static final int Shared = 7;
66 public static final int SharedMnt = 8;
67 }
68
San Mehat22dd86e2010-01-12 12:21:18 -080069 class VoldResponseCode {
70 public static final int VolumeListResult = 110;
71 public static final int AsecListResult = 111;
72
73 public static final int ShareAvailabilityResult = 210;
74 public static final int AsecPathResult = 211;
75
76 public static final int VolumeStateChange = 605;
77 public static final int VolumeMountFailedBlank = 610;
78 public static final int VolumeMountFailedDamaged = 611;
79 public static final int VolumeMountFailedNoMedia = 612;
80 public static final int ShareAvailabilityChange = 620;
81 public static final int VolumeDiskInserted = 630;
82 public static final int VolumeDiskRemoved = 631;
83 public static final int VolumeBadRemoval = 632;
84 }
85
86
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 /**
88 * Binder context for this service
89 */
90 private Context mContext;
91
92 /**
San Mehat22dd86e2010-01-12 12:21:18 -080093 * connectorr object for communicating with vold
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 */
San Mehat22dd86e2010-01-12 12:21:18 -080095 private NativeDaemonConnector mConnector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096
97 /**
98 * The notification that is shown when a USB mass storage host
99 * is connected.
100 * <p>
101 * This is lazily created, so use {@link #setUsbStorageNotification()}.
102 */
103 private Notification mUsbStorageNotification;
104
105
106 /**
107 * The notification that is shown when the following media events occur:
108 * - Media is being checked
109 * - Media is blank (or unknown filesystem)
110 * - Media is corrupt
111 * - Media is safe to unmount
112 * - Media is missing
113 * <p>
114 * This is lazily created, so use {@link #setMediaStorageNotification()}.
115 */
116 private Notification mMediaStorageNotification;
117
118 private boolean mShowSafeUnmountNotificationWhenUnmounted;
119
120 private boolean mPlaySounds;
121
122 private boolean mMounted;
123
San Mehat1f6301e2010-01-07 22:40:27 -0800124 private SettingsWatcher mSettingsWatcher;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700125 private boolean mAutoStartUms;
San Mehat1f6301e2010-01-07 22:40:27 -0800126 private boolean mPromptUms;
127 private boolean mUmsActiveNotify;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700128
San Mehat7fd0fee2009-12-17 07:12:23 -0800129 private boolean mUmsConnected = false;
130 private boolean mUmsEnabled = false;
San Mehat5fbf4092010-01-15 10:13:59 -0800131 private boolean mUmsEnabling = false;
San Mehat7fd0fee2009-12-17 07:12:23 -0800132
133 private String mLegacyState = Environment.MEDIA_REMOVED;
134
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800135 private PackageManagerService mPms;
136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
138 * Constructs a new MountService instance
139 *
140 * @param context Binder context for this service
141 */
142 public MountService(Context context) {
143 mContext = context;
144
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800145 mPms = (PackageManagerService) ServiceManager.getService("package");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 // Register a BOOT_COMPLETED handler so that we can start
San Mehat22dd86e2010-01-12 12:21:18 -0800147 // our NativeDaemonConnector. We defer the startup so that we don't
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 // start processing events before we ought-to
149 mContext.registerReceiver(mBroadcastReceiver,
150 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
151
San Mehat22dd86e2010-01-12 12:21:18 -0800152 mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 mShowSafeUnmountNotificationWhenUnmounted = false;
154
155 mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700156
San Mehat1f6301e2010-01-07 22:40:27 -0800157 ContentResolver cr = mContext.getContentResolver();
158 mAutoStartUms = (Settings.Secure.getInt(
159 cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
160 mPromptUms = (Settings.Secure.getInt(
161 cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
162 mUmsActiveNotify = (Settings.Secure.getInt(
163 cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
164
165 mSettingsWatcher = new SettingsWatcher(new Handler());
166 }
167
168 private class SettingsWatcher extends ContentObserver {
169 public SettingsWatcher(Handler handler) {
170 super(handler);
171 ContentResolver cr = mContext.getContentResolver();
172 cr.registerContentObserver(Settings.System.getUriFor(
173 Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND), false, this);
174 cr.registerContentObserver(Settings.Secure.getUriFor(
175 Settings.Secure.MOUNT_UMS_AUTOSTART), false, this);
176 cr.registerContentObserver(Settings.Secure.getUriFor(
177 Settings.Secure.MOUNT_UMS_PROMPT), false, this);
178 cr.registerContentObserver(Settings.Secure.getUriFor(
179 Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED), false, this);
180 }
181
182 public void onChange(boolean selfChange) {
183 super.onChange(selfChange);
184 ContentResolver cr = mContext.getContentResolver();
185
186 boolean newPlayNotificationSounds = (Settings.Secure.getInt(
187 cr, Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND, 1) == 1);
188
189 boolean newUmsAutostart = (Settings.Secure.getInt(
190 cr, Settings.Secure.MOUNT_UMS_AUTOSTART, 0) == 1);
191
192 if (newUmsAutostart != mAutoStartUms) {
San Mehat1f6301e2010-01-07 22:40:27 -0800193 mAutoStartUms = newUmsAutostart;
194 }
195
196 boolean newUmsPrompt = (Settings.Secure.getInt(
197 cr, Settings.Secure.MOUNT_UMS_PROMPT, 1) == 1);
198
199 if (newUmsPrompt != mPromptUms) {
San Mehat1f6301e2010-01-07 22:40:27 -0800200 mPromptUms = newUmsAutostart;
201 }
202
203 boolean newUmsNotifyEnabled = (Settings.Secure.getInt(
204 cr, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED, 1) == 1);
205
San Mehat1f6301e2010-01-07 22:40:27 -0800206 if (mUmsEnabled) {
207 if (newUmsNotifyEnabled) {
208 Intent intent = new Intent();
209 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
210 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
211 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
212 com.android.internal.R.string.usb_storage_stop_notification_message,
213 com.android.internal.R.drawable.stat_sys_warning,
214 false, true, pi);
215 } else {
216 setUsbStorageNotification(0, 0, 0, false, false, null);
217 }
218 }
219 if (newUmsNotifyEnabled != mUmsActiveNotify) {
San Mehat1f6301e2010-01-07 22:40:27 -0800220 mUmsActiveNotify = newUmsNotifyEnabled;
221 }
222 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
224
225 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
226 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800227 String action = intent.getAction();
228
229 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat22dd86e2010-01-12 12:21:18 -0800230 /*
231 * Vold does not run in the simulator, so fake out a mounted
232 * event to trigger MediaScanner
233 */
234 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
235 notifyMediaMounted(
236 Environment.getExternalStorageDirectory().getPath(), false);
237 return;
238 }
239
240 Thread thread = new Thread(
241 mConnector, NativeDaemonConnector.class.getName());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 thread.start();
243 }
244 }
245 };
246
San Mehat91c77612010-01-07 10:39:41 -0800247 public void shutdown() {
248 if (mContext.checkCallingOrSelfPermission(
249 android.Manifest.permission.SHUTDOWN)
250 != PackageManager.PERMISSION_GRANTED) {
251 throw new SecurityException("Requires SHUTDOWN permission");
252 }
253
San Mehat7ebf0172010-01-12 07:57:42 -0800254 Log.d(TAG, "Shutting down");
San Mehat91c77612010-01-07 10:39:41 -0800255 String state = Environment.getExternalStorageState();
256
257 if (state.equals(Environment.MEDIA_SHARED)) {
258 /*
259 * If the media is currently shared, unshare it.
260 * XXX: This is still dangerous!. We should not
261 * be rebooting at *all* if UMS is enabled, since
262 * the UMS host could have dirty FAT cache entries
263 * yet to flush.
264 */
265 try {
266 setMassStorageEnabled(false);
267 } catch (Exception e) {
268 Log.e(TAG, "ums disable failed", e);
269 }
270 } else if (state.equals(Environment.MEDIA_CHECKING)) {
271 /*
272 * If the media is being checked, then we need to wait for
273 * it to complete before being able to proceed.
274 */
275 // XXX: @hackbod - Should we disable the ANR timer here?
276 int retries = 30;
277 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
278 try {
279 Thread.sleep(1000);
280 } catch (InterruptedException iex) {
281 Log.e(TAG, "Interrupted while waiting for media", iex);
282 break;
283 }
284 state = Environment.getExternalStorageState();
285 }
286 if (retries == 0) {
287 Log.e(TAG, "Timed out waiting for media to check");
288 }
289 }
290
291 if (state.equals(Environment.MEDIA_MOUNTED)) {
292 /*
293 * If the media is mounted, then gracefully unmount it.
294 */
295 try {
296 String m = Environment.getExternalStorageDirectory().toString();
San Mehat22dd86e2010-01-12 12:21:18 -0800297 unmountVolume(m);
San Mehat7ebf0172010-01-12 07:57:42 -0800298
299 int retries = 12;
300 while (!state.equals(Environment.MEDIA_UNMOUNTED) && (retries-- >=0)) {
301 try {
302 Thread.sleep(1000);
303 } catch (InterruptedException iex) {
304 Log.e(TAG, "Interrupted while waiting for media", iex);
305 break;
306 }
307 state = Environment.getExternalStorageState();
308 }
309 if (retries == 0) {
310 Log.e(TAG, "Timed out waiting for media to unmount");
Jean-Baptiste Querufa101532010-01-12 11:53:42 -0800311 }
San Mehat91c77612010-01-07 10:39:41 -0800312 } catch (Exception e) {
313 Log.e(TAG, "external storage unmount failed", e);
314 }
315 }
316 }
317
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 /**
319 * @return true if USB mass storage support is enabled.
320 */
San Mehat36972292010-01-06 11:06:32 -0800321 public boolean getMassStorageEnabled() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800322 return mUmsEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 }
324
325 /**
326 * Enables or disables USB mass storage support.
327 *
328 * @param enable true to enable USB mass storage support
329 */
San Mehat36972292010-01-06 11:06:32 -0800330 public void setMassStorageEnabled(boolean enable) throws IllegalStateException {
San Mehat1f6301e2010-01-07 22:40:27 -0800331 if (mContext.checkCallingOrSelfPermission(
332 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
333 != PackageManager.PERMISSION_GRANTED) {
334 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
335 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800336 try {
337 String vp = Environment.getExternalStorageDirectory().getPath();
338 String vs = getVolumeState(vp);
339
San Mehat5fbf4092010-01-15 10:13:59 -0800340 mUmsEnabling = enable;
San Mehat7fd0fee2009-12-17 07:12:23 -0800341 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
San Mehat22dd86e2010-01-12 12:21:18 -0800342 unmountVolume(vp);
San Mehat5fbf4092010-01-15 10:13:59 -0800343 mUmsEnabling = false;
San Mehat1f6301e2010-01-07 22:40:27 -0800344 updateUsbMassStorageNotification(true, false);
San Mehat7fd0fee2009-12-17 07:12:23 -0800345 }
346
San Mehat22dd86e2010-01-12 12:21:18 -0800347 setShareMethodEnabled(vp, "ums", enable);
San Mehat7fd0fee2009-12-17 07:12:23 -0800348 mUmsEnabled = enable;
San Mehat5fbf4092010-01-15 10:13:59 -0800349 mUmsEnabling = false;
San Mehat7fd0fee2009-12-17 07:12:23 -0800350 if (!enable) {
San Mehat22dd86e2010-01-12 12:21:18 -0800351 mountVolume(vp);
San Mehat1f6301e2010-01-07 22:40:27 -0800352 if (mPromptUms) {
353 updateUsbMassStorageNotification(false, false);
354 } else {
355 updateUsbMassStorageNotification(true, false);
356 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800357 }
San Mehat36972292010-01-06 11:06:32 -0800358 } catch (IllegalStateException rex) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800359 Log.e(TAG, "Failed to set ums enable {" + enable + "}");
360 return;
361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
363
364 /**
365 * @return true if USB mass storage is connected.
366 */
San Mehat36972292010-01-06 11:06:32 -0800367 public boolean getMassStorageConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800368 return mUmsConnected;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800370
371 /**
372 * @return state of the volume at the specified mount point
373 */
San Mehat36972292010-01-06 11:06:32 -0800374 public String getVolumeState(String mountPoint) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800375 /*
376 * XXX: Until we have multiple volume discovery, just hardwire
377 * this to /sdcard
378 */
379 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
380 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
381 throw new IllegalArgumentException();
382 }
383
384 return mLegacyState;
385 }
386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387
388 /**
389 * Attempt to mount external media
390 */
San Mehat22dd86e2010-01-12 12:21:18 -0800391 public void mountVolume(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 if (mContext.checkCallingOrSelfPermission(
393 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
394 != PackageManager.PERMISSION_GRANTED) {
395 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
396 }
San Mehat22dd86e2010-01-12 12:21:18 -0800397 mConnector.doCommand(String.format("mount %s", mountPath));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 }
399
400 /**
401 * Attempt to unmount external media to prepare for eject
402 */
San Mehat22dd86e2010-01-12 12:21:18 -0800403 public void unmountVolume(String mountPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 if (mContext.checkCallingOrSelfPermission(
405 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
406 != PackageManager.PERMISSION_GRANTED) {
407 throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
408 }
409
410 // Set a flag so that when we get the unmounted event, we know
411 // to display the notification
412 mShowSafeUnmountNotificationWhenUnmounted = true;
413
San Mehat22dd86e2010-01-12 12:21:18 -0800414 mConnector.doCommand(String.format("unmount %s", mountPath));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 }
416
417 /**
418 * Attempt to format external media
419 */
San Mehat22dd86e2010-01-12 12:21:18 -0800420 public void formatVolume(String formatPath) throws IllegalStateException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 if (mContext.checkCallingOrSelfPermission(
422 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
423 != PackageManager.PERMISSION_GRANTED) {
424 throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
425 }
426
San Mehat22dd86e2010-01-12 12:21:18 -0800427 mConnector.doCommand(String.format("format %s", formatPath));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 }
429
San Mehat22dd86e2010-01-12 12:21:18 -0800430 boolean getShareAvailable(String method) throws IllegalStateException {
431 ArrayList<String> rsp = mConnector.doCommand("share_available " + method);
432
433 for (String line : rsp) {
434 String []tok = line.split(" ");
435 int code = Integer.parseInt(tok[0]);
436 if (code == VoldResponseCode.ShareAvailabilityResult) {
437 if (tok[2].equals("available"))
438 return true;
439 return false;
440 } else {
441 throw new IllegalStateException(String.format("Unexpected response code %d", code));
442 }
443 }
444 throw new IllegalStateException("Got an empty response");
445 }
446
447 /**
448 * Enables or disables USB mass storage support.
449 *
450 * @param enable true to enable USB mass storage support
451 */
452 void setShareMethodEnabled(String mountPoint, String method,
453 boolean enable) throws IllegalStateException {
454 mConnector.doCommand(String.format(
455 "%sshare %s %s", (enable ? "" : "un"), mountPoint, method));
456 }
457
458
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 /**
460 * Returns true if we're playing media notification sounds.
461 */
462 public boolean getPlayNotificationSounds() {
463 return mPlaySounds;
464 }
465
466 /**
467 * Set whether or not we're playing media notification sounds.
468 */
469 public void setPlayNotificationSounds(boolean enabled) {
470 if (mContext.checkCallingOrSelfPermission(
471 android.Manifest.permission.WRITE_SETTINGS)
472 != PackageManager.PERMISSION_GRANTED) {
473 throw new SecurityException("Requires WRITE_SETTINGS permission");
474 }
475 mPlaySounds = enabled;
476 SystemProperties.set("persist.service.mount.playsnd", (enabled ? "1" : "0"));
477 }
478
San Mehat7fd0fee2009-12-17 07:12:23 -0800479 void updatePublicVolumeState(String mountPoint, String state) {
480 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
481 Log.w(TAG, "Multiple volumes not currently supported");
482 return;
483 }
San Mehat22dd86e2010-01-12 12:21:18 -0800484 Log.i(TAG, "State for {" + mountPoint + "} = {" + state + "}");
San Mehat7fd0fee2009-12-17 07:12:23 -0800485 mLegacyState = state;
486 }
487
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700488 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 * Update the state of the USB mass storage notification
490 */
491 void updateUsbMassStorageNotification(boolean suppressIfConnected, boolean sound) {
492
493 try {
494
495 if (getMassStorageConnected() && !suppressIfConnected) {
496 Intent intent = new Intent();
497 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
Mike Lockwood95174432009-08-26 09:44:09 -0700498 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
500 setUsbStorageNotification(
501 com.android.internal.R.string.usb_storage_notification_title,
502 com.android.internal.R.string.usb_storage_notification_message,
503 com.android.internal.R.drawable.stat_sys_data_usb,
504 sound, true, pi);
505 } else {
506 setUsbStorageNotification(0, 0, 0, false, false, null);
507 }
San Mehat36972292010-01-06 11:06:32 -0800508 } catch (IllegalStateException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 // Nothing to do
510 }
511 }
512
513 void handlePossibleExplicitUnmountBroadcast(String path) {
514 if (mMounted) {
515 mMounted = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800516 // Update media status on PackageManagerService to unmount packages on sdcard
517 mPms.updateExternalMediaStatus(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
519 Uri.parse("file://" + path));
520 mContext.sendBroadcast(intent);
521 }
522 }
523
San Mehat22dd86e2010-01-12 12:21:18 -0800524 /**
525 *
526 * Callback from NativeDaemonConnector
527 */
528 public void onDaemonConnected() {
San Mehat5b77dab2010-01-26 13:28:50 -0800529 /*
530 * Since we'll be calling back into the NativeDaemonConnector,
531 * we need to do our work in a new thread.
532 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800533 new Thread() {
534 public void run() {
San Mehat5b77dab2010-01-26 13:28:50 -0800535 /**
536 * Determine media state and UMS detection status
537 */
538 String path = Environment.getExternalStorageDirectory().getPath();
539 String state = Environment.MEDIA_REMOVED;
540
San Mehat7fd0fee2009-12-17 07:12:23 -0800541 try {
San Mehat5b77dab2010-01-26 13:28:50 -0800542 String[] vols = mConnector.doListCommand(
543 "list_volumes", VoldResponseCode.VolumeListResult);
544 for (String volstr : vols) {
545 String[] tok = volstr.split(" ");
546 // FMT: <label> <mountpoint> <state>
547 if (!tok[1].equals(path)) {
548 Log.w(TAG, String.format(
549 "Skipping unknown volume '%s'",tok[1]));
550 continue;
San Mehat7fd0fee2009-12-17 07:12:23 -0800551 }
San Mehat5b77dab2010-01-26 13:28:50 -0800552 int st = Integer.parseInt(tok[2]);
553 if (st == VolumeState.NoMedia) {
554 state = Environment.MEDIA_REMOVED;
555 } else if (st == VolumeState.Idle) {
San Mehatc2a39472010-01-26 16:51:35 -0800556 state = null;
San Mehat5b77dab2010-01-26 13:28:50 -0800557 try {
558 mountVolume(path);
559 } catch (Exception ex) {
560 Log.e(TAG, "Connection-mount failed", ex);
561 }
562 } else if (st == VolumeState.Mounted) {
563 state = Environment.MEDIA_MOUNTED;
564 Log.i(TAG, "Media already mounted on daemon connection");
565 } else if (st == VolumeState.Shared) {
566 state = Environment.MEDIA_SHARED;
567 Log.i(TAG, "Media shared on daemon connection");
568 } else {
569 throw new Exception(String.format("Unexpected state %d", st));
570 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800571 }
San Mehatc2a39472010-01-26 16:51:35 -0800572 if (state != null) {
573 updatePublicVolumeState(path, state);
574 }
San Mehat5b77dab2010-01-26 13:28:50 -0800575 } catch (Exception e) {
576 Log.e(TAG, "Error processing initial volume state", e);
577 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
San Mehat7fd0fee2009-12-17 07:12:23 -0800578 }
579
580 try {
San Mehat22dd86e2010-01-12 12:21:18 -0800581 boolean avail = getShareAvailable("ums");
San Mehat7fd0fee2009-12-17 07:12:23 -0800582 notifyShareAvailabilityChange("ums", avail);
583 } catch (Exception ex) {
584 Log.w(TAG, "Failed to get share availability");
585 }
586 }
587 }.start();
588 }
589
San Mehat22dd86e2010-01-12 12:21:18 -0800590 /**
591 *
592 * Callback from NativeDaemonConnector
593 */
594 public boolean onEvent(int code, String raw, String[] cooked) {
595 // Log.d(TAG, "event {" + raw + "}");
596 if (code == VoldResponseCode.VolumeStateChange) {
597 // FMT: NNN Volume <label> <mountpoint> state changed
598 // from <old_#> (<old_str>) to <new_#> (<new_str>)
599 notifyVolumeStateChange(
600 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
601 Integer.parseInt(cooked[10]));
602 } else if (code == VoldResponseCode.VolumeMountFailedBlank) {
603 // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
604 notifyMediaNoFs(cooked[3]);
605 // FMT: NNN Volume <label> <mountpoint> mount failed - no media
606 } else if (code == VoldResponseCode.VolumeMountFailedNoMedia) {
607 notifyMediaRemoved(cooked[3]);
608 } else if (code == VoldResponseCode.VolumeMountFailedDamaged) {
609 // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
610 notifyMediaUnmountable(cooked[3]);
611 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
612 // FMT: NNN Share method <method> now <available|unavailable>
613 boolean avail = false;
614 if (cooked[5].equals("available")) {
615 avail = true;
616 }
617 notifyShareAvailabilityChange(cooked[3], avail);
618 } else if (code == VoldResponseCode.VolumeDiskInserted) {
619 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
620 notifyMediaInserted(cooked[3]);
621 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
622 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
623 notifyMediaRemoved(cooked[3]);
624 } else if (code == VoldResponseCode.VolumeBadRemoval) {
625 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
626 notifyMediaBadRemoval(cooked[3]);
627 } else {
628 return false;
629 }
630 return true;
631 }
632
San Mehat7fd0fee2009-12-17 07:12:23 -0800633 void notifyVolumeStateChange(String label, String mountPoint, int oldState,
San Mehat36972292010-01-06 11:06:32 -0800634 int newState) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800635 String vs = getVolumeState(mountPoint);
636
637 if (newState == VolumeState.Init) {
638 } else if (newState == VolumeState.NoMedia) {
639 // NoMedia is handled via Disk Remove events
640 } else if (newState == VolumeState.Idle) {
San Mehat5fbf4092010-01-15 10:13:59 -0800641 /*
642 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
643 * if we're in the process of enabling UMS
644 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800645 if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
646 !vs.equals(Environment.MEDIA_NOFS) &&
San Mehat5fbf4092010-01-15 10:13:59 -0800647 !vs.equals(Environment.MEDIA_UNMOUNTABLE) &&
648 !mUmsEnabling) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800649 notifyMediaUnmounted(mountPoint);
650 }
651 } else if (newState == VolumeState.Pending) {
652 } else if (newState == VolumeState.Checking) {
653 notifyMediaChecking(mountPoint);
654 } else if (newState == VolumeState.Mounted) {
655 notifyMediaMounted(mountPoint, false);
656 } else if (newState == VolumeState.Unmounting) {
657 notifyMediaUnmounting(mountPoint);
658 } else if (newState == VolumeState.Formatting) {
659 } else if (newState == VolumeState.Shared) {
660 notifyMediaShared(mountPoint, false);
661 } else if (newState == VolumeState.SharedMnt) {
662 notifyMediaShared(mountPoint, true);
663 } else {
664 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
665 }
666 }
667
668
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 /**
670 * Broadcasts the USB mass storage connected event to all clients.
671 */
672 void notifyUmsConnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800673 mUmsConnected = true;
674
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 String storageState = Environment.getExternalStorageState();
676 if (!storageState.equals(Environment.MEDIA_REMOVED) &&
677 !storageState.equals(Environment.MEDIA_BAD_REMOVAL) &&
678 !storageState.equals(Environment.MEDIA_CHECKING)) {
679
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700680 if (mAutoStartUms) {
681 try {
682 setMassStorageEnabled(true);
San Mehat36972292010-01-06 11:06:32 -0800683 } catch (IllegalStateException e) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700684 }
San Mehat1f6301e2010-01-07 22:40:27 -0800685 } else if (mPromptUms) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700686 updateUsbMassStorageNotification(false, true);
687 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 }
689
690 Intent intent = new Intent(Intent.ACTION_UMS_CONNECTED);
691 mContext.sendBroadcast(intent);
692 }
693
San Mehat1f6301e2010-01-07 22:40:27 -0800694 void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800695 if (!method.equals("ums")) {
696 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
697 return;
698 }
San Mehat1f6301e2010-01-07 22:40:27 -0800699
700 /*
701 * Notification needs to run in a different thread as
702 * it may need to call back into vold
703 */
704 new Thread() {
705 public void run() {
706 try {
707 if (avail) {
708 notifyUmsConnected();
709 } else {
710 notifyUmsDisconnected();
711 }
712 } catch (Exception ex) {
713 Log.w(TAG, "Failed to mount media on insertion");
714 }
715 }
716 }.start();
San Mehat7fd0fee2009-12-17 07:12:23 -0800717 }
718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 /**
720 * Broadcasts the USB mass storage disconnected event to all clients.
721 */
722 void notifyUmsDisconnected() {
San Mehat7fd0fee2009-12-17 07:12:23 -0800723 mUmsConnected = false;
San Mehat1f6301e2010-01-07 22:40:27 -0800724 if (mUmsEnabled) {
725 try {
726 Log.w(TAG, "UMS disconnected while enabled!");
727 setMassStorageEnabled(false);
728 } catch (Exception ex) {
729 Log.e(TAG, "Error disabling UMS on unsafe UMS disconnect", ex);
730 }
731 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 updateUsbMassStorageNotification(false, false);
733 Intent intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
734 mContext.sendBroadcast(intent);
735 }
736
San Mehat36972292010-01-06 11:06:32 -0800737 void notifyMediaInserted(final String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800738 new Thread() {
739 public void run() {
740 try {
San Mehat22dd86e2010-01-12 12:21:18 -0800741 mountVolume(path);
San Mehat7fd0fee2009-12-17 07:12:23 -0800742 } catch (Exception ex) {
San Mehat1f6301e2010-01-07 22:40:27 -0800743 Log.w(TAG, "Failed to mount media on insertion", ex);
San Mehat7fd0fee2009-12-17 07:12:23 -0800744 }
745 }
746 }.start();
747 }
748
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 /**
750 * Broadcasts the media removed event to all clients.
751 */
San Mehat36972292010-01-06 11:06:32 -0800752 void notifyMediaRemoved(String path) throws IllegalStateException {
San Mehat7fd0fee2009-12-17 07:12:23 -0800753
754 // Suppress this on bad removal
755 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
756 return;
757 }
758
759 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
760
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 updateUsbMassStorageNotification(true, false);
762
763 setMediaStorageNotification(
San Mehat7fd0fee2009-12-17 07:12:23 -0800764 com.android.internal.R.string.ext_media_nomedia_notification_title,
765 com.android.internal.R.string.ext_media_nomedia_notification_message,
766 com.android.internal.R.drawable.stat_notify_sdcard_usb,
767 true, false, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 handlePossibleExplicitUnmountBroadcast(path);
769
770 Intent intent = new Intent(Intent.ACTION_MEDIA_REMOVED,
771 Uri.parse("file://" + path));
772 mContext.sendBroadcast(intent);
773 }
774
775 /**
776 * Broadcasts the media unmounted event to all clients.
777 */
778 void notifyMediaUnmounted(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800779
780 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
781
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800782 // Update media status on PackageManagerService to unmount packages on sdcard
783 mPms.updateExternalMediaStatus(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 if (mShowSafeUnmountNotificationWhenUnmounted) {
785 setMediaStorageNotification(
786 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
787 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400788 com.android.internal.R.drawable.stat_notify_sdcard,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 true, true, null);
790 mShowSafeUnmountNotificationWhenUnmounted = false;
791 } else {
792 setMediaStorageNotification(0, 0, 0, false, false, null);
793 }
794 updateUsbMassStorageNotification(false, false);
795
796 Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED,
797 Uri.parse("file://" + path));
798 mContext.sendBroadcast(intent);
799 }
800
801 /**
802 * Broadcasts the media checking event to all clients.
803 */
804 void notifyMediaChecking(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800805 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
806
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 setMediaStorageNotification(
808 com.android.internal.R.string.ext_media_checking_notification_title,
809 com.android.internal.R.string.ext_media_checking_notification_message,
Mike Lockwoodde46acdd2009-09-30 19:30:56 -0400810 com.android.internal.R.drawable.stat_notify_sdcard_prepare,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 true, false, null);
812
813 updateUsbMassStorageNotification(true, false);
814 Intent intent = new Intent(Intent.ACTION_MEDIA_CHECKING,
815 Uri.parse("file://" + path));
816 mContext.sendBroadcast(intent);
817 }
818
819 /**
820 * Broadcasts the media nofs event to all clients.
821 */
822 void notifyMediaNoFs(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800823 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824
825 Intent intent = new Intent();
826 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
827 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
828
829 setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
830 com.android.internal.R.string.ext_media_nofs_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400831 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 true, false, pi);
833 updateUsbMassStorageNotification(false, false);
834 intent = new Intent(Intent.ACTION_MEDIA_NOFS,
835 Uri.parse("file://" + path));
836 mContext.sendBroadcast(intent);
837 }
838
839 /**
840 * Broadcasts the media mounted event to all clients.
841 */
842 void notifyMediaMounted(String path, boolean readOnly) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800843 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
844
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800845 // Update media status on PackageManagerService to mount packages on sdcard
846 mPms.updateExternalMediaStatus(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 setMediaStorageNotification(0, 0, 0, false, false, null);
848 updateUsbMassStorageNotification(false, false);
849 Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,
850 Uri.parse("file://" + path));
851 intent.putExtra("read-only", readOnly);
852 mMounted = true;
853 mContext.sendBroadcast(intent);
854 }
855
856 /**
857 * Broadcasts the media shared event to all clients.
858 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800859 void notifyMediaShared(String path, boolean mounted) {
860 if (mounted) {
861 Log.e(TAG, "Live shared mounts not supported yet!");
862 return;
863 }
864
865 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
866
San Mehat1f6301e2010-01-07 22:40:27 -0800867 if (mUmsActiveNotify) {
868 Intent intent = new Intent();
869 intent.setClass(mContext, com.android.internal.app.UsbStorageStopActivity.class);
870 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
871 setUsbStorageNotification(com.android.internal.R.string.usb_storage_stop_notification_title,
872 com.android.internal.R.string.usb_storage_stop_notification_message,
873 com.android.internal.R.drawable.stat_sys_warning,
874 false, true, pi);
875 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 handlePossibleExplicitUnmountBroadcast(path);
San Mehat1f6301e2010-01-07 22:40:27 -0800877 Intent intent = new Intent(Intent.ACTION_MEDIA_SHARED,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 Uri.parse("file://" + path));
879 mContext.sendBroadcast(intent);
880 }
881
882 /**
883 * Broadcasts the media bad removal event to all clients.
884 */
885 void notifyMediaBadRemoval(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800886 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
887
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 updateUsbMassStorageNotification(true, false);
889 setMediaStorageNotification(com.android.internal.R.string.ext_media_badremoval_notification_title,
890 com.android.internal.R.string.ext_media_badremoval_notification_message,
891 com.android.internal.R.drawable.stat_sys_warning,
892 true, true, null);
893
894 handlePossibleExplicitUnmountBroadcast(path);
895 Intent intent = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL,
896 Uri.parse("file://" + path));
897 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 }
899
900 /**
901 * Broadcasts the media unmountable event to all clients.
902 */
903 void notifyMediaUnmountable(String path) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800904 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
905
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 Intent intent = new Intent();
907 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
908 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
909
910 setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
911 com.android.internal.R.string.ext_media_unmountable_notification_message,
Mike Lockwooda7ef2692009-09-10 11:15:26 -0400912 com.android.internal.R.drawable.stat_notify_sdcard_usb,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 true, false, pi);
914 updateUsbMassStorageNotification(false, false);
915
916 handlePossibleExplicitUnmountBroadcast(path);
917
918 intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE,
919 Uri.parse("file://" + path));
920 mContext.sendBroadcast(intent);
921 }
922
923 /**
924 * Broadcasts the media eject event to all clients.
925 */
San Mehat7fd0fee2009-12-17 07:12:23 -0800926 void notifyMediaUnmounting(String path) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 Intent intent = new Intent(Intent.ACTION_MEDIA_EJECT,
928 Uri.parse("file://" + path));
929 mContext.sendBroadcast(intent);
930 }
931
932 /**
933 * Sets the USB storage notification.
934 */
935 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
936 PendingIntent pi) {
937
938 if (!visible && mUsbStorageNotification == null) {
939 return;
940 }
941
942 NotificationManager notificationManager = (NotificationManager) mContext
943 .getSystemService(Context.NOTIFICATION_SERVICE);
944
945 if (notificationManager == null) {
946 return;
947 }
948
949 if (visible) {
950 Resources r = Resources.getSystem();
951 CharSequence title = r.getText(titleId);
952 CharSequence message = r.getText(messageId);
953
954 if (mUsbStorageNotification == null) {
955 mUsbStorageNotification = new Notification();
956 mUsbStorageNotification.icon = icon;
957 mUsbStorageNotification.when = 0;
958 }
959
960 if (sound && mPlaySounds) {
961 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
962 } else {
963 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
964 }
965
966 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
967
968 mUsbStorageNotification.tickerText = title;
969 if (pi == null) {
970 Intent intent = new Intent();
971 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
972 }
973
974 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
975 }
976
977 final int notificationId = mUsbStorageNotification.icon;
978 if (visible) {
979 notificationManager.notify(notificationId, mUsbStorageNotification);
980 } else {
981 notificationManager.cancel(notificationId);
982 }
983 }
984
985 private synchronized boolean getMediaStorageNotificationDismissable() {
986 if ((mMediaStorageNotification != null) &&
987 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
988 Notification.FLAG_AUTO_CANCEL))
989 return true;
990
991 return false;
992 }
993
994 /**
995 * Sets the media storage notification.
996 */
997 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
998 boolean dismissable, PendingIntent pi) {
999
1000 if (!visible && mMediaStorageNotification == null) {
1001 return;
1002 }
1003
1004 NotificationManager notificationManager = (NotificationManager) mContext
1005 .getSystemService(Context.NOTIFICATION_SERVICE);
1006
1007 if (notificationManager == null) {
1008 return;
1009 }
1010
1011 if (mMediaStorageNotification != null && visible) {
1012 /*
1013 * Dismiss the previous notification - we're about to
1014 * re-use it.
1015 */
1016 final int notificationId = mMediaStorageNotification.icon;
1017 notificationManager.cancel(notificationId);
1018 }
1019
1020 if (visible) {
1021 Resources r = Resources.getSystem();
1022 CharSequence title = r.getText(titleId);
1023 CharSequence message = r.getText(messageId);
1024
1025 if (mMediaStorageNotification == null) {
1026 mMediaStorageNotification = new Notification();
1027 mMediaStorageNotification.when = 0;
1028 }
1029
1030 if (mPlaySounds) {
1031 mMediaStorageNotification.defaults |= Notification.DEFAULT_SOUND;
1032 } else {
1033 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
1034 }
1035
1036 if (dismissable) {
1037 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
1038 } else {
1039 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
1040 }
1041
1042 mMediaStorageNotification.tickerText = title;
1043 if (pi == null) {
1044 Intent intent = new Intent();
1045 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
1046 }
1047
1048 mMediaStorageNotification.icon = icon;
1049 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
1050 }
1051
1052 final int notificationId = mMediaStorageNotification.icon;
1053 if (visible) {
1054 notificationManager.notify(notificationId, mMediaStorageNotification);
1055 } else {
1056 notificationManager.cancel(notificationId);
1057 }
1058 }
San Mehat36972292010-01-06 11:06:32 -08001059
San Mehat0f5525a2010-01-11 10:15:16 -08001060 public String[] getSecureContainerList() throws IllegalStateException {
San Mehat8ab61352010-01-20 15:16:42 -08001061 return mConnector.doListCommand("list_asec", VoldResponseCode.AsecListResult);
San Mehat36972292010-01-06 11:06:32 -08001062 }
1063
San Mehat0f5525a2010-01-11 10:15:16 -08001064 public String createSecureContainer(String id, int sizeMb, String fstype,
San Mehat36972292010-01-06 11:06:32 -08001065 String key, int ownerUid) throws IllegalStateException {
San Mehat22dd86e2010-01-12 12:21:18 -08001066 String cmd = String.format("create_asec %s %d %s %s %d",
1067 id, sizeMb, fstype, key, ownerUid);
1068 mConnector.doCommand(cmd);
1069 return getSecureContainerPath(id);
San Mehat36972292010-01-06 11:06:32 -08001070 }
1071
San Mehat0f5525a2010-01-11 10:15:16 -08001072 public void finalizeSecureContainer(String id) throws IllegalStateException {
San Mehat22dd86e2010-01-12 12:21:18 -08001073 mConnector.doCommand(String.format("finalize_asec %s", id));
San Mehat36972292010-01-06 11:06:32 -08001074 }
1075
San Mehat0f5525a2010-01-11 10:15:16 -08001076 public void destroySecureContainer(String id) throws IllegalStateException {
San Mehat22dd86e2010-01-12 12:21:18 -08001077 mConnector.doCommand(String.format("destroy_asec %s", id));
San Mehat36972292010-01-06 11:06:32 -08001078 }
1079
San Mehat22dd86e2010-01-12 12:21:18 -08001080 public String mountSecureContainer(String id, String key,
1081 int ownerUid) throws IllegalStateException {
1082 String cmd = String.format("mount_asec %s %s %d",
1083 id, key, ownerUid);
1084 mConnector.doCommand(cmd);
1085 return getSecureContainerPath(id);
San Mehat36972292010-01-06 11:06:32 -08001086 }
1087
San Mehat9dba7092010-01-18 06:47:41 -08001088 public void unmountSecureContainer(String id) throws IllegalStateException {
San Mehat3e3a6432010-01-22 16:46:23 -08001089 String cmd = String.format("unmount_asec %s", id);
San Mehat9dba7092010-01-18 06:47:41 -08001090 mConnector.doCommand(cmd);
1091 }
1092
San Mehat45f61042010-01-23 08:12:43 -08001093 public void renameSecureContainer(String oldId, String newId) throws IllegalStateException {
1094 String cmd = String.format("rename_asec %s %s", oldId, newId);
1095 mConnector.doCommand(cmd);
1096 }
1097
San Mehat0f5525a2010-01-11 10:15:16 -08001098 public String getSecureContainerPath(String id) throws IllegalStateException {
San Mehat22dd86e2010-01-12 12:21:18 -08001099 ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
San Mehat36972292010-01-06 11:06:32 -08001100
San Mehat22dd86e2010-01-12 12:21:18 -08001101 for (String line : rsp) {
1102 String []tok = line.split(" ");
1103 int code = Integer.parseInt(tok[0]);
1104 if (code == VoldResponseCode.AsecPathResult) {
1105 return tok[1];
1106 } else {
1107 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1108 }
1109 }
1110 throw new IllegalStateException("Got an empty response");
1111 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112}
1113