blob: f8611e45030dfdde34ca8f5cdab614f7082f26ab [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
Kenny Roota02b8b02010-08-05 16:14:17 -070019import com.android.internal.app.IMediaContainerService;
Kenny Root735de3b2010-09-30 14:11:39 -070020import com.android.internal.util.HexDump;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080021import com.android.server.am.ActivityManagerService;
22
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070024import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070028import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.pm.PackageManager;
Kenny Root02c87302010-07-01 08:10:18 -070030import android.content.res.ObbInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070032import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070033import android.os.Environment;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080034import android.os.Handler;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040035import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070036import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040037import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080038import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080039import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080040import android.os.ServiceManager;
San Mehat207e5382010-02-04 20:46:54 -080041import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.os.SystemProperties;
Kenny Roota02b8b02010-08-05 16:14:17 -070043import android.os.storage.IMountService;
44import android.os.storage.IMountServiceListener;
45import android.os.storage.IMountShutdownObserver;
46import android.os.storage.IObbActionListener;
Kenny Rootaf9d6672010-10-08 09:21:39 -070047import android.os.storage.OnObbStateChangeListener;
Kenny Roota02b8b02010-08-05 16:14:17 -070048import android.os.storage.StorageResultCode;
Kenny Root735de3b2010-09-30 14:11:39 -070049import android.security.MessageDigest;
San Mehata5078592010-03-25 09:36:54 -070050import android.util.Slog;
Kenny Roota02b8b02010-08-05 16:14:17 -070051
Kenny Root38cf8862010-09-26 14:18:51 -070052import java.io.FileDescriptor;
Kenny Root05105f72010-09-22 17:29:43 -070053import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070054import java.io.PrintWriter;
Kenny Root735de3b2010-09-30 14:11:39 -070055import java.security.NoSuchAlgorithmException;
San Mehat22dd86e2010-01-12 12:21:18 -080056import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -070057import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080058import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -070059import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -070060import java.util.LinkedList;
61import java.util.List;
62import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -070063import java.util.Map.Entry;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065/**
San Mehatb1043402010-02-05 08:26:50 -080066 * MountService implements back-end services for platform storage
67 * management.
68 * @hide - Applications should use android.os.storage.StorageManager
69 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 */
San Mehat22dd86e2010-01-12 12:21:18 -080071class MountService extends IMountService.Stub
72 implements INativeDaemonConnectorCallbacks {
San Mehatb1043402010-02-05 08:26:50 -080073 private static final boolean LOCAL_LOGD = false;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -080074 private static final boolean DEBUG_UNMOUNT = false;
75 private static final boolean DEBUG_EVENTS = false;
Kenny Root02c87302010-07-01 08:10:18 -070076 private static final boolean DEBUG_OBB = true;
77
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private static final String TAG = "MountService";
79
Kenny Root305bcbf2010-09-03 07:56:38 -070080 private static final String VOLD_TAG = "VoldConnector";
81
San Mehat4270e1e2010-01-29 05:32:19 -080082 /*
83 * Internal vold volume state constants
84 */
San Mehat7fd0fee2009-12-17 07:12:23 -080085 class VolumeState {
86 public static final int Init = -1;
87 public static final int NoMedia = 0;
88 public static final int Idle = 1;
89 public static final int Pending = 2;
90 public static final int Checking = 3;
91 public static final int Mounted = 4;
92 public static final int Unmounting = 5;
93 public static final int Formatting = 6;
94 public static final int Shared = 7;
95 public static final int SharedMnt = 8;
96 }
97
San Mehat4270e1e2010-01-29 05:32:19 -080098 /*
99 * Internal vold response code constants
100 */
San Mehat22dd86e2010-01-12 12:21:18 -0800101 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -0800102 /*
103 * 100 series - Requestion action was initiated; expect another reply
104 * before proceeding with a new command.
105 */
San Mehat22dd86e2010-01-12 12:21:18 -0800106 public static final int VolumeListResult = 110;
107 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800108 public static final int StorageUsersListResult = 112;
San Mehat22dd86e2010-01-12 12:21:18 -0800109
San Mehat4270e1e2010-01-29 05:32:19 -0800110 /*
111 * 200 series - Requestion action has been successfully completed.
112 */
113 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800114 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800115 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800116
San Mehat4270e1e2010-01-29 05:32:19 -0800117 /*
118 * 400 series - Command was accepted, but the requested action
119 * did not take place.
120 */
121 public static final int OpFailedNoMedia = 401;
122 public static final int OpFailedMediaBlank = 402;
123 public static final int OpFailedMediaCorrupt = 403;
124 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800125 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700126 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800127
128 /*
129 * 600 series - Unsolicited broadcasts.
130 */
San Mehat22dd86e2010-01-12 12:21:18 -0800131 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800132 public static final int ShareAvailabilityChange = 620;
133 public static final int VolumeDiskInserted = 630;
134 public static final int VolumeDiskRemoved = 631;
135 public static final int VolumeBadRemoval = 632;
136 }
137
San Mehat4270e1e2010-01-29 05:32:19 -0800138 private Context mContext;
139 private NativeDaemonConnector mConnector;
140 private String mLegacyState = Environment.MEDIA_REMOVED;
141 private PackageManagerService mPms;
142 private boolean mUmsEnabling;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800143 // Used as a lock for methods that register/unregister listeners.
144 final private ArrayList<MountServiceBinderListener> mListeners =
145 new ArrayList<MountServiceBinderListener>();
San Mehat6a965af22010-02-24 17:47:30 -0800146 private boolean mBooted = false;
147 private boolean mReady = false;
148 private boolean mSendUmsConnectedOnBoot = false;
Mike Lockwood03559752010-07-19 18:25:03 -0400149 // true if we should fake MEDIA_MOUNTED state for external storage
150 private boolean mEmulateExternalStorage = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800151
San Mehat6cdd9c02010-02-09 14:45:20 -0800152 /**
153 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800154 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800155 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800156 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800157
Kenny Root02c87302010-07-01 08:10:18 -0700158 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700159 * Mounted OBB tracking information. Used to track the current state of all
160 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700161 */
Kenny Root735de3b2010-09-30 14:11:39 -0700162 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
Kenny Roota02b8b02010-08-05 16:14:17 -0700163 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
164
165 class ObbState implements IBinder.DeathRecipient {
Kenny Rootaf9d6672010-10-08 09:21:39 -0700166 public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
Kenny Root735de3b2010-09-30 14:11:39 -0700167 throws RemoteException {
Kenny Roota02b8b02010-08-05 16:14:17 -0700168 this.filename = filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700169 this.callerUid = callerUid;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700170 this.token = token;
171 this.nonce = nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700172 }
173
174 // OBB source filename
Kenny Rootaf9d6672010-10-08 09:21:39 -0700175 String filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700176
177 // Binder.callingUid()
Kenny Root05105f72010-09-22 17:29:43 -0700178 final public int callerUid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700179
Kenny Rootaf9d6672010-10-08 09:21:39 -0700180 // Token of remote Binder caller
181 final IObbActionListener token;
182
183 // Identifier to pass back to the token
184 final int nonce;
Kenny Roota02b8b02010-08-05 16:14:17 -0700185
Kenny Root735de3b2010-09-30 14:11:39 -0700186 public IBinder getBinder() {
187 return token.asBinder();
188 }
189
Kenny Roota02b8b02010-08-05 16:14:17 -0700190 @Override
191 public void binderDied() {
192 ObbAction action = new UnmountObbAction(this, true);
193 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root735de3b2010-09-30 14:11:39 -0700194 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700195
Kenny Root5919ac62010-10-05 09:49:40 -0700196 public void link() throws RemoteException {
197 getBinder().linkToDeath(this, 0);
198 }
199
200 public void unlink() {
Kenny Root735de3b2010-09-30 14:11:39 -0700201 getBinder().unlinkToDeath(this, 0);
Kenny Roota02b8b02010-08-05 16:14:17 -0700202 }
Kenny Root38cf8862010-09-26 14:18:51 -0700203
204 @Override
205 public String toString() {
206 StringBuilder sb = new StringBuilder("ObbState{");
207 sb.append("filename=");
208 sb.append(filename);
209 sb.append(",token=");
210 sb.append(token.toString());
211 sb.append(",callerUid=");
212 sb.append(callerUid);
Kenny Root38cf8862010-09-26 14:18:51 -0700213 sb.append('}');
214 return sb.toString();
215 }
Kenny Roota02b8b02010-08-05 16:14:17 -0700216 }
217
218 // OBB Action Handler
219 final private ObbActionHandler mObbActionHandler;
220
221 // OBB action handler messages
222 private static final int OBB_RUN_ACTION = 1;
223 private static final int OBB_MCS_BOUND = 2;
224 private static final int OBB_MCS_UNBIND = 3;
225 private static final int OBB_MCS_RECONNECT = 4;
Kenny Rootaf9d6672010-10-08 09:21:39 -0700226 private static final int OBB_FLUSH_MOUNT_STATE = 5;
Kenny Roota02b8b02010-08-05 16:14:17 -0700227
228 /*
229 * Default Container Service information
230 */
231 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
232 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
233
234 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
235
236 class DefaultContainerConnection implements ServiceConnection {
237 public void onServiceConnected(ComponentName name, IBinder service) {
238 if (DEBUG_OBB)
239 Slog.i(TAG, "onServiceConnected");
240 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
241 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
242 }
243
244 public void onServiceDisconnected(ComponentName name) {
245 if (DEBUG_OBB)
246 Slog.i(TAG, "onServiceDisconnected");
247 }
248 };
249
250 // Used in the ObbActionHandler
251 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700252
253 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800254 private static final int H_UNMOUNT_PM_UPDATE = 1;
255 private static final int H_UNMOUNT_PM_DONE = 2;
256 private static final int H_UNMOUNT_MS = 3;
257 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
258 private static final int MAX_UNMOUNT_RETRIES = 4;
259
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800260 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700261 final String path;
262 final boolean force;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800263 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800264
265 UnmountCallBack(String path, boolean force) {
266 retries = 0;
267 this.path = path;
268 this.force = force;
269 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800270
271 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700272 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800273 doUnmountVolume(path, true);
274 }
275 }
276
277 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700278 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800279
280 UmsEnableCallBack(String path, String method, boolean force) {
281 super(path, force);
282 this.method = method;
283 }
284
285 @Override
286 void handleFinished() {
287 super.handleFinished();
288 doShareUnshareVolume(path, method, true);
289 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800290 }
291
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800292 class ShutdownCallBack extends UnmountCallBack {
293 IMountShutdownObserver observer;
294 ShutdownCallBack(String path, IMountShutdownObserver observer) {
295 super(path, true);
296 this.observer = observer;
297 }
298
299 @Override
300 void handleFinished() {
301 int ret = doUnmountVolume(path, true);
302 if (observer != null) {
303 try {
304 observer.onShutDownComplete(ret);
305 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700306 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800307 }
308 }
309 }
310 }
311
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400312 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800313 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700314 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800315
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400316 MountServiceHandler(Looper l) {
317 super(l);
318 }
319
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800320 public void handleMessage(Message msg) {
321 switch (msg.what) {
322 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700323 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800324 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
325 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700326 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800327 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700328 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700329 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700330 mUpdatingStatus = true;
331 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800332 }
333 break;
334 }
335 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700336 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700337 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700338 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800339 int size = mForceUnmounts.size();
340 int sizeArr[] = new int[size];
341 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700342 // Kill processes holding references first
343 ActivityManagerService ams = (ActivityManagerService)
344 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800345 for (int i = 0; i < size; i++) {
346 UnmountCallBack ucb = mForceUnmounts.get(i);
347 String path = ucb.path;
348 boolean done = false;
349 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800350 done = true;
351 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800352 int pids[] = getStorageUsers(path);
353 if (pids == null || pids.length == 0) {
354 done = true;
355 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800356 // Eliminate system process here?
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700357 ams.killPids(pids, "unmount media");
358 // Confirm if file references have been freed.
359 pids = getStorageUsers(path);
360 if (pids == null || pids.length == 0) {
361 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800362 }
363 }
364 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700365 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
366 // Retry again
367 Slog.i(TAG, "Retrying to kill storage users again");
368 mHandler.sendMessageDelayed(
369 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
370 ucb.retries++),
371 RETRY_UNMOUNT_DELAY);
372 } else {
373 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
374 Slog.i(TAG, "Failed to unmount media inspite of " +
375 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
376 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800377 sizeArr[sizeArrN++] = i;
378 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
379 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800380 }
381 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800382 // Remove already processed elements from list.
383 for (int i = (sizeArrN-1); i >= 0; i--) {
384 mForceUnmounts.remove(sizeArr[i]);
385 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800386 break;
387 }
388 case H_UNMOUNT_MS : {
San Mehata5078592010-03-25 09:36:54 -0700389 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800390 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800391 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800392 break;
393 }
394 }
395 }
396 };
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400397 final private HandlerThread mHandlerThread;
398 final private Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800399
San Mehat207e5382010-02-04 20:46:54 -0800400 private void waitForReady() {
401 while (mReady == false) {
402 for (int retries = 5; retries > 0; retries--) {
403 if (mReady) {
404 return;
405 }
406 SystemClock.sleep(1000);
407 }
San Mehata5078592010-03-25 09:36:54 -0700408 Slog.w(TAG, "Waiting too long for mReady!");
San Mehat207e5382010-02-04 20:46:54 -0800409 }
San Mehat1f6301e2010-01-07 22:40:27 -0800410 }
Kenny Root02c87302010-07-01 08:10:18 -0700411
San Mehat207e5382010-02-04 20:46:54 -0800412 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800414 String action = intent.getAction();
415
416 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800417 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800418
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800419 /*
420 * In the simulator, we need to broadcast a volume mounted event
421 * to make the media scanner run.
422 */
423 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
424 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
425 return;
426 }
San Mehatfafb0412010-02-18 19:40:04 -0800427 new Thread() {
428 public void run() {
429 try {
430 String path = Environment.getExternalStorageDirectory().getPath();
San Mehat6a254402010-03-22 10:21:00 -0700431 String state = getVolumeState(path);
432
Mike Lockwood03559752010-07-19 18:25:03 -0400433 if (mEmulateExternalStorage) {
434 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Mounted);
435 } else if (state.equals(Environment.MEDIA_UNMOUNTED)) {
San Mehatfafb0412010-02-18 19:40:04 -0800436 int rc = doMountVolume(path);
437 if (rc != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700438 Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
San Mehatfafb0412010-02-18 19:40:04 -0800439 }
San Mehat6a254402010-03-22 10:21:00 -0700440 } else if (state.equals(Environment.MEDIA_SHARED)) {
441 /*
442 * Bootstrap UMS enabled state since vold indicates
443 * the volume is shared (runtime restart while ums enabled)
444 */
445 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
San Mehatfafb0412010-02-18 19:40:04 -0800446 }
San Mehat6a254402010-03-22 10:21:00 -0700447
San Mehat6a965af22010-02-24 17:47:30 -0800448 /*
San Mehat6a254402010-03-22 10:21:00 -0700449 * If UMS was connected on boot, send the connected event
San Mehat6a965af22010-02-24 17:47:30 -0800450 * now that we're up.
451 */
452 if (mSendUmsConnectedOnBoot) {
453 sendUmsIntent(true);
454 mSendUmsConnectedOnBoot = false;
455 }
San Mehatfafb0412010-02-18 19:40:04 -0800456 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700457 Slog.e(TAG, "Boot-time mount exception", ex);
San Mehatfafb0412010-02-18 19:40:04 -0800458 }
San Mehat207e5382010-02-04 20:46:54 -0800459 }
San Mehatfafb0412010-02-18 19:40:04 -0800460 }.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 }
462 }
463 };
464
San Mehat4270e1e2010-01-29 05:32:19 -0800465 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
466 final IMountServiceListener mListener;
467
468 MountServiceBinderListener(IMountServiceListener listener) {
469 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700470
San Mehat91c77612010-01-07 10:39:41 -0800471 }
472
San Mehat4270e1e2010-01-29 05:32:19 -0800473 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700474 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700475 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800476 mListeners.remove(this);
477 mListener.asBinder().unlinkToDeath(this, 0);
478 }
479 }
480 }
481
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800482 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800483 // TODO: Add support for multiple share methods
484 if (!method.equals("ums")) {
485 throw new IllegalArgumentException(String.format("Method %s not supported", method));
486 }
487
San Mehat4270e1e2010-01-29 05:32:19 -0800488 try {
489 mConnector.doCommand(String.format(
490 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
491 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700492 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800493 }
San Mehat4270e1e2010-01-29 05:32:19 -0800494 }
495
San Mehat207e5382010-02-04 20:46:54 -0800496 private void updatePublicVolumeState(String path, String state) {
San Mehat4270e1e2010-01-29 05:32:19 -0800497 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -0700498 Slog.w(TAG, "Multiple volumes not currently supported");
San Mehat4270e1e2010-01-29 05:32:19 -0800499 return;
500 }
San Mehatb1043402010-02-05 08:26:50 -0800501
502 if (mLegacyState.equals(state)) {
San Mehata5078592010-03-25 09:36:54 -0700503 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
San Mehatb1043402010-02-05 08:26:50 -0800504 return;
505 }
Mike Lockwood03559752010-07-19 18:25:03 -0400506 // Update state on PackageManager, but only of real events
507 if (!mEmulateExternalStorage) {
508 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
509 mPms.updateExternalMediaStatus(false, false);
Kenny Rootaf9d6672010-10-08 09:21:39 -0700510
Kenny Root0a9b54e2010-10-13 15:43:35 -0700511 /*
512 * Some OBBs might have been unmounted when this volume was
513 * unmounted, so send a message to the handler to let it know to
514 * remove those from the list of mounted OBBS.
515 */
516 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
517 path));
Mike Lockwood03559752010-07-19 18:25:03 -0400518 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
519 mPms.updateExternalMediaStatus(true, false);
520 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800521 }
Kenny Root38cf8862010-09-26 14:18:51 -0700522
San Mehat4270e1e2010-01-29 05:32:19 -0800523 String oldState = mLegacyState;
524 mLegacyState = state;
525
526 synchronized (mListeners) {
527 for (int i = mListeners.size() -1; i >= 0; i--) {
528 MountServiceBinderListener bl = mListeners.get(i);
529 try {
San Mehatb1043402010-02-05 08:26:50 -0800530 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800531 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700532 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800533 mListeners.remove(i);
534 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700535 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800536 }
537 }
538 }
539 }
540
541 /**
542 *
543 * Callback from NativeDaemonConnector
544 */
545 public void onDaemonConnected() {
546 /*
547 * Since we'll be calling back into the NativeDaemonConnector,
548 * we need to do our work in a new thread.
549 */
550 new Thread() {
551 public void run() {
552 /**
553 * Determine media state and UMS detection status
554 */
555 String path = Environment.getExternalStorageDirectory().getPath();
556 String state = Environment.MEDIA_REMOVED;
557
558 try {
559 String[] vols = mConnector.doListCommand(
560 "volume list", VoldResponseCode.VolumeListResult);
561 for (String volstr : vols) {
562 String[] tok = volstr.split(" ");
563 // FMT: <label> <mountpoint> <state>
564 if (!tok[1].equals(path)) {
San Mehata5078592010-03-25 09:36:54 -0700565 Slog.w(TAG, String.format(
San Mehat4270e1e2010-01-29 05:32:19 -0800566 "Skipping unknown volume '%s'",tok[1]));
567 continue;
568 }
569 int st = Integer.parseInt(tok[2]);
570 if (st == VolumeState.NoMedia) {
571 state = Environment.MEDIA_REMOVED;
572 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800573 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800574 } else if (st == VolumeState.Mounted) {
575 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700576 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800577 } else if (st == VolumeState.Shared) {
578 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700579 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800580 } else {
581 throw new Exception(String.format("Unexpected state %d", st));
582 }
583 }
584 if (state != null) {
San Mehata5078592010-03-25 09:36:54 -0700585 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
San Mehat4270e1e2010-01-29 05:32:19 -0800586 updatePublicVolumeState(path, state);
587 }
588 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700589 Slog.e(TAG, "Error processing initial volume state", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800590 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
591 }
592
593 try {
San Mehat207e5382010-02-04 20:46:54 -0800594 boolean avail = doGetShareMethodAvailable("ums");
San Mehat4270e1e2010-01-29 05:32:19 -0800595 notifyShareAvailabilityChange("ums", avail);
596 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700597 Slog.w(TAG, "Failed to get share availability");
San Mehat4270e1e2010-01-29 05:32:19 -0800598 }
San Mehat207e5382010-02-04 20:46:54 -0800599 /*
600 * Now that we've done our initialization, release
601 * the hounds!
602 */
603 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800604 }
605 }.start();
606 }
607
608 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800609 * Callback from NativeDaemonConnector
610 */
611 public boolean onEvent(int code, String raw, String[] cooked) {
612 Intent in = null;
613
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800614 if (DEBUG_EVENTS) {
615 StringBuilder builder = new StringBuilder();
616 builder.append("onEvent::");
617 builder.append(" raw= " + raw);
618 if (cooked != null) {
619 builder.append(" cooked = " );
620 for (String str : cooked) {
621 builder.append(" " + str);
622 }
623 }
San Mehata5078592010-03-25 09:36:54 -0700624 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800625 }
San Mehat4270e1e2010-01-29 05:32:19 -0800626 if (code == VoldResponseCode.VolumeStateChange) {
627 /*
628 * One of the volumes we're managing has changed state.
629 * Format: "NNN Volume <label> <path> state changed
630 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
631 */
632 notifyVolumeStateChange(
633 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
634 Integer.parseInt(cooked[10]));
635 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
636 // FMT: NNN Share method <method> now <available|unavailable>
637 boolean avail = false;
638 if (cooked[5].equals("available")) {
639 avail = true;
640 }
641 notifyShareAvailabilityChange(cooked[3], avail);
642 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
643 (code == VoldResponseCode.VolumeDiskRemoved) ||
644 (code == VoldResponseCode.VolumeBadRemoval)) {
645 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
646 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
647 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
648 final String label = cooked[2];
649 final String path = cooked[3];
650 int major = -1;
651 int minor = -1;
652
653 try {
654 String devComp = cooked[6].substring(1, cooked[6].length() -1);
655 String[] devTok = devComp.split(":");
656 major = Integer.parseInt(devTok[0]);
657 minor = Integer.parseInt(devTok[1]);
658 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700659 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800660 }
661
San Mehat4270e1e2010-01-29 05:32:19 -0800662 if (code == VoldResponseCode.VolumeDiskInserted) {
663 new Thread() {
664 public void run() {
665 try {
666 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800667 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700668 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800669 }
670 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700671 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800672 }
673 }
674 }.start();
675 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
676 /*
677 * This event gets trumped if we're already in BAD_REMOVAL state
678 */
679 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
680 return true;
681 }
682 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700683 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800684 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
685 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
686 mContext.sendBroadcast(in);
687
San Mehata5078592010-03-25 09:36:54 -0700688 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
San Mehat4270e1e2010-01-29 05:32:19 -0800689 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
690 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
691 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700692 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800693 /* Send the media unmounted event first */
694 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
695 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
696 mContext.sendBroadcast(in);
697
San Mehata5078592010-03-25 09:36:54 -0700698 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
San Mehat4270e1e2010-01-29 05:32:19 -0800699 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
700 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
701 } else {
San Mehata5078592010-03-25 09:36:54 -0700702 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800703 }
704 } else {
705 return false;
706 }
707
708 if (in != null) {
709 mContext.sendBroadcast(in);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400710 }
711 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800712 }
713
San Mehat207e5382010-02-04 20:46:54 -0800714 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800715 String vs = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -0700716 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
San Mehat4270e1e2010-01-29 05:32:19 -0800717
718 Intent in = null;
719
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500720 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700721 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500722 mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
723 Uri.parse("file://" + path)));
724 }
725
San Mehat4270e1e2010-01-29 05:32:19 -0800726 if (newState == VolumeState.Init) {
727 } else if (newState == VolumeState.NoMedia) {
728 // NoMedia is handled via Disk Remove events
729 } else if (newState == VolumeState.Idle) {
730 /*
731 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
732 * if we're in the process of enabling UMS
733 */
734 if (!vs.equals(
735 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
736 Environment.MEDIA_NOFS) && !vs.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800737 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700738 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
San Mehat4270e1e2010-01-29 05:32:19 -0800739 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
740 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
741 }
742 } else if (newState == VolumeState.Pending) {
743 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -0700744 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
San Mehat4270e1e2010-01-29 05:32:19 -0800745 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
746 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
747 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -0700748 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800749 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
San Mehat4270e1e2010-01-29 05:32:19 -0800750 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
751 in.putExtra("read-only", false);
752 } else if (newState == VolumeState.Unmounting) {
San Mehat4270e1e2010-01-29 05:32:19 -0800753 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
754 } else if (newState == VolumeState.Formatting) {
755 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -0700756 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800757 /* Send the media unmounted event first */
758 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
759 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
760 mContext.sendBroadcast(in);
761
San Mehata5078592010-03-25 09:36:54 -0700762 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
San Mehat4270e1e2010-01-29 05:32:19 -0800763 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
764 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
San Mehata5078592010-03-25 09:36:54 -0700765 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -0800766 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -0700767 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -0800768 return;
769 } else {
San Mehata5078592010-03-25 09:36:54 -0700770 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800771 }
772
773 if (in != null) {
774 mContext.sendBroadcast(in);
775 }
776 }
777
San Mehat207e5382010-02-04 20:46:54 -0800778 private boolean doGetShareMethodAvailable(String method) {
Kenny Root85fb2062010-06-01 20:50:21 -0700779 ArrayList<String> rsp;
Kenny Roota80ce062010-06-01 13:23:53 -0700780 try {
Kenny Root85fb2062010-06-01 20:50:21 -0700781 rsp = mConnector.doCommand("share status " + method);
Kenny Roota80ce062010-06-01 13:23:53 -0700782 } catch (NativeDaemonConnectorException ex) {
783 Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
784 return false;
785 }
San Mehat207e5382010-02-04 20:46:54 -0800786
787 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700788 String[] tok = line.split(" ");
789 if (tok.length < 3) {
790 Slog.e(TAG, "Malformed response to share status " + method);
791 return false;
792 }
793
San Mehat207e5382010-02-04 20:46:54 -0800794 int code;
795 try {
796 code = Integer.parseInt(tok[0]);
797 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700798 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehat207e5382010-02-04 20:46:54 -0800799 return false;
800 }
801 if (code == VoldResponseCode.ShareStatusResult) {
802 if (tok[2].equals("available"))
803 return true;
804 return false;
805 } else {
San Mehata5078592010-03-25 09:36:54 -0700806 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehat207e5382010-02-04 20:46:54 -0800807 return false;
808 }
809 }
San Mehata5078592010-03-25 09:36:54 -0700810 Slog.e(TAG, "Got an empty response");
San Mehat207e5382010-02-04 20:46:54 -0800811 return false;
812 }
813
814 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800815 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800816
San Mehata5078592010-03-25 09:36:54 -0700817 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -0800818 try {
819 mConnector.doCommand(String.format("volume mount %s", path));
820 } catch (NativeDaemonConnectorException e) {
821 /*
822 * Mount failed for some reason
823 */
824 Intent in = null;
825 int code = e.getCode();
826 if (code == VoldResponseCode.OpFailedNoMedia) {
827 /*
828 * Attempt to mount but no media inserted
829 */
San Mehatb1043402010-02-05 08:26:50 -0800830 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800831 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -0700832 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -0800833 /*
834 * Media is blank or does not contain a supported filesystem
835 */
836 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
837 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800838 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800839 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -0700840 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -0800841 /*
842 * Volume consistency check failed
843 */
844 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
845 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800846 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800847 } else {
San Mehatb1043402010-02-05 08:26:50 -0800848 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800849 }
850
851 /*
852 * Send broadcast intent (if required for the failure)
853 */
854 if (in != null) {
855 mContext.sendBroadcast(in);
856 }
857 }
858
859 return rc;
860 }
861
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800862 /*
863 * If force is not set, we do not unmount if there are
864 * processes holding references to the volume about to be unmounted.
865 * If force is set, all the processes holding references need to be
866 * killed via the ActivityManager before actually unmounting the volume.
867 * This might even take a while and might be retried after timed delays
868 * to make sure we dont end up in an instable state and kill some core
869 * processes.
870 */
San Mehatd9709982010-02-18 11:43:03 -0800871 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800872 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800873 return VoldResponseCode.OpFailedVolNotMounted;
874 }
Kenny Rootaa485402010-09-14 14:49:41 -0700875
876 /*
877 * Force a GC to make sure AssetManagers in other threads of the
878 * system_server are cleaned up. We have to do this since AssetManager
879 * instances are kept as a WeakReference and it's possible we have files
880 * open on the external storage.
881 */
882 Runtime.getRuntime().gc();
883
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800884 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700885 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -0800886 try {
San Mehatd9709982010-02-18 11:43:03 -0800887 mConnector.doCommand(String.format(
888 "volume unmount %s%s", path, (force ? " force" : "")));
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700889 // We unmounted the volume. None of the asec containers are available now.
890 synchronized (mAsecMountSet) {
891 mAsecMountSet.clear();
892 }
San Mehatb1043402010-02-05 08:26:50 -0800893 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800894 } catch (NativeDaemonConnectorException e) {
895 // Don't worry about mismatch in PackageManager since the
896 // call back will handle the status changes any way.
897 int code = e.getCode();
898 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800899 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800900 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
901 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -0800902 } else {
San Mehatb1043402010-02-05 08:26:50 -0800903 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800904 }
905 }
906 }
907
908 private int doFormatVolume(String path) {
909 try {
910 String cmd = String.format("volume format %s", path);
911 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800912 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800913 } catch (NativeDaemonConnectorException e) {
914 int code = e.getCode();
915 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800916 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800917 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800918 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800919 } else {
San Mehatb1043402010-02-05 08:26:50 -0800920 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800921 }
922 }
923 }
924
San Mehatb1043402010-02-05 08:26:50 -0800925 private boolean doGetVolumeShared(String path, String method) {
926 String cmd = String.format("volume shared %s %s", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -0700927 ArrayList<String> rsp;
928
929 try {
930 rsp = mConnector.doCommand(cmd);
931 } catch (NativeDaemonConnectorException ex) {
932 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
933 return false;
934 }
San Mehatb1043402010-02-05 08:26:50 -0800935
936 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700937 String[] tok = line.split(" ");
938 if (tok.length < 3) {
939 Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
940 return false;
941 }
942
San Mehatb1043402010-02-05 08:26:50 -0800943 int code;
944 try {
945 code = Integer.parseInt(tok[0]);
946 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700947 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehatb1043402010-02-05 08:26:50 -0800948 return false;
949 }
950 if (code == VoldResponseCode.ShareEnabledResult) {
Kenny Roota80ce062010-06-01 13:23:53 -0700951 return "enabled".equals(tok[2]);
San Mehatb1043402010-02-05 08:26:50 -0800952 } else {
San Mehata5078592010-03-25 09:36:54 -0700953 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehatb1043402010-02-05 08:26:50 -0800954 return false;
955 }
956 }
San Mehata5078592010-03-25 09:36:54 -0700957 Slog.e(TAG, "Got an empty response");
San Mehatb1043402010-02-05 08:26:50 -0800958 return false;
959 }
960
San Mehat207e5382010-02-04 20:46:54 -0800961 private void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800962 if (!method.equals("ums")) {
San Mehata5078592010-03-25 09:36:54 -0700963 Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800964 return;
965 }
966
967 synchronized (mListeners) {
968 for (int i = mListeners.size() -1; i >= 0; i--) {
969 MountServiceBinderListener bl = mListeners.get(i);
970 try {
San Mehatb1043402010-02-05 08:26:50 -0800971 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800972 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700973 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800974 mListeners.remove(i);
975 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700976 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800977 }
978 }
979 }
980
San Mehat207e5382010-02-04 20:46:54 -0800981 if (mBooted == true) {
San Mehat6a965af22010-02-24 17:47:30 -0800982 sendUmsIntent(avail);
983 } else {
984 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -0800985 }
San Mehat2fe718a2010-03-11 12:01:49 -0800986
987 final String path = Environment.getExternalStorageDirectory().getPath();
988 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
989 /*
990 * USB mass storage disconnected while enabled
991 */
992 new Thread() {
993 public void run() {
994 try {
995 int rc;
San Mehata5078592010-03-25 09:36:54 -0700996 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -0800997 doShareUnshareVolume(path, "ums", false);
998 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700999 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -08001000 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
1001 path, rc));
1002 }
1003 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001004 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -08001005 }
1006 }
1007 }.start();
1008 }
San Mehat4270e1e2010-01-29 05:32:19 -08001009 }
1010
San Mehat6a965af22010-02-24 17:47:30 -08001011 private void sendUmsIntent(boolean c) {
1012 mContext.sendBroadcast(
1013 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
1014 }
1015
San Mehat207e5382010-02-04 20:46:54 -08001016 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -08001017 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
1018 throw new SecurityException(String.format("Requires %s permission", perm));
1019 }
1020 }
1021
1022 /**
San Mehat207e5382010-02-04 20:46:54 -08001023 * Constructs a new MountService instance
1024 *
1025 * @param context Binder context for this service
1026 */
1027 public MountService(Context context) {
1028 mContext = context;
1029
Mike Lockwood03559752010-07-19 18:25:03 -04001030 mEmulateExternalStorage = context.getResources().getBoolean(
1031 com.android.internal.R.bool.config_emulateExternalStorage);
1032 if (mEmulateExternalStorage) {
1033 Slog.d(TAG, "using emulated external storage");
1034 mLegacyState = Environment.MEDIA_MOUNTED;
1035 }
1036
San Mehat207e5382010-02-04 20:46:54 -08001037 // XXX: This will go away soon in favor of IMountServiceObserver
1038 mPms = (PackageManagerService) ServiceManager.getService("package");
1039
1040 mContext.registerReceiver(mBroadcastReceiver,
1041 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
1042
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001043 mHandlerThread = new HandlerThread("MountService");
1044 mHandlerThread.start();
1045 mHandler = new MountServiceHandler(mHandlerThread.getLooper());
1046
Kenny Roota02b8b02010-08-05 16:14:17 -07001047 // Add OBB Action Handler to MountService thread.
1048 mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
1049
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001050 /*
1051 * Vold does not run in the simulator, so pretend the connector thread
1052 * ran and did its thing.
1053 */
1054 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
1055 mReady = true;
1056 mUmsEnabling = true;
1057 return;
1058 }
1059
Kenny Root305bcbf2010-09-03 07:56:38 -07001060 /*
1061 * Create the connection to vold with a maximum queue of twice the
1062 * amount of containers we'd ever expect to have. This keeps an
1063 * "asec list" from blocking a thread repeatedly.
1064 */
1065 mConnector = new NativeDaemonConnector(this, "vold",
1066 PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001067 mReady = false;
Kenny Root305bcbf2010-09-03 07:56:38 -07001068 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001069 thread.start();
1070 }
1071
1072 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001073 * Exposed API calls below here
1074 */
1075
1076 public void registerListener(IMountServiceListener listener) {
1077 synchronized (mListeners) {
1078 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1079 try {
1080 listener.asBinder().linkToDeath(bl, 0);
1081 mListeners.add(bl);
1082 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001083 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001084 }
1085 }
1086 }
1087
1088 public void unregisterListener(IMountServiceListener listener) {
1089 synchronized (mListeners) {
1090 for(MountServiceBinderListener bl : mListeners) {
1091 if (bl.mListener == listener) {
1092 mListeners.remove(mListeners.indexOf(bl));
1093 return;
1094 }
1095 }
1096 }
1097 }
1098
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001099 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001100 validatePermission(android.Manifest.permission.SHUTDOWN);
1101
San Mehata5078592010-03-25 09:36:54 -07001102 Slog.i(TAG, "Shutting down");
San Mehat4270e1e2010-01-29 05:32:19 -08001103
1104 String path = Environment.getExternalStorageDirectory().getPath();
1105 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -08001106
1107 if (state.equals(Environment.MEDIA_SHARED)) {
1108 /*
1109 * If the media is currently shared, unshare it.
1110 * XXX: This is still dangerous!. We should not
1111 * be rebooting at *all* if UMS is enabled, since
1112 * the UMS host could have dirty FAT cache entries
1113 * yet to flush.
1114 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001115 setUsbMassStorageEnabled(false);
San Mehat91c77612010-01-07 10:39:41 -08001116 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1117 /*
1118 * If the media is being checked, then we need to wait for
1119 * it to complete before being able to proceed.
1120 */
1121 // XXX: @hackbod - Should we disable the ANR timer here?
1122 int retries = 30;
1123 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1124 try {
1125 Thread.sleep(1000);
1126 } catch (InterruptedException iex) {
San Mehata5078592010-03-25 09:36:54 -07001127 Slog.e(TAG, "Interrupted while waiting for media", iex);
San Mehat91c77612010-01-07 10:39:41 -08001128 break;
1129 }
1130 state = Environment.getExternalStorageState();
1131 }
1132 if (retries == 0) {
San Mehata5078592010-03-25 09:36:54 -07001133 Slog.e(TAG, "Timed out waiting for media to check");
San Mehat91c77612010-01-07 10:39:41 -08001134 }
1135 }
1136
1137 if (state.equals(Environment.MEDIA_MOUNTED)) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001138 // Post a unmount message.
1139 ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
1140 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
San Mehat4270e1e2010-01-29 05:32:19 -08001141 }
1142 }
1143
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001144 private boolean getUmsEnabling() {
1145 synchronized (mListeners) {
1146 return mUmsEnabling;
1147 }
1148 }
1149
1150 private void setUmsEnabling(boolean enable) {
1151 synchronized (mListeners) {
Tony Wufc711252010-08-09 16:49:19 +08001152 mUmsEnabling = enable;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001153 }
1154 }
1155
San Mehatb1043402010-02-05 08:26:50 -08001156 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001157 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001158
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001159 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001160 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001161 }
San Mehatb1043402010-02-05 08:26:50 -08001162 return doGetShareMethodAvailable("ums");
1163 }
1164
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001165 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001166 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001167 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -08001168
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001169 // TODO: Add support for multiple share methods
1170
1171 /*
1172 * If the volume is mounted and we're enabling then unmount it
1173 */
1174 String path = Environment.getExternalStorageDirectory().getPath();
1175 String vs = getVolumeState(path);
1176 String method = "ums";
1177 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1178 // Override for isUsbMassStorageEnabled()
1179 setUmsEnabling(enable);
1180 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1181 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1182 // Clear override
1183 setUmsEnabling(false);
1184 }
1185 /*
1186 * If we disabled UMS then mount the volume
1187 */
1188 if (!enable) {
1189 doShareUnshareVolume(path, method, enable);
1190 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001191 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001192 " after disabling share method " + method);
1193 /*
1194 * Even though the mount failed, the unshare didn't so don't indicate an error.
1195 * The mountVolume() call will have set the storage state and sent the necessary
1196 * broadcasts.
1197 */
1198 }
1199 }
San Mehatb1043402010-02-05 08:26:50 -08001200 }
1201
1202 public boolean isUsbMassStorageEnabled() {
1203 waitForReady();
1204 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 }
San Mehat4270e1e2010-01-29 05:32:19 -08001206
San Mehat7fd0fee2009-12-17 07:12:23 -08001207 /**
1208 * @return state of the volume at the specified mount point
1209 */
San Mehat4270e1e2010-01-29 05:32:19 -08001210 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -08001211 /*
1212 * XXX: Until we have multiple volume discovery, just hardwire
1213 * this to /sdcard
1214 */
1215 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -07001216 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
San Mehat7fd0fee2009-12-17 07:12:23 -08001217 throw new IllegalArgumentException();
1218 }
1219
1220 return mLegacyState;
1221 }
1222
Kenny Roote1ff2142010-10-12 11:20:01 -07001223 public boolean isExternalStorageEmulated() {
1224 return mEmulateExternalStorage;
1225 }
1226
San Mehat4270e1e2010-01-29 05:32:19 -08001227 public int mountVolume(String path) {
1228 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001229
San Mehat207e5382010-02-04 20:46:54 -08001230 waitForReady();
1231 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 }
1233
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001234 public void unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001235 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001236 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001238 String volState = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -07001239 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001240 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1241 Environment.MEDIA_REMOVED.equals(volState) ||
1242 Environment.MEDIA_SHARED.equals(volState) ||
1243 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1244 // Media already unmounted or cannot be unmounted.
1245 // TODO return valid return code when adding observer call back.
1246 return;
1247 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001248 UnmountCallBack ucb = new UnmountCallBack(path, force);
1249 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 }
1251
San Mehat4270e1e2010-01-29 05:32:19 -08001252 public int formatVolume(String path) {
1253 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001254 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001255
San Mehat207e5382010-02-04 20:46:54 -08001256 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 }
1258
San Mehatc1b4ce92010-02-16 17:13:03 -08001259 public int []getStorageUsers(String path) {
1260 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1261 waitForReady();
1262 try {
1263 String[] r = mConnector.doListCommand(
1264 String.format("storage users %s", path),
1265 VoldResponseCode.StorageUsersListResult);
1266 // FMT: <pid> <process name>
1267 int[] data = new int[r.length];
1268 for (int i = 0; i < r.length; i++) {
1269 String []tok = r[i].split(" ");
1270 try {
1271 data[i] = Integer.parseInt(tok[0]);
1272 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001273 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001274 return new int[0];
1275 }
1276 }
1277 return data;
1278 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001279 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001280 return new int[0];
1281 }
1282 }
1283
San Mehatb1043402010-02-05 08:26:50 -08001284 private void warnOnNotMounted() {
1285 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
San Mehata5078592010-03-25 09:36:54 -07001286 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
San Mehatb1043402010-02-05 08:26:50 -08001287 }
1288 }
1289
San Mehat4270e1e2010-01-29 05:32:19 -08001290 public String[] getSecureContainerList() {
1291 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001292 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001293 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001294
San Mehat4270e1e2010-01-29 05:32:19 -08001295 try {
1296 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
1297 } catch (NativeDaemonConnectorException e) {
1298 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 }
1300 }
San Mehat36972292010-01-06 11:06:32 -08001301
San Mehat4270e1e2010-01-29 05:32:19 -08001302 public int createSecureContainer(String id, int sizeMb, String fstype,
1303 String key, int ownerUid) {
1304 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001305 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001306 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001307
San Mehatb1043402010-02-05 08:26:50 -08001308 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001309 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
1310 try {
1311 mConnector.doCommand(cmd);
1312 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001313 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001314 }
San Mehata181b212010-02-11 06:50:20 -08001315
1316 if (rc == StorageResultCode.OperationSucceeded) {
1317 synchronized (mAsecMountSet) {
1318 mAsecMountSet.add(id);
1319 }
1320 }
San Mehat4270e1e2010-01-29 05:32:19 -08001321 return rc;
San Mehat36972292010-01-06 11:06:32 -08001322 }
1323
San Mehat4270e1e2010-01-29 05:32:19 -08001324 public int finalizeSecureContainer(String id) {
1325 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001326 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001327
San Mehatb1043402010-02-05 08:26:50 -08001328 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001329 try {
1330 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -08001331 /*
1332 * Finalization does a remount, so no need
1333 * to update mAsecMountSet
1334 */
San Mehat4270e1e2010-01-29 05:32:19 -08001335 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001336 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001337 }
San Mehat4270e1e2010-01-29 05:32:19 -08001338 return rc;
San Mehat36972292010-01-06 11:06:32 -08001339 }
1340
San Mehatd9709982010-02-18 11:43:03 -08001341 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001342 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001343 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001344 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001345
Kenny Rootaa485402010-09-14 14:49:41 -07001346 /*
1347 * Force a GC to make sure AssetManagers in other threads of the
1348 * system_server are cleaned up. We have to do this since AssetManager
1349 * instances are kept as a WeakReference and it's possible we have files
1350 * open on the external storage.
1351 */
1352 Runtime.getRuntime().gc();
1353
San Mehatb1043402010-02-05 08:26:50 -08001354 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001355 try {
San Mehatd9709982010-02-18 11:43:03 -08001356 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -08001357 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001358 int code = e.getCode();
1359 if (code == VoldResponseCode.OpFailedStorageBusy) {
1360 rc = StorageResultCode.OperationFailedStorageBusy;
1361 } else {
1362 rc = StorageResultCode.OperationFailedInternalError;
1363 }
San Mehat02735bc2010-01-26 15:18:08 -08001364 }
San Mehata181b212010-02-11 06:50:20 -08001365
1366 if (rc == StorageResultCode.OperationSucceeded) {
1367 synchronized (mAsecMountSet) {
1368 if (mAsecMountSet.contains(id)) {
1369 mAsecMountSet.remove(id);
1370 }
1371 }
1372 }
1373
San Mehat4270e1e2010-01-29 05:32:19 -08001374 return rc;
San Mehat36972292010-01-06 11:06:32 -08001375 }
1376
San Mehat4270e1e2010-01-29 05:32:19 -08001377 public int mountSecureContainer(String id, String key, int ownerUid) {
1378 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001379 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001380 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001381
San Mehata181b212010-02-11 06:50:20 -08001382 synchronized (mAsecMountSet) {
1383 if (mAsecMountSet.contains(id)) {
1384 return StorageResultCode.OperationFailedStorageMounted;
1385 }
1386 }
1387
San Mehatb1043402010-02-05 08:26:50 -08001388 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001389 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
1390 try {
1391 mConnector.doCommand(cmd);
1392 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001393 int code = e.getCode();
1394 if (code != VoldResponseCode.OpFailedStorageBusy) {
1395 rc = StorageResultCode.OperationFailedInternalError;
1396 }
San Mehat02735bc2010-01-26 15:18:08 -08001397 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001398
1399 if (rc == StorageResultCode.OperationSucceeded) {
1400 synchronized (mAsecMountSet) {
1401 mAsecMountSet.add(id);
1402 }
1403 }
San Mehat4270e1e2010-01-29 05:32:19 -08001404 return rc;
San Mehat36972292010-01-06 11:06:32 -08001405 }
1406
San Mehatd9709982010-02-18 11:43:03 -08001407 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001408 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001409 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001410 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001411
San Mehat6cdd9c02010-02-09 14:45:20 -08001412 synchronized (mAsecMountSet) {
1413 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001414 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001415 }
1416 }
1417
Kenny Rootaa485402010-09-14 14:49:41 -07001418 /*
1419 * Force a GC to make sure AssetManagers in other threads of the
1420 * system_server are cleaned up. We have to do this since AssetManager
1421 * instances are kept as a WeakReference and it's possible we have files
1422 * open on the external storage.
1423 */
1424 Runtime.getRuntime().gc();
1425
San Mehatb1043402010-02-05 08:26:50 -08001426 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -08001427 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -08001428 try {
1429 mConnector.doCommand(cmd);
1430 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001431 int code = e.getCode();
1432 if (code == VoldResponseCode.OpFailedStorageBusy) {
1433 rc = StorageResultCode.OperationFailedStorageBusy;
1434 } else {
1435 rc = StorageResultCode.OperationFailedInternalError;
1436 }
San Mehat02735bc2010-01-26 15:18:08 -08001437 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001438
1439 if (rc == StorageResultCode.OperationSucceeded) {
1440 synchronized (mAsecMountSet) {
1441 mAsecMountSet.remove(id);
1442 }
1443 }
San Mehat4270e1e2010-01-29 05:32:19 -08001444 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001445 }
1446
San Mehat6cdd9c02010-02-09 14:45:20 -08001447 public boolean isSecureContainerMounted(String id) {
1448 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1449 waitForReady();
1450 warnOnNotMounted();
1451
1452 synchronized (mAsecMountSet) {
1453 return mAsecMountSet.contains(id);
1454 }
1455 }
1456
San Mehat4270e1e2010-01-29 05:32:19 -08001457 public int renameSecureContainer(String oldId, String newId) {
1458 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001459 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001460 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001461
San Mehata181b212010-02-11 06:50:20 -08001462 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001463 /*
1464 * Because a mounted container has active internal state which cannot be
1465 * changed while active, we must ensure both ids are not currently mounted.
1466 */
1467 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001468 return StorageResultCode.OperationFailedStorageMounted;
1469 }
1470 }
1471
San Mehatb1043402010-02-05 08:26:50 -08001472 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001473 String cmd = String.format("asec rename %s %s", oldId, newId);
1474 try {
1475 mConnector.doCommand(cmd);
1476 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001477 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001478 }
San Mehata181b212010-02-11 06:50:20 -08001479
San Mehat4270e1e2010-01-29 05:32:19 -08001480 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001481 }
1482
San Mehat4270e1e2010-01-29 05:32:19 -08001483 public String getSecureContainerPath(String id) {
1484 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001485 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001486 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001487
San Mehat2d66cef2010-03-23 11:12:52 -07001488 try {
1489 ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
1490 String []tok = rsp.get(0).split(" ");
San Mehat22dd86e2010-01-12 12:21:18 -08001491 int code = Integer.parseInt(tok[0]);
San Mehat2d66cef2010-03-23 11:12:52 -07001492 if (code != VoldResponseCode.AsecPathResult) {
1493 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1494 }
1495 return tok[1];
1496 } catch (NativeDaemonConnectorException e) {
1497 int code = e.getCode();
1498 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1499 throw new IllegalArgumentException(String.format("Container '%s' not found", id));
San Mehat22dd86e2010-01-12 12:21:18 -08001500 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001501 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001502 }
1503 }
San Mehat22dd86e2010-01-12 12:21:18 -08001504 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001505
1506 public void finishMediaUpdate() {
1507 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
1508 }
Kenny Root02c87302010-07-01 08:10:18 -07001509
Kenny Roota02b8b02010-08-05 16:14:17 -07001510 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1511 if (callerUid == android.os.Process.SYSTEM_UID) {
1512 return true;
1513 }
1514
Kenny Root02c87302010-07-01 08:10:18 -07001515 if (packageName == null) {
1516 return false;
1517 }
1518
1519 final int packageUid = mPms.getPackageUid(packageName);
1520
1521 if (DEBUG_OBB) {
1522 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1523 packageUid + ", callerUid = " + callerUid);
1524 }
1525
1526 return callerUid == packageUid;
1527 }
1528
1529 public String getMountedObbPath(String filename) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001530 if (filename == null) {
1531 throw new IllegalArgumentException("filename cannot be null");
1532 }
1533
Kenny Root02c87302010-07-01 08:10:18 -07001534 waitForReady();
1535 warnOnNotMounted();
1536
Kenny Root02c87302010-07-01 08:10:18 -07001537 try {
1538 ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
1539 String []tok = rsp.get(0).split(" ");
1540 int code = Integer.parseInt(tok[0]);
1541 if (code != VoldResponseCode.AsecPathResult) {
1542 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1543 }
1544 return tok[1];
1545 } catch (NativeDaemonConnectorException e) {
1546 int code = e.getCode();
1547 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001548 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001549 } else {
1550 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1551 }
1552 }
1553 }
1554
1555 public boolean isObbMounted(String filename) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001556 if (filename == null) {
1557 throw new IllegalArgumentException("filename cannot be null");
Kenny Root02c87302010-07-01 08:10:18 -07001558 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001559
1560 synchronized (mObbMounts) {
1561 return mObbPathToStateMap.containsKey(filename);
1562 }
Kenny Root02c87302010-07-01 08:10:18 -07001563 }
1564
Kenny Rootaf9d6672010-10-08 09:21:39 -07001565 public void mountObb(String filename, String key, IObbActionListener token, int nonce)
Kenny Root735de3b2010-09-30 14:11:39 -07001566 throws RemoteException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001567 if (filename == null) {
1568 throw new IllegalArgumentException("filename cannot be null");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001569 }
1570
1571 if (token == null) {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001572 throw new IllegalArgumentException("token cannot be null");
1573 }
1574
Kenny Rootaf9d6672010-10-08 09:21:39 -07001575 final int callerUid = Binder.getCallingUid();
1576 final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
1577 final ObbAction action = new MountObbAction(obbState, key);
Kenny Roota02b8b02010-08-05 16:14:17 -07001578 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1579
1580 if (DEBUG_OBB)
1581 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001582 }
1583
Kenny Rootaf9d6672010-10-08 09:21:39 -07001584 public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
1585 throws RemoteException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001586 if (filename == null) {
1587 throw new IllegalArgumentException("filename cannot be null");
Kenny Rootf1121dc2010-09-29 07:30:53 -07001588 }
1589
Kenny Rootaf9d6672010-10-08 09:21:39 -07001590 final int callerUid = Binder.getCallingUid();
1591 final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
1592 final ObbAction action = new UnmountObbAction(obbState, force);
Kenny Roota02b8b02010-08-05 16:14:17 -07001593 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001594
Kenny Roota02b8b02010-08-05 16:14:17 -07001595 if (DEBUG_OBB)
1596 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1597 }
1598
Kenny Rootaf9d6672010-10-08 09:21:39 -07001599 private void addObbStateLocked(ObbState obbState) throws RemoteException {
1600 final IBinder binder = obbState.getBinder();
1601 List<ObbState> obbStates = mObbMounts.get(binder);
Kenny Root5919ac62010-10-05 09:49:40 -07001602
Kenny Rootaf9d6672010-10-08 09:21:39 -07001603 if (obbStates == null) {
1604 obbStates = new ArrayList<ObbState>();
1605 mObbMounts.put(binder, obbStates);
1606 } else {
1607 for (final ObbState o : obbStates) {
1608 if (o.filename.equals(obbState.filename)) {
1609 throw new IllegalStateException("Attempt to add ObbState twice. "
1610 + "This indicates an error in the MountService logic.");
Kenny Root5919ac62010-10-05 09:49:40 -07001611 }
1612 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001613 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001614
1615 obbStates.add(obbState);
1616 try {
1617 obbState.link();
1618 } catch (RemoteException e) {
1619 /*
1620 * The binder died before we could link it, so clean up our state
1621 * and return failure.
1622 */
1623 obbStates.remove(obbState);
1624 if (obbStates.isEmpty()) {
1625 mObbMounts.remove(binder);
1626 }
1627
1628 // Rethrow the error so mountObb can get it
1629 throw e;
1630 }
1631
1632 mObbPathToStateMap.put(obbState.filename, obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07001633 }
1634
Kenny Rootaf9d6672010-10-08 09:21:39 -07001635 private void removeObbStateLocked(ObbState obbState) {
1636 final IBinder binder = obbState.getBinder();
1637 final List<ObbState> obbStates = mObbMounts.get(binder);
1638 if (obbStates != null) {
1639 if (obbStates.remove(obbState)) {
1640 obbState.unlink();
Kenny Root05105f72010-09-22 17:29:43 -07001641 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001642 if (obbStates.isEmpty()) {
1643 mObbMounts.remove(binder);
1644 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001645 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001646
Kenny Rootaf9d6672010-10-08 09:21:39 -07001647 mObbPathToStateMap.remove(obbState.filename);
Kenny Root38cf8862010-09-26 14:18:51 -07001648 }
1649
Kenny Roota02b8b02010-08-05 16:14:17 -07001650 private class ObbActionHandler extends Handler {
1651 private boolean mBound = false;
Kenny Root480afe72010-10-07 10:17:50 -07001652 private final List<ObbAction> mActions = new LinkedList<ObbAction>();
Kenny Roota02b8b02010-08-05 16:14:17 -07001653
1654 ObbActionHandler(Looper l) {
1655 super(l);
1656 }
1657
1658 @Override
1659 public void handleMessage(Message msg) {
1660 switch (msg.what) {
1661 case OBB_RUN_ACTION: {
Kenny Root480afe72010-10-07 10:17:50 -07001662 final ObbAction action = (ObbAction) msg.obj;
Kenny Roota02b8b02010-08-05 16:14:17 -07001663
1664 if (DEBUG_OBB)
1665 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
1666
1667 // If a bind was already initiated we don't really
1668 // need to do anything. The pending install
1669 // will be processed later on.
1670 if (!mBound) {
1671 // If this is the only one pending we might
1672 // have to bind to the service again.
1673 if (!connectToService()) {
1674 Slog.e(TAG, "Failed to bind to media container service");
1675 action.handleError();
1676 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07001677 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001678 }
Kenny Root735de3b2010-09-30 14:11:39 -07001679
Kenny Root735de3b2010-09-30 14:11:39 -07001680 mActions.add(action);
Kenny Roota02b8b02010-08-05 16:14:17 -07001681 break;
1682 }
1683 case OBB_MCS_BOUND: {
1684 if (DEBUG_OBB)
1685 Slog.i(TAG, "OBB_MCS_BOUND");
1686 if (msg.obj != null) {
1687 mContainerService = (IMediaContainerService) msg.obj;
1688 }
1689 if (mContainerService == null) {
1690 // Something seriously wrong. Bail out
1691 Slog.e(TAG, "Cannot bind to media container service");
1692 for (ObbAction action : mActions) {
1693 // Indicate service bind error
1694 action.handleError();
1695 }
1696 mActions.clear();
1697 } else if (mActions.size() > 0) {
Kenny Root480afe72010-10-07 10:17:50 -07001698 final ObbAction action = mActions.get(0);
Kenny Roota02b8b02010-08-05 16:14:17 -07001699 if (action != null) {
1700 action.execute(this);
1701 }
1702 } else {
1703 // Should never happen ideally.
1704 Slog.w(TAG, "Empty queue");
1705 }
1706 break;
1707 }
1708 case OBB_MCS_RECONNECT: {
1709 if (DEBUG_OBB)
1710 Slog.i(TAG, "OBB_MCS_RECONNECT");
1711 if (mActions.size() > 0) {
1712 if (mBound) {
1713 disconnectService();
1714 }
1715 if (!connectToService()) {
1716 Slog.e(TAG, "Failed to bind to media container service");
1717 for (ObbAction action : mActions) {
1718 // Indicate service bind error
1719 action.handleError();
1720 }
1721 mActions.clear();
1722 }
1723 }
1724 break;
1725 }
1726 case OBB_MCS_UNBIND: {
1727 if (DEBUG_OBB)
1728 Slog.i(TAG, "OBB_MCS_UNBIND");
1729
1730 // Delete pending install
1731 if (mActions.size() > 0) {
1732 mActions.remove(0);
1733 }
1734 if (mActions.size() == 0) {
1735 if (mBound) {
1736 disconnectService();
1737 }
1738 } else {
1739 // There are more pending requests in queue.
1740 // Just post MCS_BOUND message to trigger processing
1741 // of next pending install.
1742 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
1743 }
1744 break;
1745 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07001746 case OBB_FLUSH_MOUNT_STATE: {
1747 final String path = (String) msg.obj;
1748
1749 if (DEBUG_OBB)
1750 Slog.i(TAG, "Flushing all OBB state for path " + path);
1751
1752 synchronized (mObbMounts) {
1753 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
1754
1755 final Iterator<Entry<String, ObbState>> i =
1756 mObbPathToStateMap.entrySet().iterator();
1757 while (i.hasNext()) {
1758 final Entry<String, ObbState> obbEntry = i.next();
1759
1760 /*
1761 * If this entry's source file is in the volume path
1762 * that got unmounted, remove it because it's no
1763 * longer valid.
1764 */
1765 if (obbEntry.getKey().startsWith(path)) {
1766 obbStatesToRemove.add(obbEntry.getValue());
1767 }
1768 }
1769
1770 for (final ObbState obbState : obbStatesToRemove) {
1771 if (DEBUG_OBB)
1772 Slog.i(TAG, "Removing state for " + obbState.filename);
1773
1774 removeObbStateLocked(obbState);
1775
1776 try {
1777 obbState.token.onObbResult(obbState.filename, obbState.nonce,
1778 OnObbStateChangeListener.UNMOUNTED);
1779 } catch (RemoteException e) {
1780 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
1781 + obbState.filename);
1782 }
1783 }
1784 }
1785 break;
1786 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001787 }
1788 }
1789
1790 private boolean connectToService() {
1791 if (DEBUG_OBB)
1792 Slog.i(TAG, "Trying to bind to DefaultContainerService");
1793
1794 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
1795 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
1796 mBound = true;
1797 return true;
1798 }
1799 return false;
1800 }
1801
1802 private void disconnectService() {
1803 mContainerService = null;
1804 mBound = false;
1805 mContext.unbindService(mDefContainerConn);
1806 }
1807 }
1808
1809 abstract class ObbAction {
1810 private static final int MAX_RETRIES = 3;
1811 private int mRetries;
1812
1813 ObbState mObbState;
1814
1815 ObbAction(ObbState obbState) {
1816 mObbState = obbState;
1817 }
1818
1819 public void execute(ObbActionHandler handler) {
1820 try {
1821 if (DEBUG_OBB)
1822 Slog.i(TAG, "Starting to execute action: " + this.toString());
1823 mRetries++;
1824 if (mRetries > MAX_RETRIES) {
1825 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
Kenny Root480afe72010-10-07 10:17:50 -07001826 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07001827 handleError();
1828 return;
1829 } else {
1830 handleExecute();
1831 if (DEBUG_OBB)
1832 Slog.i(TAG, "Posting install MCS_UNBIND");
1833 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
1834 }
1835 } catch (RemoteException e) {
1836 if (DEBUG_OBB)
1837 Slog.i(TAG, "Posting install MCS_RECONNECT");
1838 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
1839 } catch (Exception e) {
1840 if (DEBUG_OBB)
1841 Slog.d(TAG, "Error handling OBB action", e);
1842 handleError();
Kenny Root17eb6fb2010-10-06 15:02:52 -07001843 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
Kenny Roota02b8b02010-08-05 16:14:17 -07001844 }
1845 }
1846
Kenny Root05105f72010-09-22 17:29:43 -07001847 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07001848 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07001849
1850 protected ObbInfo getObbInfo() throws IOException {
1851 ObbInfo obbInfo;
1852 try {
1853 obbInfo = mContainerService.getObbInfo(mObbState.filename);
1854 } catch (RemoteException e) {
1855 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
1856 + mObbState.filename);
1857 obbInfo = null;
1858 }
1859 if (obbInfo == null) {
1860 throw new IOException("Couldn't read OBB file: " + mObbState.filename);
1861 }
1862 return obbInfo;
1863 }
1864
Kenny Rootaf9d6672010-10-08 09:21:39 -07001865 protected void sendNewStatusOrIgnore(int status) {
1866 if (mObbState == null || mObbState.token == null) {
1867 return;
1868 }
1869
Kenny Root38cf8862010-09-26 14:18:51 -07001870 try {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001871 mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
Kenny Root38cf8862010-09-26 14:18:51 -07001872 } catch (RemoteException e) {
1873 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
1874 }
1875 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001876 }
1877
1878 class MountObbAction extends ObbAction {
1879 private String mKey;
1880
1881 MountObbAction(ObbState obbState, String key) {
1882 super(obbState);
1883 mKey = key;
1884 }
1885
Kenny Root735de3b2010-09-30 14:11:39 -07001886 public void handleExecute() throws IOException, RemoteException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001887 waitForReady();
1888 warnOnNotMounted();
1889
Kenny Root38cf8862010-09-26 14:18:51 -07001890 final ObbInfo obbInfo = getObbInfo();
1891
Kenny Roota02b8b02010-08-05 16:14:17 -07001892 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001893 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
1894 + " which is owned by " + obbInfo.packageName);
1895 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
1896 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07001897 }
1898
Kenny Rootaf9d6672010-10-08 09:21:39 -07001899 final boolean isMounted;
1900 synchronized (mObbMounts) {
1901 isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
1902 }
1903 if (isMounted) {
1904 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
1905 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
1906 return;
1907 }
1908
1909 /*
1910 * The filename passed in might not be the canonical name, so just
1911 * set the filename to the canonicalized version.
1912 */
1913 mObbState.filename = obbInfo.filename;
1914
1915 final String hashedKey;
1916 if (mKey == null) {
1917 hashedKey = "none";
1918 } else {
1919 final MessageDigest md;
1920 try {
1921 md = MessageDigest.getInstance("MD5");
1922 } catch (NoSuchAlgorithmException e) {
1923 Slog.e(TAG, "Could not load MD5 algorithm", e);
1924 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
Kenny Root38cf8862010-09-26 14:18:51 -07001925 return;
1926 }
1927
Kenny Rootaf9d6672010-10-08 09:21:39 -07001928 hashedKey = HexDump.toHexString(md.digest(mKey.getBytes()));
1929 }
Kenny Root38cf8862010-09-26 14:18:51 -07001930
Kenny Rootaf9d6672010-10-08 09:21:39 -07001931 int rc = StorageResultCode.OperationSucceeded;
1932 String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
1933 mObbState.callerUid);
1934 try {
1935 mConnector.doCommand(cmd);
1936 } catch (NativeDaemonConnectorException e) {
1937 int code = e.getCode();
1938 if (code != VoldResponseCode.OpFailedStorageBusy) {
1939 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07001940 }
1941 }
1942
Kenny Rootaf9d6672010-10-08 09:21:39 -07001943 if (rc == StorageResultCode.OperationSucceeded) {
1944 if (DEBUG_OBB)
1945 Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
1946
1947 synchronized (mObbMounts) {
1948 addObbStateLocked(mObbState);
1949 }
1950
1951 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07001952 } else {
Kenny Root05105f72010-09-22 17:29:43 -07001953 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07001954
Kenny Rootaf9d6672010-10-08 09:21:39 -07001955 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
Kenny Root02c87302010-07-01 08:10:18 -07001956 }
1957 }
1958
Kenny Roota02b8b02010-08-05 16:14:17 -07001959 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001960 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Root02c87302010-07-01 08:10:18 -07001961 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001962
1963 @Override
1964 public String toString() {
1965 StringBuilder sb = new StringBuilder();
1966 sb.append("MountObbAction{");
1967 sb.append("filename=");
1968 sb.append(mObbState.filename);
1969 sb.append(",callerUid=");
1970 sb.append(mObbState.callerUid);
1971 sb.append(",token=");
1972 sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
Kenny Rootaf9d6672010-10-08 09:21:39 -07001973 sb.append(",binder=");
1974 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
Kenny Roota02b8b02010-08-05 16:14:17 -07001975 sb.append('}');
1976 return sb.toString();
1977 }
1978 }
1979
1980 class UnmountObbAction extends ObbAction {
1981 private boolean mForceUnmount;
1982
1983 UnmountObbAction(ObbState obbState, boolean force) {
1984 super(obbState);
1985 mForceUnmount = force;
1986 }
1987
Kenny Root38cf8862010-09-26 14:18:51 -07001988 public void handleExecute() throws IOException {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001989 waitForReady();
1990 warnOnNotMounted();
1991
Kenny Root38cf8862010-09-26 14:18:51 -07001992 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07001993
Kenny Rootaf9d6672010-10-08 09:21:39 -07001994 final ObbState obbState;
Kenny Root38cf8862010-09-26 14:18:51 -07001995 synchronized (mObbMounts) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07001996 obbState = mObbPathToStateMap.get(obbInfo.filename);
1997 }
Kenny Root38cf8862010-09-26 14:18:51 -07001998
Kenny Rootaf9d6672010-10-08 09:21:39 -07001999 if (obbState == null) {
2000 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
2001 return;
2002 }
2003
2004 if (obbState.callerUid != mObbState.callerUid) {
2005 Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
2006 + " (owned by " + obbInfo.packageName + ")");
2007 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
2008 return;
2009 }
2010
2011 mObbState.filename = obbInfo.filename;
2012
2013 int rc = StorageResultCode.OperationSucceeded;
2014 String cmd = String.format("obb unmount %s%s", mObbState.filename,
2015 (mForceUnmount ? " force" : ""));
2016 try {
2017 mConnector.doCommand(cmd);
2018 } catch (NativeDaemonConnectorException e) {
2019 int code = e.getCode();
2020 if (code == VoldResponseCode.OpFailedStorageBusy) {
2021 rc = StorageResultCode.OperationFailedStorageBusy;
2022 } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
2023 // If it's not mounted then we've already won.
2024 rc = StorageResultCode.OperationSucceeded;
2025 } else {
2026 rc = StorageResultCode.OperationFailedInternalError;
Kenny Roota02b8b02010-08-05 16:14:17 -07002027 }
2028 }
2029
Kenny Rootaf9d6672010-10-08 09:21:39 -07002030 if (rc == StorageResultCode.OperationSucceeded) {
2031 synchronized (mObbMounts) {
2032 removeObbStateLocked(obbState);
Kenny Root38cf8862010-09-26 14:18:51 -07002033 }
2034
Kenny Rootaf9d6672010-10-08 09:21:39 -07002035 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002036 } else {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002037 Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
2038 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
Kenny Roota02b8b02010-08-05 16:14:17 -07002039 }
2040 }
2041
2042 public void handleError() {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002043 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002044 }
2045
2046 @Override
2047 public String toString() {
2048 StringBuilder sb = new StringBuilder();
2049 sb.append("UnmountObbAction{");
2050 sb.append("filename=");
2051 sb.append(mObbState.filename != null ? mObbState.filename : "null");
2052 sb.append(",force=");
2053 sb.append(mForceUnmount);
2054 sb.append(",callerUid=");
2055 sb.append(mObbState.callerUid);
2056 sb.append(",token=");
2057 sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
Kenny Root735de3b2010-09-30 14:11:39 -07002058 sb.append(",binder=");
Kenny Rootaf9d6672010-10-08 09:21:39 -07002059 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
Kenny Roota02b8b02010-08-05 16:14:17 -07002060 sb.append('}');
2061 return sb.toString();
2062 }
Kenny Root02c87302010-07-01 08:10:18 -07002063 }
Kenny Root38cf8862010-09-26 14:18:51 -07002064
2065 @Override
2066 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2067 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
2068 pw.println("Permission Denial: can't dump ActivityManager from from pid="
2069 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
2070 + " without permission " + android.Manifest.permission.DUMP);
2071 return;
2072 }
2073
Kenny Root38cf8862010-09-26 14:18:51 -07002074 synchronized (mObbMounts) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002075 pw.println(" mObbMounts:");
Kenny Root38cf8862010-09-26 14:18:51 -07002076
Kenny Rootaf9d6672010-10-08 09:21:39 -07002077 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
2078 while (binders.hasNext()) {
2079 Entry<IBinder, List<ObbState>> e = binders.next();
2080 pw.print(" Key="); pw.println(e.getKey().toString());
2081 final List<ObbState> obbStates = e.getValue();
Kenny Root38cf8862010-09-26 14:18:51 -07002082 for (final ObbState obbState : obbStates) {
Kenny Rootaf9d6672010-10-08 09:21:39 -07002083 pw.print(" "); pw.println(obbState.toString());
Kenny Root38cf8862010-09-26 14:18:51 -07002084 }
2085 }
Kenny Rootaf9d6672010-10-08 09:21:39 -07002086
2087 pw.println("");
2088 pw.println(" mObbPathToStateMap:");
2089 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
2090 while (maps.hasNext()) {
2091 final Entry<String, ObbState> e = maps.next();
2092 pw.print(" "); pw.print(e.getKey());
2093 pw.print(" -> "); pw.println(e.getValue().toString());
2094 }
Kenny Root38cf8862010-09-26 14:18:51 -07002095 }
2096 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002097}
2098