blob: d7b92ec0ae71c8a121d2c330161b2a42412e7361 [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
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080019import com.android.server.am.ActivityManagerService;
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
Kenny Root02c87302010-07-01 08:10:18 -070026import android.content.res.ObbInfo;
27import android.content.res.ObbScanner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.net.Uri;
San Mehatb1043402010-02-05 08:26:50 -080029import android.os.storage.IMountService;
30import android.os.storage.IMountServiceListener;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080031import android.os.storage.IMountShutdownObserver;
San Mehatb1043402010-02-05 08:26:50 -080032import android.os.storage.StorageResultCode;
Kenny Root02c87302010-07-01 08:10:18 -070033import android.os.Binder;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080034import android.os.Handler;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040035import android.os.HandlerThread;
36import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080037import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080038import android.os.RemoteException;
39import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.Environment;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080041import android.os.ServiceManager;
San Mehat207e5382010-02-04 20:46:54 -080042import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.SystemProperties;
San Mehata5078592010-03-25 09:36:54 -070044import android.util.Slog;
San Mehat22dd86e2010-01-12 12:21:18 -080045import java.util.ArrayList;
San Mehat6cdd9c02010-02-09 14:45:20 -080046import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048/**
San Mehatb1043402010-02-05 08:26:50 -080049 * MountService implements back-end services for platform storage
50 * management.
51 * @hide - Applications should use android.os.storage.StorageManager
52 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 */
San Mehat22dd86e2010-01-12 12:21:18 -080054class MountService extends IMountService.Stub
55 implements INativeDaemonConnectorCallbacks {
San Mehatb1043402010-02-05 08:26:50 -080056 private static final boolean LOCAL_LOGD = false;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -080057 private static final boolean DEBUG_UNMOUNT = false;
58 private static final boolean DEBUG_EVENTS = false;
Kenny Root02c87302010-07-01 08:10:18 -070059 private static final boolean DEBUG_OBB = true;
60
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 private static final String TAG = "MountService";
62
San Mehat4270e1e2010-01-29 05:32:19 -080063 /*
64 * Internal vold volume state constants
65 */
San Mehat7fd0fee2009-12-17 07:12:23 -080066 class VolumeState {
67 public static final int Init = -1;
68 public static final int NoMedia = 0;
69 public static final int Idle = 1;
70 public static final int Pending = 2;
71 public static final int Checking = 3;
72 public static final int Mounted = 4;
73 public static final int Unmounting = 5;
74 public static final int Formatting = 6;
75 public static final int Shared = 7;
76 public static final int SharedMnt = 8;
77 }
78
San Mehat4270e1e2010-01-29 05:32:19 -080079 /*
80 * Internal vold response code constants
81 */
San Mehat22dd86e2010-01-12 12:21:18 -080082 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -080083 /*
84 * 100 series - Requestion action was initiated; expect another reply
85 * before proceeding with a new command.
86 */
San Mehat22dd86e2010-01-12 12:21:18 -080087 public static final int VolumeListResult = 110;
88 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -080089 public static final int StorageUsersListResult = 112;
San Mehat22dd86e2010-01-12 12:21:18 -080090
San Mehat4270e1e2010-01-29 05:32:19 -080091 /*
92 * 200 series - Requestion action has been successfully completed.
93 */
94 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -080095 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -080096 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -080097
San Mehat4270e1e2010-01-29 05:32:19 -080098 /*
99 * 400 series - Command was accepted, but the requested action
100 * did not take place.
101 */
102 public static final int OpFailedNoMedia = 401;
103 public static final int OpFailedMediaBlank = 402;
104 public static final int OpFailedMediaCorrupt = 403;
105 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800106 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700107 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800108
109 /*
110 * 600 series - Unsolicited broadcasts.
111 */
San Mehat22dd86e2010-01-12 12:21:18 -0800112 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800113 public static final int ShareAvailabilityChange = 620;
114 public static final int VolumeDiskInserted = 630;
115 public static final int VolumeDiskRemoved = 631;
116 public static final int VolumeBadRemoval = 632;
117 }
118
San Mehat4270e1e2010-01-29 05:32:19 -0800119 private Context mContext;
120 private NativeDaemonConnector mConnector;
121 private String mLegacyState = Environment.MEDIA_REMOVED;
122 private PackageManagerService mPms;
123 private boolean mUmsEnabling;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800124 // Used as a lock for methods that register/unregister listeners.
125 final private ArrayList<MountServiceBinderListener> mListeners =
126 new ArrayList<MountServiceBinderListener>();
San Mehat6a965af22010-02-24 17:47:30 -0800127 private boolean mBooted = false;
128 private boolean mReady = false;
129 private boolean mSendUmsConnectedOnBoot = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800130
San Mehat6cdd9c02010-02-09 14:45:20 -0800131 /**
132 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800133 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800134 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800135 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800136
Kenny Root02c87302010-07-01 08:10:18 -0700137 /**
138 * Private hash of currently mounted filesystem images.
139 */
140 final private HashSet<String> mObbMountSet = new HashSet<String>();
141
142 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800143 private static final int H_UNMOUNT_PM_UPDATE = 1;
144 private static final int H_UNMOUNT_PM_DONE = 2;
145 private static final int H_UNMOUNT_MS = 3;
146 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
147 private static final int MAX_UNMOUNT_RETRIES = 4;
148
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800149 class UnmountCallBack {
150 String path;
151 int retries;
152 boolean force;
153
154 UnmountCallBack(String path, boolean force) {
155 retries = 0;
156 this.path = path;
157 this.force = force;
158 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800159
160 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700161 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800162 doUnmountVolume(path, true);
163 }
164 }
165
166 class UmsEnableCallBack extends UnmountCallBack {
167 String method;
168
169 UmsEnableCallBack(String path, String method, boolean force) {
170 super(path, force);
171 this.method = method;
172 }
173
174 @Override
175 void handleFinished() {
176 super.handleFinished();
177 doShareUnshareVolume(path, method, true);
178 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800179 }
180
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800181 class ShutdownCallBack extends UnmountCallBack {
182 IMountShutdownObserver observer;
183 ShutdownCallBack(String path, IMountShutdownObserver observer) {
184 super(path, true);
185 this.observer = observer;
186 }
187
188 @Override
189 void handleFinished() {
190 int ret = doUnmountVolume(path, true);
191 if (observer != null) {
192 try {
193 observer.onShutDownComplete(ret);
194 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700195 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800196 }
197 }
198 }
199 }
200
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400201 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800202 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700203 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800204
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400205 MountServiceHandler(Looper l) {
206 super(l);
207 }
208
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800209 public void handleMessage(Message msg) {
210 switch (msg.what) {
211 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700212 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800213 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
214 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700215 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800216 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700217 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700218 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700219 mUpdatingStatus = true;
220 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800221 }
222 break;
223 }
224 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700225 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700226 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700227 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800228 int size = mForceUnmounts.size();
229 int sizeArr[] = new int[size];
230 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700231 // Kill processes holding references first
232 ActivityManagerService ams = (ActivityManagerService)
233 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800234 for (int i = 0; i < size; i++) {
235 UnmountCallBack ucb = mForceUnmounts.get(i);
236 String path = ucb.path;
237 boolean done = false;
238 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800239 done = true;
240 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800241 int pids[] = getStorageUsers(path);
242 if (pids == null || pids.length == 0) {
243 done = true;
244 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800245 // Eliminate system process here?
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700246 ams.killPids(pids, "unmount media");
247 // Confirm if file references have been freed.
248 pids = getStorageUsers(path);
249 if (pids == null || pids.length == 0) {
250 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800251 }
252 }
253 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700254 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
255 // Retry again
256 Slog.i(TAG, "Retrying to kill storage users again");
257 mHandler.sendMessageDelayed(
258 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
259 ucb.retries++),
260 RETRY_UNMOUNT_DELAY);
261 } else {
262 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
263 Slog.i(TAG, "Failed to unmount media inspite of " +
264 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
265 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800266 sizeArr[sizeArrN++] = i;
267 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
268 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800269 }
270 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800271 // Remove already processed elements from list.
272 for (int i = (sizeArrN-1); i >= 0; i--) {
273 mForceUnmounts.remove(sizeArr[i]);
274 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800275 break;
276 }
277 case H_UNMOUNT_MS : {
San Mehata5078592010-03-25 09:36:54 -0700278 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800279 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800280 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800281 break;
282 }
283 }
284 }
285 };
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400286 final private HandlerThread mHandlerThread;
287 final private Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800288
San Mehat207e5382010-02-04 20:46:54 -0800289 private void waitForReady() {
290 while (mReady == false) {
291 for (int retries = 5; retries > 0; retries--) {
292 if (mReady) {
293 return;
294 }
295 SystemClock.sleep(1000);
296 }
San Mehata5078592010-03-25 09:36:54 -0700297 Slog.w(TAG, "Waiting too long for mReady!");
San Mehat207e5382010-02-04 20:46:54 -0800298 }
San Mehat1f6301e2010-01-07 22:40:27 -0800299 }
Kenny Root02c87302010-07-01 08:10:18 -0700300
San Mehat207e5382010-02-04 20:46:54 -0800301 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800303 String action = intent.getAction();
304
305 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800306 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800307
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800308 /*
309 * In the simulator, we need to broadcast a volume mounted event
310 * to make the media scanner run.
311 */
312 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
313 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
314 return;
315 }
San Mehatfafb0412010-02-18 19:40:04 -0800316 new Thread() {
317 public void run() {
318 try {
319 String path = Environment.getExternalStorageDirectory().getPath();
San Mehat6a254402010-03-22 10:21:00 -0700320 String state = getVolumeState(path);
321
322 if (state.equals(Environment.MEDIA_UNMOUNTED)) {
San Mehatfafb0412010-02-18 19:40:04 -0800323 int rc = doMountVolume(path);
324 if (rc != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700325 Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
San Mehatfafb0412010-02-18 19:40:04 -0800326 }
San Mehat6a254402010-03-22 10:21:00 -0700327 } else if (state.equals(Environment.MEDIA_SHARED)) {
328 /*
329 * Bootstrap UMS enabled state since vold indicates
330 * the volume is shared (runtime restart while ums enabled)
331 */
332 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
San Mehatfafb0412010-02-18 19:40:04 -0800333 }
San Mehat6a254402010-03-22 10:21:00 -0700334
San Mehat6a965af22010-02-24 17:47:30 -0800335 /*
San Mehat6a254402010-03-22 10:21:00 -0700336 * If UMS was connected on boot, send the connected event
San Mehat6a965af22010-02-24 17:47:30 -0800337 * now that we're up.
338 */
339 if (mSendUmsConnectedOnBoot) {
340 sendUmsIntent(true);
341 mSendUmsConnectedOnBoot = false;
342 }
San Mehatfafb0412010-02-18 19:40:04 -0800343 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700344 Slog.e(TAG, "Boot-time mount exception", ex);
San Mehatfafb0412010-02-18 19:40:04 -0800345 }
San Mehat207e5382010-02-04 20:46:54 -0800346 }
San Mehatfafb0412010-02-18 19:40:04 -0800347 }.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
349 }
350 };
351
San Mehat4270e1e2010-01-29 05:32:19 -0800352 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
353 final IMountServiceListener mListener;
354
355 MountServiceBinderListener(IMountServiceListener listener) {
356 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700357
San Mehat91c77612010-01-07 10:39:41 -0800358 }
359
San Mehat4270e1e2010-01-29 05:32:19 -0800360 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700361 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
San Mehat4270e1e2010-01-29 05:32:19 -0800362 synchronized(mListeners) {
363 mListeners.remove(this);
364 mListener.asBinder().unlinkToDeath(this, 0);
365 }
366 }
367 }
368
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800369 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800370 // TODO: Add support for multiple share methods
371 if (!method.equals("ums")) {
372 throw new IllegalArgumentException(String.format("Method %s not supported", method));
373 }
374
San Mehat4270e1e2010-01-29 05:32:19 -0800375 try {
376 mConnector.doCommand(String.format(
377 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
378 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700379 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800380 }
San Mehat4270e1e2010-01-29 05:32:19 -0800381 }
382
San Mehat207e5382010-02-04 20:46:54 -0800383 private void updatePublicVolumeState(String path, String state) {
San Mehat4270e1e2010-01-29 05:32:19 -0800384 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -0700385 Slog.w(TAG, "Multiple volumes not currently supported");
San Mehat4270e1e2010-01-29 05:32:19 -0800386 return;
387 }
San Mehatb1043402010-02-05 08:26:50 -0800388
389 if (mLegacyState.equals(state)) {
San Mehata5078592010-03-25 09:36:54 -0700390 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
San Mehatb1043402010-02-05 08:26:50 -0800391 return;
392 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800393 // Update state on PackageManager
394 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700395 mPms.updateExternalMediaStatus(false, false);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800396 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700397 mPms.updateExternalMediaStatus(true, false);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800398 }
San Mehat4270e1e2010-01-29 05:32:19 -0800399 String oldState = mLegacyState;
400 mLegacyState = state;
401
402 synchronized (mListeners) {
403 for (int i = mListeners.size() -1; i >= 0; i--) {
404 MountServiceBinderListener bl = mListeners.get(i);
405 try {
San Mehatb1043402010-02-05 08:26:50 -0800406 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800407 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700408 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800409 mListeners.remove(i);
410 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700411 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800412 }
413 }
414 }
415 }
416
417 /**
418 *
419 * Callback from NativeDaemonConnector
420 */
421 public void onDaemonConnected() {
422 /*
423 * Since we'll be calling back into the NativeDaemonConnector,
424 * we need to do our work in a new thread.
425 */
426 new Thread() {
427 public void run() {
428 /**
429 * Determine media state and UMS detection status
430 */
431 String path = Environment.getExternalStorageDirectory().getPath();
432 String state = Environment.MEDIA_REMOVED;
433
434 try {
435 String[] vols = mConnector.doListCommand(
436 "volume list", VoldResponseCode.VolumeListResult);
437 for (String volstr : vols) {
438 String[] tok = volstr.split(" ");
439 // FMT: <label> <mountpoint> <state>
440 if (!tok[1].equals(path)) {
San Mehata5078592010-03-25 09:36:54 -0700441 Slog.w(TAG, String.format(
San Mehat4270e1e2010-01-29 05:32:19 -0800442 "Skipping unknown volume '%s'",tok[1]));
443 continue;
444 }
445 int st = Integer.parseInt(tok[2]);
446 if (st == VolumeState.NoMedia) {
447 state = Environment.MEDIA_REMOVED;
448 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800449 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800450 } else if (st == VolumeState.Mounted) {
451 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700452 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800453 } else if (st == VolumeState.Shared) {
454 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700455 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800456 } else {
457 throw new Exception(String.format("Unexpected state %d", st));
458 }
459 }
460 if (state != null) {
San Mehata5078592010-03-25 09:36:54 -0700461 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
San Mehat4270e1e2010-01-29 05:32:19 -0800462 updatePublicVolumeState(path, state);
463 }
464 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700465 Slog.e(TAG, "Error processing initial volume state", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800466 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
467 }
468
469 try {
San Mehat207e5382010-02-04 20:46:54 -0800470 boolean avail = doGetShareMethodAvailable("ums");
San Mehat4270e1e2010-01-29 05:32:19 -0800471 notifyShareAvailabilityChange("ums", avail);
472 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700473 Slog.w(TAG, "Failed to get share availability");
San Mehat4270e1e2010-01-29 05:32:19 -0800474 }
San Mehat207e5382010-02-04 20:46:54 -0800475 /*
476 * Now that we've done our initialization, release
477 * the hounds!
478 */
479 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800480 }
481 }.start();
482 }
483
484 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800485 * Callback from NativeDaemonConnector
486 */
487 public boolean onEvent(int code, String raw, String[] cooked) {
488 Intent in = null;
489
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800490 if (DEBUG_EVENTS) {
491 StringBuilder builder = new StringBuilder();
492 builder.append("onEvent::");
493 builder.append(" raw= " + raw);
494 if (cooked != null) {
495 builder.append(" cooked = " );
496 for (String str : cooked) {
497 builder.append(" " + str);
498 }
499 }
San Mehata5078592010-03-25 09:36:54 -0700500 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800501 }
San Mehat4270e1e2010-01-29 05:32:19 -0800502 if (code == VoldResponseCode.VolumeStateChange) {
503 /*
504 * One of the volumes we're managing has changed state.
505 * Format: "NNN Volume <label> <path> state changed
506 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
507 */
508 notifyVolumeStateChange(
509 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
510 Integer.parseInt(cooked[10]));
511 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
512 // FMT: NNN Share method <method> now <available|unavailable>
513 boolean avail = false;
514 if (cooked[5].equals("available")) {
515 avail = true;
516 }
517 notifyShareAvailabilityChange(cooked[3], avail);
518 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
519 (code == VoldResponseCode.VolumeDiskRemoved) ||
520 (code == VoldResponseCode.VolumeBadRemoval)) {
521 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
522 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
523 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
524 final String label = cooked[2];
525 final String path = cooked[3];
526 int major = -1;
527 int minor = -1;
528
529 try {
530 String devComp = cooked[6].substring(1, cooked[6].length() -1);
531 String[] devTok = devComp.split(":");
532 major = Integer.parseInt(devTok[0]);
533 minor = Integer.parseInt(devTok[1]);
534 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700535 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800536 }
537
San Mehat4270e1e2010-01-29 05:32:19 -0800538 if (code == VoldResponseCode.VolumeDiskInserted) {
539 new Thread() {
540 public void run() {
541 try {
542 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800543 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700544 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800545 }
546 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700547 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800548 }
549 }
550 }.start();
551 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
552 /*
553 * This event gets trumped if we're already in BAD_REMOVAL state
554 */
555 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
556 return true;
557 }
558 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700559 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800560 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
561 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
562 mContext.sendBroadcast(in);
563
San Mehata5078592010-03-25 09:36:54 -0700564 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
San Mehat4270e1e2010-01-29 05:32:19 -0800565 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
566 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
567 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700568 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800569 /* Send the media unmounted event first */
570 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
571 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
572 mContext.sendBroadcast(in);
573
San Mehata5078592010-03-25 09:36:54 -0700574 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
San Mehat4270e1e2010-01-29 05:32:19 -0800575 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
576 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
577 } else {
San Mehata5078592010-03-25 09:36:54 -0700578 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800579 }
580 } else {
581 return false;
582 }
583
584 if (in != null) {
585 mContext.sendBroadcast(in);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400586 }
587 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800588 }
589
San Mehat207e5382010-02-04 20:46:54 -0800590 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800591 String vs = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -0700592 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
San Mehat4270e1e2010-01-29 05:32:19 -0800593
594 Intent in = null;
595
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500596 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700597 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500598 mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
599 Uri.parse("file://" + path)));
600 }
601
San Mehat4270e1e2010-01-29 05:32:19 -0800602 if (newState == VolumeState.Init) {
603 } else if (newState == VolumeState.NoMedia) {
604 // NoMedia is handled via Disk Remove events
605 } else if (newState == VolumeState.Idle) {
606 /*
607 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
608 * if we're in the process of enabling UMS
609 */
610 if (!vs.equals(
611 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
612 Environment.MEDIA_NOFS) && !vs.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800613 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700614 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
San Mehat4270e1e2010-01-29 05:32:19 -0800615 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
616 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
617 }
618 } else if (newState == VolumeState.Pending) {
619 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -0700620 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
San Mehat4270e1e2010-01-29 05:32:19 -0800621 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
622 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
623 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -0700624 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800625 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
San Mehat4270e1e2010-01-29 05:32:19 -0800626 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
627 in.putExtra("read-only", false);
628 } else if (newState == VolumeState.Unmounting) {
San Mehat4270e1e2010-01-29 05:32:19 -0800629 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
630 } else if (newState == VolumeState.Formatting) {
631 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -0700632 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800633 /* Send the media unmounted event first */
634 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
635 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
636 mContext.sendBroadcast(in);
637
San Mehata5078592010-03-25 09:36:54 -0700638 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
San Mehat4270e1e2010-01-29 05:32:19 -0800639 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
640 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
San Mehata5078592010-03-25 09:36:54 -0700641 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -0800642 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -0700643 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -0800644 return;
645 } else {
San Mehata5078592010-03-25 09:36:54 -0700646 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800647 }
648
649 if (in != null) {
650 mContext.sendBroadcast(in);
651 }
652 }
653
San Mehat207e5382010-02-04 20:46:54 -0800654 private boolean doGetShareMethodAvailable(String method) {
Kenny Root85fb2062010-06-01 20:50:21 -0700655 ArrayList<String> rsp;
Kenny Roota80ce062010-06-01 13:23:53 -0700656 try {
Kenny Root85fb2062010-06-01 20:50:21 -0700657 rsp = mConnector.doCommand("share status " + method);
Kenny Roota80ce062010-06-01 13:23:53 -0700658 } catch (NativeDaemonConnectorException ex) {
659 Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
660 return false;
661 }
San Mehat207e5382010-02-04 20:46:54 -0800662
663 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700664 String[] tok = line.split(" ");
665 if (tok.length < 3) {
666 Slog.e(TAG, "Malformed response to share status " + method);
667 return false;
668 }
669
San Mehat207e5382010-02-04 20:46:54 -0800670 int code;
671 try {
672 code = Integer.parseInt(tok[0]);
673 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700674 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehat207e5382010-02-04 20:46:54 -0800675 return false;
676 }
677 if (code == VoldResponseCode.ShareStatusResult) {
678 if (tok[2].equals("available"))
679 return true;
680 return false;
681 } else {
San Mehata5078592010-03-25 09:36:54 -0700682 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehat207e5382010-02-04 20:46:54 -0800683 return false;
684 }
685 }
San Mehata5078592010-03-25 09:36:54 -0700686 Slog.e(TAG, "Got an empty response");
San Mehat207e5382010-02-04 20:46:54 -0800687 return false;
688 }
689
690 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800691 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800692
San Mehata5078592010-03-25 09:36:54 -0700693 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -0800694 try {
695 mConnector.doCommand(String.format("volume mount %s", path));
696 } catch (NativeDaemonConnectorException e) {
697 /*
698 * Mount failed for some reason
699 */
700 Intent in = null;
701 int code = e.getCode();
702 if (code == VoldResponseCode.OpFailedNoMedia) {
703 /*
704 * Attempt to mount but no media inserted
705 */
San Mehatb1043402010-02-05 08:26:50 -0800706 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800707 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -0700708 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -0800709 /*
710 * Media is blank or does not contain a supported filesystem
711 */
712 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
713 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800714 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800715 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -0700716 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -0800717 /*
718 * Volume consistency check failed
719 */
720 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
721 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800722 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800723 } else {
San Mehatb1043402010-02-05 08:26:50 -0800724 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800725 }
726
727 /*
728 * Send broadcast intent (if required for the failure)
729 */
730 if (in != null) {
731 mContext.sendBroadcast(in);
732 }
733 }
734
735 return rc;
736 }
737
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800738 /*
739 * If force is not set, we do not unmount if there are
740 * processes holding references to the volume about to be unmounted.
741 * If force is set, all the processes holding references need to be
742 * killed via the ActivityManager before actually unmounting the volume.
743 * This might even take a while and might be retried after timed delays
744 * to make sure we dont end up in an instable state and kill some core
745 * processes.
746 */
San Mehatd9709982010-02-18 11:43:03 -0800747 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800748 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800749 return VoldResponseCode.OpFailedVolNotMounted;
750 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800751 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700752 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -0800753 try {
San Mehatd9709982010-02-18 11:43:03 -0800754 mConnector.doCommand(String.format(
755 "volume unmount %s%s", path, (force ? " force" : "")));
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700756 // We unmounted the volume. None of the asec containers are available now.
757 synchronized (mAsecMountSet) {
758 mAsecMountSet.clear();
759 }
San Mehatb1043402010-02-05 08:26:50 -0800760 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800761 } catch (NativeDaemonConnectorException e) {
762 // Don't worry about mismatch in PackageManager since the
763 // call back will handle the status changes any way.
764 int code = e.getCode();
765 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800766 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800767 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
768 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -0800769 } else {
San Mehatb1043402010-02-05 08:26:50 -0800770 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800771 }
772 }
773 }
774
775 private int doFormatVolume(String path) {
776 try {
777 String cmd = String.format("volume format %s", path);
778 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800779 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800780 } catch (NativeDaemonConnectorException e) {
781 int code = e.getCode();
782 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800783 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800784 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800785 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800786 } else {
San Mehatb1043402010-02-05 08:26:50 -0800787 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800788 }
789 }
790 }
791
San Mehatb1043402010-02-05 08:26:50 -0800792 private boolean doGetVolumeShared(String path, String method) {
793 String cmd = String.format("volume shared %s %s", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -0700794 ArrayList<String> rsp;
795
796 try {
797 rsp = mConnector.doCommand(cmd);
798 } catch (NativeDaemonConnectorException ex) {
799 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
800 return false;
801 }
San Mehatb1043402010-02-05 08:26:50 -0800802
803 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700804 String[] tok = line.split(" ");
805 if (tok.length < 3) {
806 Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
807 return false;
808 }
809
San Mehatb1043402010-02-05 08:26:50 -0800810 int code;
811 try {
812 code = Integer.parseInt(tok[0]);
813 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700814 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehatb1043402010-02-05 08:26:50 -0800815 return false;
816 }
817 if (code == VoldResponseCode.ShareEnabledResult) {
Kenny Roota80ce062010-06-01 13:23:53 -0700818 return "enabled".equals(tok[2]);
San Mehatb1043402010-02-05 08:26:50 -0800819 } else {
San Mehata5078592010-03-25 09:36:54 -0700820 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehatb1043402010-02-05 08:26:50 -0800821 return false;
822 }
823 }
San Mehata5078592010-03-25 09:36:54 -0700824 Slog.e(TAG, "Got an empty response");
San Mehatb1043402010-02-05 08:26:50 -0800825 return false;
826 }
827
San Mehat207e5382010-02-04 20:46:54 -0800828 private void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800829 if (!method.equals("ums")) {
San Mehata5078592010-03-25 09:36:54 -0700830 Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800831 return;
832 }
833
834 synchronized (mListeners) {
835 for (int i = mListeners.size() -1; i >= 0; i--) {
836 MountServiceBinderListener bl = mListeners.get(i);
837 try {
San Mehatb1043402010-02-05 08:26:50 -0800838 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800839 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700840 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800841 mListeners.remove(i);
842 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700843 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800844 }
845 }
846 }
847
San Mehat207e5382010-02-04 20:46:54 -0800848 if (mBooted == true) {
San Mehat6a965af22010-02-24 17:47:30 -0800849 sendUmsIntent(avail);
850 } else {
851 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -0800852 }
San Mehat2fe718a2010-03-11 12:01:49 -0800853
854 final String path = Environment.getExternalStorageDirectory().getPath();
855 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
856 /*
857 * USB mass storage disconnected while enabled
858 */
859 new Thread() {
860 public void run() {
861 try {
862 int rc;
San Mehata5078592010-03-25 09:36:54 -0700863 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -0800864 doShareUnshareVolume(path, "ums", false);
865 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700866 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -0800867 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
868 path, rc));
869 }
870 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700871 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -0800872 }
873 }
874 }.start();
875 }
San Mehat4270e1e2010-01-29 05:32:19 -0800876 }
877
San Mehat6a965af22010-02-24 17:47:30 -0800878 private void sendUmsIntent(boolean c) {
879 mContext.sendBroadcast(
880 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
881 }
882
San Mehat207e5382010-02-04 20:46:54 -0800883 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -0800884 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
885 throw new SecurityException(String.format("Requires %s permission", perm));
886 }
887 }
888
889 /**
San Mehat207e5382010-02-04 20:46:54 -0800890 * Constructs a new MountService instance
891 *
892 * @param context Binder context for this service
893 */
894 public MountService(Context context) {
895 mContext = context;
896
San Mehat207e5382010-02-04 20:46:54 -0800897 // XXX: This will go away soon in favor of IMountServiceObserver
898 mPms = (PackageManagerService) ServiceManager.getService("package");
899
900 mContext.registerReceiver(mBroadcastReceiver,
901 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
902
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400903 mHandlerThread = new HandlerThread("MountService");
904 mHandlerThread.start();
905 mHandler = new MountServiceHandler(mHandlerThread.getLooper());
906
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800907 /*
908 * Vold does not run in the simulator, so pretend the connector thread
909 * ran and did its thing.
910 */
911 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
912 mReady = true;
913 mUmsEnabling = true;
914 return;
915 }
916
San Mehat207e5382010-02-04 20:46:54 -0800917 mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
918 mReady = false;
919 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
920 thread.start();
921 }
922
923 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800924 * Exposed API calls below here
925 */
926
927 public void registerListener(IMountServiceListener listener) {
928 synchronized (mListeners) {
929 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
930 try {
931 listener.asBinder().linkToDeath(bl, 0);
932 mListeners.add(bl);
933 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700934 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -0800935 }
936 }
937 }
938
939 public void unregisterListener(IMountServiceListener listener) {
940 synchronized (mListeners) {
941 for(MountServiceBinderListener bl : mListeners) {
942 if (bl.mListener == listener) {
943 mListeners.remove(mListeners.indexOf(bl));
944 return;
945 }
946 }
947 }
948 }
949
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800950 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -0800951 validatePermission(android.Manifest.permission.SHUTDOWN);
952
San Mehata5078592010-03-25 09:36:54 -0700953 Slog.i(TAG, "Shutting down");
San Mehat4270e1e2010-01-29 05:32:19 -0800954
955 String path = Environment.getExternalStorageDirectory().getPath();
956 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -0800957
958 if (state.equals(Environment.MEDIA_SHARED)) {
959 /*
960 * If the media is currently shared, unshare it.
961 * XXX: This is still dangerous!. We should not
962 * be rebooting at *all* if UMS is enabled, since
963 * the UMS host could have dirty FAT cache entries
964 * yet to flush.
965 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800966 setUsbMassStorageEnabled(false);
San Mehat91c77612010-01-07 10:39:41 -0800967 } else if (state.equals(Environment.MEDIA_CHECKING)) {
968 /*
969 * If the media is being checked, then we need to wait for
970 * it to complete before being able to proceed.
971 */
972 // XXX: @hackbod - Should we disable the ANR timer here?
973 int retries = 30;
974 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
975 try {
976 Thread.sleep(1000);
977 } catch (InterruptedException iex) {
San Mehata5078592010-03-25 09:36:54 -0700978 Slog.e(TAG, "Interrupted while waiting for media", iex);
San Mehat91c77612010-01-07 10:39:41 -0800979 break;
980 }
981 state = Environment.getExternalStorageState();
982 }
983 if (retries == 0) {
San Mehata5078592010-03-25 09:36:54 -0700984 Slog.e(TAG, "Timed out waiting for media to check");
San Mehat91c77612010-01-07 10:39:41 -0800985 }
986 }
987
988 if (state.equals(Environment.MEDIA_MOUNTED)) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800989 // Post a unmount message.
990 ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
991 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
San Mehat4270e1e2010-01-29 05:32:19 -0800992 }
993 }
994
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800995 private boolean getUmsEnabling() {
996 synchronized (mListeners) {
997 return mUmsEnabling;
998 }
999 }
1000
1001 private void setUmsEnabling(boolean enable) {
1002 synchronized (mListeners) {
1003 mUmsEnabling = true;
1004 }
1005 }
1006
San Mehatb1043402010-02-05 08:26:50 -08001007 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001008 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001009
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001010 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001011 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001012 }
San Mehatb1043402010-02-05 08:26:50 -08001013 return doGetShareMethodAvailable("ums");
1014 }
1015
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001016 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001017 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001018 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -08001019
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001020 // TODO: Add support for multiple share methods
1021
1022 /*
1023 * If the volume is mounted and we're enabling then unmount it
1024 */
1025 String path = Environment.getExternalStorageDirectory().getPath();
1026 String vs = getVolumeState(path);
1027 String method = "ums";
1028 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1029 // Override for isUsbMassStorageEnabled()
1030 setUmsEnabling(enable);
1031 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1032 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1033 // Clear override
1034 setUmsEnabling(false);
1035 }
1036 /*
1037 * If we disabled UMS then mount the volume
1038 */
1039 if (!enable) {
1040 doShareUnshareVolume(path, method, enable);
1041 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001042 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001043 " after disabling share method " + method);
1044 /*
1045 * Even though the mount failed, the unshare didn't so don't indicate an error.
1046 * The mountVolume() call will have set the storage state and sent the necessary
1047 * broadcasts.
1048 */
1049 }
1050 }
San Mehatb1043402010-02-05 08:26:50 -08001051 }
1052
1053 public boolean isUsbMassStorageEnabled() {
1054 waitForReady();
1055 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 }
San Mehat4270e1e2010-01-29 05:32:19 -08001057
San Mehat7fd0fee2009-12-17 07:12:23 -08001058 /**
1059 * @return state of the volume at the specified mount point
1060 */
San Mehat4270e1e2010-01-29 05:32:19 -08001061 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -08001062 /*
1063 * XXX: Until we have multiple volume discovery, just hardwire
1064 * this to /sdcard
1065 */
1066 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -07001067 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
San Mehat7fd0fee2009-12-17 07:12:23 -08001068 throw new IllegalArgumentException();
1069 }
1070
1071 return mLegacyState;
1072 }
1073
San Mehat4270e1e2010-01-29 05:32:19 -08001074 public int mountVolume(String path) {
1075 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001076
San Mehat207e5382010-02-04 20:46:54 -08001077 waitForReady();
1078 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 }
1080
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001081 public void unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001082 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001083 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001085 String volState = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -07001086 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001087 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1088 Environment.MEDIA_REMOVED.equals(volState) ||
1089 Environment.MEDIA_SHARED.equals(volState) ||
1090 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1091 // Media already unmounted or cannot be unmounted.
1092 // TODO return valid return code when adding observer call back.
1093 return;
1094 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001095 UnmountCallBack ucb = new UnmountCallBack(path, force);
1096 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 }
1098
San Mehat4270e1e2010-01-29 05:32:19 -08001099 public int formatVolume(String path) {
1100 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001101 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001102
San Mehat207e5382010-02-04 20:46:54 -08001103 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 }
1105
San Mehatc1b4ce92010-02-16 17:13:03 -08001106 public int []getStorageUsers(String path) {
1107 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1108 waitForReady();
1109 try {
1110 String[] r = mConnector.doListCommand(
1111 String.format("storage users %s", path),
1112 VoldResponseCode.StorageUsersListResult);
1113 // FMT: <pid> <process name>
1114 int[] data = new int[r.length];
1115 for (int i = 0; i < r.length; i++) {
1116 String []tok = r[i].split(" ");
1117 try {
1118 data[i] = Integer.parseInt(tok[0]);
1119 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001120 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001121 return new int[0];
1122 }
1123 }
1124 return data;
1125 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001126 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001127 return new int[0];
1128 }
1129 }
1130
San Mehatb1043402010-02-05 08:26:50 -08001131 private void warnOnNotMounted() {
1132 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
San Mehata5078592010-03-25 09:36:54 -07001133 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
San Mehatb1043402010-02-05 08:26:50 -08001134 }
1135 }
1136
San Mehat4270e1e2010-01-29 05:32:19 -08001137 public String[] getSecureContainerList() {
1138 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001139 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001140 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001141
San Mehat4270e1e2010-01-29 05:32:19 -08001142 try {
1143 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
1144 } catch (NativeDaemonConnectorException e) {
1145 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 }
1147 }
San Mehat36972292010-01-06 11:06:32 -08001148
San Mehat4270e1e2010-01-29 05:32:19 -08001149 public int createSecureContainer(String id, int sizeMb, String fstype,
1150 String key, int ownerUid) {
1151 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001152 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001153 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001154
San Mehatb1043402010-02-05 08:26:50 -08001155 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001156 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
1157 try {
1158 mConnector.doCommand(cmd);
1159 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001160 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001161 }
San Mehata181b212010-02-11 06:50:20 -08001162
1163 if (rc == StorageResultCode.OperationSucceeded) {
1164 synchronized (mAsecMountSet) {
1165 mAsecMountSet.add(id);
1166 }
1167 }
San Mehat4270e1e2010-01-29 05:32:19 -08001168 return rc;
San Mehat36972292010-01-06 11:06:32 -08001169 }
1170
San Mehat4270e1e2010-01-29 05:32:19 -08001171 public int finalizeSecureContainer(String id) {
1172 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001173 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001174
San Mehatb1043402010-02-05 08:26:50 -08001175 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001176 try {
1177 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -08001178 /*
1179 * Finalization does a remount, so no need
1180 * to update mAsecMountSet
1181 */
San Mehat4270e1e2010-01-29 05:32:19 -08001182 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001183 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001184 }
San Mehat4270e1e2010-01-29 05:32:19 -08001185 return rc;
San Mehat36972292010-01-06 11:06:32 -08001186 }
1187
San Mehatd9709982010-02-18 11:43:03 -08001188 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001189 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001190 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001191 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001192
San Mehatb1043402010-02-05 08:26:50 -08001193 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001194 try {
San Mehatd9709982010-02-18 11:43:03 -08001195 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -08001196 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001197 int code = e.getCode();
1198 if (code == VoldResponseCode.OpFailedStorageBusy) {
1199 rc = StorageResultCode.OperationFailedStorageBusy;
1200 } else {
1201 rc = StorageResultCode.OperationFailedInternalError;
1202 }
San Mehat02735bc2010-01-26 15:18:08 -08001203 }
San Mehata181b212010-02-11 06:50:20 -08001204
1205 if (rc == StorageResultCode.OperationSucceeded) {
1206 synchronized (mAsecMountSet) {
1207 if (mAsecMountSet.contains(id)) {
1208 mAsecMountSet.remove(id);
1209 }
1210 }
1211 }
1212
San Mehat4270e1e2010-01-29 05:32:19 -08001213 return rc;
San Mehat36972292010-01-06 11:06:32 -08001214 }
1215
San Mehat4270e1e2010-01-29 05:32:19 -08001216 public int mountSecureContainer(String id, String key, int ownerUid) {
1217 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001218 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001219 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001220
San Mehata181b212010-02-11 06:50:20 -08001221 synchronized (mAsecMountSet) {
1222 if (mAsecMountSet.contains(id)) {
1223 return StorageResultCode.OperationFailedStorageMounted;
1224 }
1225 }
1226
San Mehatb1043402010-02-05 08:26:50 -08001227 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001228 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
1229 try {
1230 mConnector.doCommand(cmd);
1231 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001232 int code = e.getCode();
1233 if (code != VoldResponseCode.OpFailedStorageBusy) {
1234 rc = StorageResultCode.OperationFailedInternalError;
1235 }
San Mehat02735bc2010-01-26 15:18:08 -08001236 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001237
1238 if (rc == StorageResultCode.OperationSucceeded) {
1239 synchronized (mAsecMountSet) {
1240 mAsecMountSet.add(id);
1241 }
1242 }
San Mehat4270e1e2010-01-29 05:32:19 -08001243 return rc;
San Mehat36972292010-01-06 11:06:32 -08001244 }
1245
San Mehatd9709982010-02-18 11:43:03 -08001246 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001247 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001248 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001249 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001250
San Mehat6cdd9c02010-02-09 14:45:20 -08001251 synchronized (mAsecMountSet) {
1252 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001253 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001254 }
1255 }
1256
San Mehatb1043402010-02-05 08:26:50 -08001257 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -08001258 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -08001259 try {
1260 mConnector.doCommand(cmd);
1261 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001262 int code = e.getCode();
1263 if (code == VoldResponseCode.OpFailedStorageBusy) {
1264 rc = StorageResultCode.OperationFailedStorageBusy;
1265 } else {
1266 rc = StorageResultCode.OperationFailedInternalError;
1267 }
San Mehat02735bc2010-01-26 15:18:08 -08001268 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001269
1270 if (rc == StorageResultCode.OperationSucceeded) {
1271 synchronized (mAsecMountSet) {
1272 mAsecMountSet.remove(id);
1273 }
1274 }
San Mehat4270e1e2010-01-29 05:32:19 -08001275 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001276 }
1277
San Mehat6cdd9c02010-02-09 14:45:20 -08001278 public boolean isSecureContainerMounted(String id) {
1279 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1280 waitForReady();
1281 warnOnNotMounted();
1282
1283 synchronized (mAsecMountSet) {
1284 return mAsecMountSet.contains(id);
1285 }
1286 }
1287
San Mehat4270e1e2010-01-29 05:32:19 -08001288 public int renameSecureContainer(String oldId, String newId) {
1289 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001290 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001291 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001292
San Mehata181b212010-02-11 06:50:20 -08001293 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001294 /*
1295 * Because a mounted container has active internal state which cannot be
1296 * changed while active, we must ensure both ids are not currently mounted.
1297 */
1298 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001299 return StorageResultCode.OperationFailedStorageMounted;
1300 }
1301 }
1302
San Mehatb1043402010-02-05 08:26:50 -08001303 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001304 String cmd = String.format("asec rename %s %s", oldId, newId);
1305 try {
1306 mConnector.doCommand(cmd);
1307 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001308 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001309 }
San Mehata181b212010-02-11 06:50:20 -08001310
San Mehat4270e1e2010-01-29 05:32:19 -08001311 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001312 }
1313
San Mehat4270e1e2010-01-29 05:32:19 -08001314 public String getSecureContainerPath(String id) {
1315 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001316 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001317 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001318
San Mehat2d66cef2010-03-23 11:12:52 -07001319 try {
1320 ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
1321 String []tok = rsp.get(0).split(" ");
San Mehat22dd86e2010-01-12 12:21:18 -08001322 int code = Integer.parseInt(tok[0]);
San Mehat2d66cef2010-03-23 11:12:52 -07001323 if (code != VoldResponseCode.AsecPathResult) {
1324 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1325 }
1326 return tok[1];
1327 } catch (NativeDaemonConnectorException e) {
1328 int code = e.getCode();
1329 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1330 throw new IllegalArgumentException(String.format("Container '%s' not found", id));
San Mehat22dd86e2010-01-12 12:21:18 -08001331 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001332 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001333 }
1334 }
San Mehat22dd86e2010-01-12 12:21:18 -08001335 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001336
1337 public void finishMediaUpdate() {
1338 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
1339 }
Kenny Root02c87302010-07-01 08:10:18 -07001340
1341 private boolean isCallerOwnerOfPackage(String packageName) {
1342 final int callerUid = Binder.getCallingUid();
1343 return isUidOwnerOfPackage(packageName, callerUid);
1344 }
1345
1346 private boolean isUidOwnerOfPackage(String packageName, int callerUid) {
1347 if (packageName == null) {
1348 return false;
1349 }
1350
1351 final int packageUid = mPms.getPackageUid(packageName);
1352
1353 if (DEBUG_OBB) {
1354 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1355 packageUid + ", callerUid = " + callerUid);
1356 }
1357
1358 return callerUid == packageUid;
1359 }
1360
1361 public String getMountedObbPath(String filename) {
1362 waitForReady();
1363 warnOnNotMounted();
1364
1365 // XXX replace with call to IMediaContainerService
1366 ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
1367 if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
1368 throw new IllegalArgumentException("Caller package does not match OBB file");
1369 }
1370
1371 try {
1372 ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
1373 String []tok = rsp.get(0).split(" ");
1374 int code = Integer.parseInt(tok[0]);
1375 if (code != VoldResponseCode.AsecPathResult) {
1376 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1377 }
1378 return tok[1];
1379 } catch (NativeDaemonConnectorException e) {
1380 int code = e.getCode();
1381 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1382 throw new IllegalArgumentException(String.format("OBB '%s' not found", filename));
1383 } else {
1384 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1385 }
1386 }
1387 }
1388
1389 public boolean isObbMounted(String filename) {
1390 waitForReady();
1391 warnOnNotMounted();
1392
1393 // XXX replace with call to IMediaContainerService
1394 ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
1395 if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
1396 throw new IllegalArgumentException("Caller package does not match OBB file");
1397 }
1398
1399 synchronized (mObbMountSet) {
1400 return mObbMountSet.contains(filename);
1401 }
1402 }
1403
1404 public int mountObb(String filename, String key) {
1405 waitForReady();
1406 warnOnNotMounted();
1407
1408 synchronized (mObbMountSet) {
1409 if (mObbMountSet.contains(filename)) {
1410 return StorageResultCode.OperationFailedStorageMounted;
1411 }
1412 }
1413
1414 final int callerUid = Binder.getCallingUid();
1415
1416 // XXX replace with call to IMediaContainerService
1417 ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
1418 if (!isUidOwnerOfPackage(obbInfo.packageName, callerUid)) {
1419 throw new IllegalArgumentException("Caller package does not match OBB file");
1420 }
1421
1422 if (key == null) {
1423 key = "none";
1424 }
1425
1426 int rc = StorageResultCode.OperationSucceeded;
1427 String cmd = String.format("obb mount %s %s %d", filename, key, callerUid);
1428 try {
1429 mConnector.doCommand(cmd);
1430 } catch (NativeDaemonConnectorException e) {
1431 int code = e.getCode();
1432 if (code != VoldResponseCode.OpFailedStorageBusy) {
1433 rc = StorageResultCode.OperationFailedInternalError;
1434 }
1435 }
1436
1437 if (rc == StorageResultCode.OperationSucceeded) {
1438 synchronized (mObbMountSet) {
1439 mObbMountSet.add(filename);
1440 }
1441 }
1442 return rc;
1443 }
1444
1445 public int unmountObb(String filename, boolean force) {
1446 waitForReady();
1447 warnOnNotMounted();
1448
1449 ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
1450 if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
1451 throw new IllegalArgumentException("Caller package does not match OBB file");
1452 }
1453
1454 synchronized (mObbMountSet) {
1455 if (!mObbMountSet.contains(filename)) {
1456 return StorageResultCode.OperationFailedStorageNotMounted;
1457 }
1458 }
1459
1460 int rc = StorageResultCode.OperationSucceeded;
1461 String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : ""));
1462 try {
1463 mConnector.doCommand(cmd);
1464 } catch (NativeDaemonConnectorException e) {
1465 int code = e.getCode();
1466 if (code == VoldResponseCode.OpFailedStorageBusy) {
1467 rc = StorageResultCode.OperationFailedStorageBusy;
1468 } else {
1469 rc = StorageResultCode.OperationFailedInternalError;
1470 }
1471 }
1472
1473 if (rc == StorageResultCode.OperationSucceeded) {
1474 synchronized (mObbMountSet) {
1475 mObbMountSet.remove(filename);
1476 }
1477 }
1478 return rc;
1479 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001480}
1481