blob: ea280a54f23058a7774b61650e5e6fbfab14fe56 [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;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080020import com.android.server.am.ActivityManagerService;
21
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070023import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070027import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.pm.PackageManager;
Kenny Root02c87302010-07-01 08:10:18 -070029import android.content.res.ObbInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070031import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070032import android.os.Environment;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080033import android.os.Handler;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040034import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070035import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040036import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080037import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080038import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080039import android.os.ServiceManager;
San Mehat207e5382010-02-04 20:46:54 -080040import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.SystemProperties;
Kenny Roota02b8b02010-08-05 16:14:17 -070042import android.os.storage.IMountService;
43import android.os.storage.IMountServiceListener;
44import android.os.storage.IMountShutdownObserver;
45import android.os.storage.IObbActionListener;
46import android.os.storage.StorageResultCode;
San Mehata5078592010-03-25 09:36:54 -070047import android.util.Slog;
Kenny Roota02b8b02010-08-05 16:14:17 -070048
Kenny Root38cf8862010-09-26 14:18:51 -070049import java.io.FileDescriptor;
Kenny Root05105f72010-09-22 17:29:43 -070050import java.io.IOException;
Kenny Root38cf8862010-09-26 14:18:51 -070051import java.io.PrintWriter;
San Mehat22dd86e2010-01-12 12:21:18 -080052import java.util.ArrayList;
Kenny Root38cf8862010-09-26 14:18:51 -070053import java.util.Collection;
Kenny Roota02b8b02010-08-05 16:14:17 -070054import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080055import java.util.HashSet;
Kenny Root38cf8862010-09-26 14:18:51 -070056import java.util.Iterator;
Kenny Roota02b8b02010-08-05 16:14:17 -070057import java.util.LinkedList;
58import java.util.List;
59import java.util.Map;
Kenny Root38cf8862010-09-26 14:18:51 -070060import java.util.Map.Entry;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062/**
San Mehatb1043402010-02-05 08:26:50 -080063 * MountService implements back-end services for platform storage
64 * management.
65 * @hide - Applications should use android.os.storage.StorageManager
66 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 */
San Mehat22dd86e2010-01-12 12:21:18 -080068class MountService extends IMountService.Stub
69 implements INativeDaemonConnectorCallbacks {
San Mehatb1043402010-02-05 08:26:50 -080070 private static final boolean LOCAL_LOGD = false;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -080071 private static final boolean DEBUG_UNMOUNT = false;
72 private static final boolean DEBUG_EVENTS = false;
Kenny Root02c87302010-07-01 08:10:18 -070073 private static final boolean DEBUG_OBB = true;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 private static final String TAG = "MountService";
76
Kenny Root305bcbf2010-09-03 07:56:38 -070077 private static final String VOLD_TAG = "VoldConnector";
78
San Mehat4270e1e2010-01-29 05:32:19 -080079 /*
80 * Internal vold volume state constants
81 */
San Mehat7fd0fee2009-12-17 07:12:23 -080082 class VolumeState {
83 public static final int Init = -1;
84 public static final int NoMedia = 0;
85 public static final int Idle = 1;
86 public static final int Pending = 2;
87 public static final int Checking = 3;
88 public static final int Mounted = 4;
89 public static final int Unmounting = 5;
90 public static final int Formatting = 6;
91 public static final int Shared = 7;
92 public static final int SharedMnt = 8;
93 }
94
San Mehat4270e1e2010-01-29 05:32:19 -080095 /*
96 * Internal vold response code constants
97 */
San Mehat22dd86e2010-01-12 12:21:18 -080098 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -080099 /*
100 * 100 series - Requestion action was initiated; expect another reply
101 * before proceeding with a new command.
102 */
San Mehat22dd86e2010-01-12 12:21:18 -0800103 public static final int VolumeListResult = 110;
104 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800105 public static final int StorageUsersListResult = 112;
San Mehat22dd86e2010-01-12 12:21:18 -0800106
San Mehat4270e1e2010-01-29 05:32:19 -0800107 /*
108 * 200 series - Requestion action has been successfully completed.
109 */
110 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800111 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800112 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800113
San Mehat4270e1e2010-01-29 05:32:19 -0800114 /*
115 * 400 series - Command was accepted, but the requested action
116 * did not take place.
117 */
118 public static final int OpFailedNoMedia = 401;
119 public static final int OpFailedMediaBlank = 402;
120 public static final int OpFailedMediaCorrupt = 403;
121 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800122 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700123 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800124
125 /*
126 * 600 series - Unsolicited broadcasts.
127 */
San Mehat22dd86e2010-01-12 12:21:18 -0800128 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800129 public static final int ShareAvailabilityChange = 620;
130 public static final int VolumeDiskInserted = 630;
131 public static final int VolumeDiskRemoved = 631;
132 public static final int VolumeBadRemoval = 632;
133 }
134
San Mehat4270e1e2010-01-29 05:32:19 -0800135 private Context mContext;
136 private NativeDaemonConnector mConnector;
137 private String mLegacyState = Environment.MEDIA_REMOVED;
138 private PackageManagerService mPms;
139 private boolean mUmsEnabling;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800140 // Used as a lock for methods that register/unregister listeners.
141 final private ArrayList<MountServiceBinderListener> mListeners =
142 new ArrayList<MountServiceBinderListener>();
San Mehat6a965af22010-02-24 17:47:30 -0800143 private boolean mBooted = false;
144 private boolean mReady = false;
145 private boolean mSendUmsConnectedOnBoot = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800146
San Mehat6cdd9c02010-02-09 14:45:20 -0800147 /**
148 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800149 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800150 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800151 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800152
Kenny Root02c87302010-07-01 08:10:18 -0700153 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700154 * Mounted OBB tracking information. Used to track the current state of all
155 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700156 */
Kenny Root05105f72010-09-22 17:29:43 -0700157 final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>();
Kenny Roota02b8b02010-08-05 16:14:17 -0700158 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
159
160 class ObbState implements IBinder.DeathRecipient {
161 public ObbState(String filename, IObbActionListener token, int callerUid) {
162 this.filename = filename;
163 this.token = token;
164 this.callerUid = callerUid;
165 mounted = false;
166 }
167
168 // OBB source filename
Kenny Root05105f72010-09-22 17:29:43 -0700169 final String filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700170
171 // Token of remote Binder caller
Kenny Root05105f72010-09-22 17:29:43 -0700172 final IObbActionListener token;
Kenny Roota02b8b02010-08-05 16:14:17 -0700173
174 // Binder.callingUid()
Kenny Root05105f72010-09-22 17:29:43 -0700175 final public int callerUid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700176
177 // Whether this is mounted currently.
178 boolean mounted;
179
180 @Override
181 public void binderDied() {
182 ObbAction action = new UnmountObbAction(this, true);
183 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
184
185 removeObbState(this);
186
187 token.asBinder().unlinkToDeath(this, 0);
188 }
Kenny Root38cf8862010-09-26 14:18:51 -0700189
190 @Override
191 public String toString() {
192 StringBuilder sb = new StringBuilder("ObbState{");
193 sb.append("filename=");
194 sb.append(filename);
195 sb.append(",token=");
196 sb.append(token.toString());
197 sb.append(",callerUid=");
198 sb.append(callerUid);
199 sb.append(",mounted=");
200 sb.append(mounted);
201 sb.append('}');
202 return sb.toString();
203 }
204
Kenny Roota02b8b02010-08-05 16:14:17 -0700205 }
206
207 // OBB Action Handler
208 final private ObbActionHandler mObbActionHandler;
209
210 // OBB action handler messages
211 private static final int OBB_RUN_ACTION = 1;
212 private static final int OBB_MCS_BOUND = 2;
213 private static final int OBB_MCS_UNBIND = 3;
214 private static final int OBB_MCS_RECONNECT = 4;
215 private static final int OBB_MCS_GIVE_UP = 5;
216
217 /*
218 * Default Container Service information
219 */
220 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
221 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
222
223 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
224
225 class DefaultContainerConnection implements ServiceConnection {
226 public void onServiceConnected(ComponentName name, IBinder service) {
227 if (DEBUG_OBB)
228 Slog.i(TAG, "onServiceConnected");
229 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
230 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
231 }
232
233 public void onServiceDisconnected(ComponentName name) {
234 if (DEBUG_OBB)
235 Slog.i(TAG, "onServiceDisconnected");
236 }
237 };
238
239 // Used in the ObbActionHandler
240 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700241
242 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800243 private static final int H_UNMOUNT_PM_UPDATE = 1;
244 private static final int H_UNMOUNT_PM_DONE = 2;
245 private static final int H_UNMOUNT_MS = 3;
246 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
247 private static final int MAX_UNMOUNT_RETRIES = 4;
248
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800249 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700250 final String path;
251 final boolean force;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800252 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800253
254 UnmountCallBack(String path, boolean force) {
255 retries = 0;
256 this.path = path;
257 this.force = force;
258 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800259
260 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700261 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800262 doUnmountVolume(path, true);
263 }
264 }
265
266 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700267 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800268
269 UmsEnableCallBack(String path, String method, boolean force) {
270 super(path, force);
271 this.method = method;
272 }
273
274 @Override
275 void handleFinished() {
276 super.handleFinished();
277 doShareUnshareVolume(path, method, true);
278 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800279 }
280
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800281 class ShutdownCallBack extends UnmountCallBack {
282 IMountShutdownObserver observer;
283 ShutdownCallBack(String path, IMountShutdownObserver observer) {
284 super(path, true);
285 this.observer = observer;
286 }
287
288 @Override
289 void handleFinished() {
290 int ret = doUnmountVolume(path, true);
291 if (observer != null) {
292 try {
293 observer.onShutDownComplete(ret);
294 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700295 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800296 }
297 }
298 }
299 }
300
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400301 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800302 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700303 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800304
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400305 MountServiceHandler(Looper l) {
306 super(l);
307 }
308
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800309 public void handleMessage(Message msg) {
310 switch (msg.what) {
311 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700312 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800313 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
314 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700315 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800316 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700317 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700318 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700319 mUpdatingStatus = true;
320 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800321 }
322 break;
323 }
324 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700325 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700326 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700327 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800328 int size = mForceUnmounts.size();
329 int sizeArr[] = new int[size];
330 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700331 // Kill processes holding references first
332 ActivityManagerService ams = (ActivityManagerService)
333 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800334 for (int i = 0; i < size; i++) {
335 UnmountCallBack ucb = mForceUnmounts.get(i);
336 String path = ucb.path;
337 boolean done = false;
338 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800339 done = true;
340 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800341 int pids[] = getStorageUsers(path);
342 if (pids == null || pids.length == 0) {
343 done = true;
344 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800345 // Eliminate system process here?
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700346 ams.killPids(pids, "unmount media");
347 // Confirm if file references have been freed.
348 pids = getStorageUsers(path);
349 if (pids == null || pids.length == 0) {
350 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800351 }
352 }
353 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700354 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
355 // Retry again
356 Slog.i(TAG, "Retrying to kill storage users again");
357 mHandler.sendMessageDelayed(
358 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
359 ucb.retries++),
360 RETRY_UNMOUNT_DELAY);
361 } else {
362 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
363 Slog.i(TAG, "Failed to unmount media inspite of " +
364 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
365 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800366 sizeArr[sizeArrN++] = i;
367 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
368 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800369 }
370 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800371 // Remove already processed elements from list.
372 for (int i = (sizeArrN-1); i >= 0; i--) {
373 mForceUnmounts.remove(sizeArr[i]);
374 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800375 break;
376 }
377 case H_UNMOUNT_MS : {
San Mehata5078592010-03-25 09:36:54 -0700378 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800379 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800380 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800381 break;
382 }
383 }
384 }
385 };
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400386 final private HandlerThread mHandlerThread;
387 final private Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800388
San Mehat207e5382010-02-04 20:46:54 -0800389 private void waitForReady() {
390 while (mReady == false) {
391 for (int retries = 5; retries > 0; retries--) {
392 if (mReady) {
393 return;
394 }
395 SystemClock.sleep(1000);
396 }
San Mehata5078592010-03-25 09:36:54 -0700397 Slog.w(TAG, "Waiting too long for mReady!");
San Mehat207e5382010-02-04 20:46:54 -0800398 }
San Mehat1f6301e2010-01-07 22:40:27 -0800399 }
Kenny Root02c87302010-07-01 08:10:18 -0700400
San Mehat207e5382010-02-04 20:46:54 -0800401 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800403 String action = intent.getAction();
404
405 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800406 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800407
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800408 /*
409 * In the simulator, we need to broadcast a volume mounted event
410 * to make the media scanner run.
411 */
412 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
413 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
414 return;
415 }
San Mehatfafb0412010-02-18 19:40:04 -0800416 new Thread() {
417 public void run() {
418 try {
419 String path = Environment.getExternalStorageDirectory().getPath();
San Mehat6a254402010-03-22 10:21:00 -0700420 String state = getVolumeState(path);
421
422 if (state.equals(Environment.MEDIA_UNMOUNTED)) {
San Mehatfafb0412010-02-18 19:40:04 -0800423 int rc = doMountVolume(path);
424 if (rc != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700425 Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
San Mehatfafb0412010-02-18 19:40:04 -0800426 }
San Mehat6a254402010-03-22 10:21:00 -0700427 } else if (state.equals(Environment.MEDIA_SHARED)) {
428 /*
429 * Bootstrap UMS enabled state since vold indicates
430 * the volume is shared (runtime restart while ums enabled)
431 */
432 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
San Mehatfafb0412010-02-18 19:40:04 -0800433 }
San Mehat6a254402010-03-22 10:21:00 -0700434
San Mehat6a965af22010-02-24 17:47:30 -0800435 /*
San Mehat6a254402010-03-22 10:21:00 -0700436 * If UMS was connected on boot, send the connected event
San Mehat6a965af22010-02-24 17:47:30 -0800437 * now that we're up.
438 */
439 if (mSendUmsConnectedOnBoot) {
440 sendUmsIntent(true);
441 mSendUmsConnectedOnBoot = false;
442 }
San Mehatfafb0412010-02-18 19:40:04 -0800443 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700444 Slog.e(TAG, "Boot-time mount exception", ex);
San Mehatfafb0412010-02-18 19:40:04 -0800445 }
San Mehat207e5382010-02-04 20:46:54 -0800446 }
San Mehatfafb0412010-02-18 19:40:04 -0800447 }.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 }
449 }
450 };
451
San Mehat4270e1e2010-01-29 05:32:19 -0800452 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
453 final IMountServiceListener mListener;
454
455 MountServiceBinderListener(IMountServiceListener listener) {
456 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700457
San Mehat91c77612010-01-07 10:39:41 -0800458 }
459
San Mehat4270e1e2010-01-29 05:32:19 -0800460 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700461 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700462 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800463 mListeners.remove(this);
464 mListener.asBinder().unlinkToDeath(this, 0);
465 }
466 }
467 }
468
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800469 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800470 // TODO: Add support for multiple share methods
471 if (!method.equals("ums")) {
472 throw new IllegalArgumentException(String.format("Method %s not supported", method));
473 }
474
San Mehat4270e1e2010-01-29 05:32:19 -0800475 try {
476 mConnector.doCommand(String.format(
477 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
478 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700479 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800480 }
San Mehat4270e1e2010-01-29 05:32:19 -0800481 }
482
San Mehat207e5382010-02-04 20:46:54 -0800483 private void updatePublicVolumeState(String path, String state) {
San Mehat4270e1e2010-01-29 05:32:19 -0800484 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -0700485 Slog.w(TAG, "Multiple volumes not currently supported");
San Mehat4270e1e2010-01-29 05:32:19 -0800486 return;
487 }
San Mehatb1043402010-02-05 08:26:50 -0800488
489 if (mLegacyState.equals(state)) {
San Mehata5078592010-03-25 09:36:54 -0700490 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
San Mehatb1043402010-02-05 08:26:50 -0800491 return;
492 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800493 // Update state on PackageManager
494 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700495 mPms.updateExternalMediaStatus(false, false);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800496 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700497 mPms.updateExternalMediaStatus(true, false);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800498 }
Kenny Root38cf8862010-09-26 14:18:51 -0700499
500 // Remove all OBB mappings and listeners from this path
501 synchronized (mObbMounts) {
502 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
503
504 final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator();
505 while (i.hasNext()) {
506 final Entry<String, ObbState> obbEntry = i.next();
507
508 // If this entry's source file is in the volume path that got
509 // unmounted, remove it because it's no longer valid.
510 if (obbEntry.getKey().startsWith(path)) {
511 obbStatesToRemove.add(obbEntry.getValue());
512 }
513 }
514
515 for (final ObbState obbState : obbStatesToRemove) {
516 removeObbState(obbState);
517
518 try {
519 obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED);
520 } catch (RemoteException e) {
521 Slog.i(TAG, "Couldn't send unmount notification for OBB: "
522 + obbState.filename);
523 }
524 }
525 }
526
San Mehat4270e1e2010-01-29 05:32:19 -0800527 String oldState = mLegacyState;
528 mLegacyState = state;
529
530 synchronized (mListeners) {
531 for (int i = mListeners.size() -1; i >= 0; i--) {
532 MountServiceBinderListener bl = mListeners.get(i);
533 try {
San Mehatb1043402010-02-05 08:26:50 -0800534 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800535 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700536 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800537 mListeners.remove(i);
538 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700539 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800540 }
541 }
542 }
543 }
544
545 /**
546 *
547 * Callback from NativeDaemonConnector
548 */
549 public void onDaemonConnected() {
550 /*
551 * Since we'll be calling back into the NativeDaemonConnector,
552 * we need to do our work in a new thread.
553 */
554 new Thread() {
555 public void run() {
556 /**
557 * Determine media state and UMS detection status
558 */
559 String path = Environment.getExternalStorageDirectory().getPath();
560 String state = Environment.MEDIA_REMOVED;
561
562 try {
563 String[] vols = mConnector.doListCommand(
564 "volume list", VoldResponseCode.VolumeListResult);
565 for (String volstr : vols) {
566 String[] tok = volstr.split(" ");
567 // FMT: <label> <mountpoint> <state>
568 if (!tok[1].equals(path)) {
San Mehata5078592010-03-25 09:36:54 -0700569 Slog.w(TAG, String.format(
San Mehat4270e1e2010-01-29 05:32:19 -0800570 "Skipping unknown volume '%s'",tok[1]));
571 continue;
572 }
573 int st = Integer.parseInt(tok[2]);
574 if (st == VolumeState.NoMedia) {
575 state = Environment.MEDIA_REMOVED;
576 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800577 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800578 } else if (st == VolumeState.Mounted) {
579 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700580 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800581 } else if (st == VolumeState.Shared) {
582 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700583 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800584 } else {
585 throw new Exception(String.format("Unexpected state %d", st));
586 }
587 }
588 if (state != null) {
San Mehata5078592010-03-25 09:36:54 -0700589 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
San Mehat4270e1e2010-01-29 05:32:19 -0800590 updatePublicVolumeState(path, state);
591 }
592 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700593 Slog.e(TAG, "Error processing initial volume state", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800594 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
595 }
596
597 try {
San Mehat207e5382010-02-04 20:46:54 -0800598 boolean avail = doGetShareMethodAvailable("ums");
San Mehat4270e1e2010-01-29 05:32:19 -0800599 notifyShareAvailabilityChange("ums", avail);
600 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700601 Slog.w(TAG, "Failed to get share availability");
San Mehat4270e1e2010-01-29 05:32:19 -0800602 }
San Mehat207e5382010-02-04 20:46:54 -0800603 /*
604 * Now that we've done our initialization, release
605 * the hounds!
606 */
607 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800608 }
609 }.start();
610 }
611
612 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800613 * Callback from NativeDaemonConnector
614 */
615 public boolean onEvent(int code, String raw, String[] cooked) {
616 Intent in = null;
617
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800618 if (DEBUG_EVENTS) {
619 StringBuilder builder = new StringBuilder();
620 builder.append("onEvent::");
621 builder.append(" raw= " + raw);
622 if (cooked != null) {
623 builder.append(" cooked = " );
624 for (String str : cooked) {
625 builder.append(" " + str);
626 }
627 }
San Mehata5078592010-03-25 09:36:54 -0700628 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800629 }
San Mehat4270e1e2010-01-29 05:32:19 -0800630 if (code == VoldResponseCode.VolumeStateChange) {
631 /*
632 * One of the volumes we're managing has changed state.
633 * Format: "NNN Volume <label> <path> state changed
634 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
635 */
636 notifyVolumeStateChange(
637 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
638 Integer.parseInt(cooked[10]));
639 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
640 // FMT: NNN Share method <method> now <available|unavailable>
641 boolean avail = false;
642 if (cooked[5].equals("available")) {
643 avail = true;
644 }
645 notifyShareAvailabilityChange(cooked[3], avail);
646 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
647 (code == VoldResponseCode.VolumeDiskRemoved) ||
648 (code == VoldResponseCode.VolumeBadRemoval)) {
649 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
650 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
651 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
652 final String label = cooked[2];
653 final String path = cooked[3];
654 int major = -1;
655 int minor = -1;
656
657 try {
658 String devComp = cooked[6].substring(1, cooked[6].length() -1);
659 String[] devTok = devComp.split(":");
660 major = Integer.parseInt(devTok[0]);
661 minor = Integer.parseInt(devTok[1]);
662 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700663 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800664 }
665
San Mehat4270e1e2010-01-29 05:32:19 -0800666 if (code == VoldResponseCode.VolumeDiskInserted) {
667 new Thread() {
668 public void run() {
669 try {
670 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800671 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700672 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800673 }
674 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700675 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800676 }
677 }
678 }.start();
679 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
680 /*
681 * This event gets trumped if we're already in BAD_REMOVAL state
682 */
683 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
684 return true;
685 }
686 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700687 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800688 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
689 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
690 mContext.sendBroadcast(in);
691
San Mehata5078592010-03-25 09:36:54 -0700692 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
San Mehat4270e1e2010-01-29 05:32:19 -0800693 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
694 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
695 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700696 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800697 /* Send the media unmounted event first */
698 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
699 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
700 mContext.sendBroadcast(in);
701
San Mehata5078592010-03-25 09:36:54 -0700702 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
San Mehat4270e1e2010-01-29 05:32:19 -0800703 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
704 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
705 } else {
San Mehata5078592010-03-25 09:36:54 -0700706 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800707 }
708 } else {
709 return false;
710 }
711
712 if (in != null) {
713 mContext.sendBroadcast(in);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400714 }
715 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800716 }
717
San Mehat207e5382010-02-04 20:46:54 -0800718 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800719 String vs = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -0700720 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
San Mehat4270e1e2010-01-29 05:32:19 -0800721
722 Intent in = null;
723
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500724 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700725 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500726 mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
727 Uri.parse("file://" + path)));
728 }
729
San Mehat4270e1e2010-01-29 05:32:19 -0800730 if (newState == VolumeState.Init) {
731 } else if (newState == VolumeState.NoMedia) {
732 // NoMedia is handled via Disk Remove events
733 } else if (newState == VolumeState.Idle) {
734 /*
735 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
736 * if we're in the process of enabling UMS
737 */
738 if (!vs.equals(
739 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
740 Environment.MEDIA_NOFS) && !vs.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800741 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700742 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
San Mehat4270e1e2010-01-29 05:32:19 -0800743 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
744 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
745 }
746 } else if (newState == VolumeState.Pending) {
747 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -0700748 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
San Mehat4270e1e2010-01-29 05:32:19 -0800749 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
750 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
751 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -0700752 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800753 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
San Mehat4270e1e2010-01-29 05:32:19 -0800754 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
755 in.putExtra("read-only", false);
756 } else if (newState == VolumeState.Unmounting) {
San Mehat4270e1e2010-01-29 05:32:19 -0800757 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
758 } else if (newState == VolumeState.Formatting) {
759 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -0700760 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800761 /* Send the media unmounted event first */
762 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
763 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
764 mContext.sendBroadcast(in);
765
San Mehata5078592010-03-25 09:36:54 -0700766 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
San Mehat4270e1e2010-01-29 05:32:19 -0800767 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
768 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
San Mehata5078592010-03-25 09:36:54 -0700769 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -0800770 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -0700771 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -0800772 return;
773 } else {
San Mehata5078592010-03-25 09:36:54 -0700774 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800775 }
776
777 if (in != null) {
778 mContext.sendBroadcast(in);
779 }
780 }
781
San Mehat207e5382010-02-04 20:46:54 -0800782 private boolean doGetShareMethodAvailable(String method) {
Kenny Root85fb2062010-06-01 20:50:21 -0700783 ArrayList<String> rsp;
Kenny Roota80ce062010-06-01 13:23:53 -0700784 try {
Kenny Root85fb2062010-06-01 20:50:21 -0700785 rsp = mConnector.doCommand("share status " + method);
Kenny Roota80ce062010-06-01 13:23:53 -0700786 } catch (NativeDaemonConnectorException ex) {
787 Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
788 return false;
789 }
San Mehat207e5382010-02-04 20:46:54 -0800790
791 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700792 String[] tok = line.split(" ");
793 if (tok.length < 3) {
794 Slog.e(TAG, "Malformed response to share status " + method);
795 return false;
796 }
797
San Mehat207e5382010-02-04 20:46:54 -0800798 int code;
799 try {
800 code = Integer.parseInt(tok[0]);
801 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700802 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehat207e5382010-02-04 20:46:54 -0800803 return false;
804 }
805 if (code == VoldResponseCode.ShareStatusResult) {
806 if (tok[2].equals("available"))
807 return true;
808 return false;
809 } else {
San Mehata5078592010-03-25 09:36:54 -0700810 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehat207e5382010-02-04 20:46:54 -0800811 return false;
812 }
813 }
San Mehata5078592010-03-25 09:36:54 -0700814 Slog.e(TAG, "Got an empty response");
San Mehat207e5382010-02-04 20:46:54 -0800815 return false;
816 }
817
818 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800819 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800820
San Mehata5078592010-03-25 09:36:54 -0700821 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -0800822 try {
823 mConnector.doCommand(String.format("volume mount %s", path));
824 } catch (NativeDaemonConnectorException e) {
825 /*
826 * Mount failed for some reason
827 */
828 Intent in = null;
829 int code = e.getCode();
830 if (code == VoldResponseCode.OpFailedNoMedia) {
831 /*
832 * Attempt to mount but no media inserted
833 */
San Mehatb1043402010-02-05 08:26:50 -0800834 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800835 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -0700836 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -0800837 /*
838 * Media is blank or does not contain a supported filesystem
839 */
840 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
841 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800842 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800843 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -0700844 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -0800845 /*
846 * Volume consistency check failed
847 */
848 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
849 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800850 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800851 } else {
San Mehatb1043402010-02-05 08:26:50 -0800852 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800853 }
854
855 /*
856 * Send broadcast intent (if required for the failure)
857 */
858 if (in != null) {
859 mContext.sendBroadcast(in);
860 }
861 }
862
863 return rc;
864 }
865
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800866 /*
867 * If force is not set, we do not unmount if there are
868 * processes holding references to the volume about to be unmounted.
869 * If force is set, all the processes holding references need to be
870 * killed via the ActivityManager before actually unmounting the volume.
871 * This might even take a while and might be retried after timed delays
872 * to make sure we dont end up in an instable state and kill some core
873 * processes.
874 */
San Mehatd9709982010-02-18 11:43:03 -0800875 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800876 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800877 return VoldResponseCode.OpFailedVolNotMounted;
878 }
Kenny Rootaa485402010-09-14 14:49:41 -0700879
880 /*
881 * Force a GC to make sure AssetManagers in other threads of the
882 * system_server are cleaned up. We have to do this since AssetManager
883 * instances are kept as a WeakReference and it's possible we have files
884 * open on the external storage.
885 */
886 Runtime.getRuntime().gc();
887
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800888 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700889 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -0800890 try {
San Mehatd9709982010-02-18 11:43:03 -0800891 mConnector.doCommand(String.format(
892 "volume unmount %s%s", path, (force ? " force" : "")));
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700893 // We unmounted the volume. None of the asec containers are available now.
894 synchronized (mAsecMountSet) {
895 mAsecMountSet.clear();
896 }
San Mehatb1043402010-02-05 08:26:50 -0800897 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800898 } catch (NativeDaemonConnectorException e) {
899 // Don't worry about mismatch in PackageManager since the
900 // call back will handle the status changes any way.
901 int code = e.getCode();
902 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800903 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800904 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
905 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -0800906 } else {
San Mehatb1043402010-02-05 08:26:50 -0800907 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800908 }
909 }
910 }
911
912 private int doFormatVolume(String path) {
913 try {
914 String cmd = String.format("volume format %s", path);
915 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800916 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800917 } catch (NativeDaemonConnectorException e) {
918 int code = e.getCode();
919 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800920 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800921 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800922 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800923 } else {
San Mehatb1043402010-02-05 08:26:50 -0800924 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800925 }
926 }
927 }
928
San Mehatb1043402010-02-05 08:26:50 -0800929 private boolean doGetVolumeShared(String path, String method) {
930 String cmd = String.format("volume shared %s %s", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -0700931 ArrayList<String> rsp;
932
933 try {
934 rsp = mConnector.doCommand(cmd);
935 } catch (NativeDaemonConnectorException ex) {
936 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
937 return false;
938 }
San Mehatb1043402010-02-05 08:26:50 -0800939
940 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700941 String[] tok = line.split(" ");
942 if (tok.length < 3) {
943 Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
944 return false;
945 }
946
San Mehatb1043402010-02-05 08:26:50 -0800947 int code;
948 try {
949 code = Integer.parseInt(tok[0]);
950 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700951 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehatb1043402010-02-05 08:26:50 -0800952 return false;
953 }
954 if (code == VoldResponseCode.ShareEnabledResult) {
Kenny Roota80ce062010-06-01 13:23:53 -0700955 return "enabled".equals(tok[2]);
San Mehatb1043402010-02-05 08:26:50 -0800956 } else {
San Mehata5078592010-03-25 09:36:54 -0700957 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehatb1043402010-02-05 08:26:50 -0800958 return false;
959 }
960 }
San Mehata5078592010-03-25 09:36:54 -0700961 Slog.e(TAG, "Got an empty response");
San Mehatb1043402010-02-05 08:26:50 -0800962 return false;
963 }
964
San Mehat207e5382010-02-04 20:46:54 -0800965 private void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800966 if (!method.equals("ums")) {
San Mehata5078592010-03-25 09:36:54 -0700967 Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800968 return;
969 }
970
971 synchronized (mListeners) {
972 for (int i = mListeners.size() -1; i >= 0; i--) {
973 MountServiceBinderListener bl = mListeners.get(i);
974 try {
San Mehatb1043402010-02-05 08:26:50 -0800975 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800976 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700977 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800978 mListeners.remove(i);
979 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700980 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800981 }
982 }
983 }
984
San Mehat207e5382010-02-04 20:46:54 -0800985 if (mBooted == true) {
San Mehat6a965af22010-02-24 17:47:30 -0800986 sendUmsIntent(avail);
987 } else {
988 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -0800989 }
San Mehat2fe718a2010-03-11 12:01:49 -0800990
991 final String path = Environment.getExternalStorageDirectory().getPath();
992 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
993 /*
994 * USB mass storage disconnected while enabled
995 */
996 new Thread() {
997 public void run() {
998 try {
999 int rc;
San Mehata5078592010-03-25 09:36:54 -07001000 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -08001001 doShareUnshareVolume(path, "ums", false);
1002 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001003 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -08001004 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
1005 path, rc));
1006 }
1007 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -07001008 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -08001009 }
1010 }
1011 }.start();
1012 }
San Mehat4270e1e2010-01-29 05:32:19 -08001013 }
1014
San Mehat6a965af22010-02-24 17:47:30 -08001015 private void sendUmsIntent(boolean c) {
1016 mContext.sendBroadcast(
1017 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
1018 }
1019
San Mehat207e5382010-02-04 20:46:54 -08001020 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -08001021 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
1022 throw new SecurityException(String.format("Requires %s permission", perm));
1023 }
1024 }
1025
1026 /**
San Mehat207e5382010-02-04 20:46:54 -08001027 * Constructs a new MountService instance
1028 *
1029 * @param context Binder context for this service
1030 */
1031 public MountService(Context context) {
1032 mContext = context;
1033
San Mehat207e5382010-02-04 20:46:54 -08001034 // XXX: This will go away soon in favor of IMountServiceObserver
1035 mPms = (PackageManagerService) ServiceManager.getService("package");
1036
1037 mContext.registerReceiver(mBroadcastReceiver,
1038 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
1039
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001040 mHandlerThread = new HandlerThread("MountService");
1041 mHandlerThread.start();
1042 mHandler = new MountServiceHandler(mHandlerThread.getLooper());
1043
Kenny Roota02b8b02010-08-05 16:14:17 -07001044 // Add OBB Action Handler to MountService thread.
1045 mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
1046
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001047 /*
1048 * Vold does not run in the simulator, so pretend the connector thread
1049 * ran and did its thing.
1050 */
1051 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
1052 mReady = true;
1053 mUmsEnabling = true;
1054 return;
1055 }
1056
Kenny Root305bcbf2010-09-03 07:56:38 -07001057 /*
1058 * Create the connection to vold with a maximum queue of twice the
1059 * amount of containers we'd ever expect to have. This keeps an
1060 * "asec list" from blocking a thread repeatedly.
1061 */
1062 mConnector = new NativeDaemonConnector(this, "vold",
1063 PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001064 mReady = false;
Kenny Root305bcbf2010-09-03 07:56:38 -07001065 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001066 thread.start();
1067 }
1068
1069 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001070 * Exposed API calls below here
1071 */
1072
1073 public void registerListener(IMountServiceListener listener) {
1074 synchronized (mListeners) {
1075 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1076 try {
1077 listener.asBinder().linkToDeath(bl, 0);
1078 mListeners.add(bl);
1079 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001080 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001081 }
1082 }
1083 }
1084
1085 public void unregisterListener(IMountServiceListener listener) {
1086 synchronized (mListeners) {
1087 for(MountServiceBinderListener bl : mListeners) {
1088 if (bl.mListener == listener) {
1089 mListeners.remove(mListeners.indexOf(bl));
1090 return;
1091 }
1092 }
1093 }
1094 }
1095
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001096 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001097 validatePermission(android.Manifest.permission.SHUTDOWN);
1098
San Mehata5078592010-03-25 09:36:54 -07001099 Slog.i(TAG, "Shutting down");
San Mehat4270e1e2010-01-29 05:32:19 -08001100
1101 String path = Environment.getExternalStorageDirectory().getPath();
1102 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -08001103
1104 if (state.equals(Environment.MEDIA_SHARED)) {
1105 /*
1106 * If the media is currently shared, unshare it.
1107 * XXX: This is still dangerous!. We should not
1108 * be rebooting at *all* if UMS is enabled, since
1109 * the UMS host could have dirty FAT cache entries
1110 * yet to flush.
1111 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001112 setUsbMassStorageEnabled(false);
San Mehat91c77612010-01-07 10:39:41 -08001113 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1114 /*
1115 * If the media is being checked, then we need to wait for
1116 * it to complete before being able to proceed.
1117 */
1118 // XXX: @hackbod - Should we disable the ANR timer here?
1119 int retries = 30;
1120 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1121 try {
1122 Thread.sleep(1000);
1123 } catch (InterruptedException iex) {
San Mehata5078592010-03-25 09:36:54 -07001124 Slog.e(TAG, "Interrupted while waiting for media", iex);
San Mehat91c77612010-01-07 10:39:41 -08001125 break;
1126 }
1127 state = Environment.getExternalStorageState();
1128 }
1129 if (retries == 0) {
San Mehata5078592010-03-25 09:36:54 -07001130 Slog.e(TAG, "Timed out waiting for media to check");
San Mehat91c77612010-01-07 10:39:41 -08001131 }
1132 }
1133
1134 if (state.equals(Environment.MEDIA_MOUNTED)) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001135 // Post a unmount message.
1136 ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
1137 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
San Mehat4270e1e2010-01-29 05:32:19 -08001138 }
1139 }
1140
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001141 private boolean getUmsEnabling() {
1142 synchronized (mListeners) {
1143 return mUmsEnabling;
1144 }
1145 }
1146
1147 private void setUmsEnabling(boolean enable) {
1148 synchronized (mListeners) {
1149 mUmsEnabling = true;
1150 }
1151 }
1152
San Mehatb1043402010-02-05 08:26:50 -08001153 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001154 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001155
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001156 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001157 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001158 }
San Mehatb1043402010-02-05 08:26:50 -08001159 return doGetShareMethodAvailable("ums");
1160 }
1161
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001162 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001163 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001164 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -08001165
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001166 // TODO: Add support for multiple share methods
1167
1168 /*
1169 * If the volume is mounted and we're enabling then unmount it
1170 */
1171 String path = Environment.getExternalStorageDirectory().getPath();
1172 String vs = getVolumeState(path);
1173 String method = "ums";
1174 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1175 // Override for isUsbMassStorageEnabled()
1176 setUmsEnabling(enable);
1177 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1178 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1179 // Clear override
1180 setUmsEnabling(false);
1181 }
1182 /*
1183 * If we disabled UMS then mount the volume
1184 */
1185 if (!enable) {
1186 doShareUnshareVolume(path, method, enable);
1187 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001188 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001189 " after disabling share method " + method);
1190 /*
1191 * Even though the mount failed, the unshare didn't so don't indicate an error.
1192 * The mountVolume() call will have set the storage state and sent the necessary
1193 * broadcasts.
1194 */
1195 }
1196 }
San Mehatb1043402010-02-05 08:26:50 -08001197 }
1198
1199 public boolean isUsbMassStorageEnabled() {
1200 waitForReady();
1201 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 }
San Mehat4270e1e2010-01-29 05:32:19 -08001203
San Mehat7fd0fee2009-12-17 07:12:23 -08001204 /**
1205 * @return state of the volume at the specified mount point
1206 */
San Mehat4270e1e2010-01-29 05:32:19 -08001207 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -08001208 /*
1209 * XXX: Until we have multiple volume discovery, just hardwire
1210 * this to /sdcard
1211 */
1212 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -07001213 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
San Mehat7fd0fee2009-12-17 07:12:23 -08001214 throw new IllegalArgumentException();
1215 }
1216
1217 return mLegacyState;
1218 }
1219
San Mehat4270e1e2010-01-29 05:32:19 -08001220 public int mountVolume(String path) {
1221 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001222
San Mehat207e5382010-02-04 20:46:54 -08001223 waitForReady();
1224 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 }
1226
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001227 public void unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001228 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001229 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001231 String volState = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -07001232 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001233 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1234 Environment.MEDIA_REMOVED.equals(volState) ||
1235 Environment.MEDIA_SHARED.equals(volState) ||
1236 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1237 // Media already unmounted or cannot be unmounted.
1238 // TODO return valid return code when adding observer call back.
1239 return;
1240 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001241 UnmountCallBack ucb = new UnmountCallBack(path, force);
1242 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 }
1244
San Mehat4270e1e2010-01-29 05:32:19 -08001245 public int formatVolume(String path) {
1246 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001247 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001248
San Mehat207e5382010-02-04 20:46:54 -08001249 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 }
1251
San Mehatc1b4ce92010-02-16 17:13:03 -08001252 public int []getStorageUsers(String path) {
1253 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1254 waitForReady();
1255 try {
1256 String[] r = mConnector.doListCommand(
1257 String.format("storage users %s", path),
1258 VoldResponseCode.StorageUsersListResult);
1259 // FMT: <pid> <process name>
1260 int[] data = new int[r.length];
1261 for (int i = 0; i < r.length; i++) {
1262 String []tok = r[i].split(" ");
1263 try {
1264 data[i] = Integer.parseInt(tok[0]);
1265 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001266 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001267 return new int[0];
1268 }
1269 }
1270 return data;
1271 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001272 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001273 return new int[0];
1274 }
1275 }
1276
San Mehatb1043402010-02-05 08:26:50 -08001277 private void warnOnNotMounted() {
1278 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
San Mehata5078592010-03-25 09:36:54 -07001279 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
San Mehatb1043402010-02-05 08:26:50 -08001280 }
1281 }
1282
San Mehat4270e1e2010-01-29 05:32:19 -08001283 public String[] getSecureContainerList() {
1284 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001285 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001286 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001287
San Mehat4270e1e2010-01-29 05:32:19 -08001288 try {
1289 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
1290 } catch (NativeDaemonConnectorException e) {
1291 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 }
1293 }
San Mehat36972292010-01-06 11:06:32 -08001294
San Mehat4270e1e2010-01-29 05:32:19 -08001295 public int createSecureContainer(String id, int sizeMb, String fstype,
1296 String key, int ownerUid) {
1297 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001298 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001299 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001300
San Mehatb1043402010-02-05 08:26:50 -08001301 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001302 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
1303 try {
1304 mConnector.doCommand(cmd);
1305 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001306 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001307 }
San Mehata181b212010-02-11 06:50:20 -08001308
1309 if (rc == StorageResultCode.OperationSucceeded) {
1310 synchronized (mAsecMountSet) {
1311 mAsecMountSet.add(id);
1312 }
1313 }
San Mehat4270e1e2010-01-29 05:32:19 -08001314 return rc;
San Mehat36972292010-01-06 11:06:32 -08001315 }
1316
San Mehat4270e1e2010-01-29 05:32:19 -08001317 public int finalizeSecureContainer(String id) {
1318 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001319 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001320
San Mehatb1043402010-02-05 08:26:50 -08001321 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001322 try {
1323 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -08001324 /*
1325 * Finalization does a remount, so no need
1326 * to update mAsecMountSet
1327 */
San Mehat4270e1e2010-01-29 05:32:19 -08001328 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001329 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001330 }
San Mehat4270e1e2010-01-29 05:32:19 -08001331 return rc;
San Mehat36972292010-01-06 11:06:32 -08001332 }
1333
San Mehatd9709982010-02-18 11:43:03 -08001334 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001335 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001336 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001337 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001338
Kenny Rootaa485402010-09-14 14:49:41 -07001339 /*
1340 * Force a GC to make sure AssetManagers in other threads of the
1341 * system_server are cleaned up. We have to do this since AssetManager
1342 * instances are kept as a WeakReference and it's possible we have files
1343 * open on the external storage.
1344 */
1345 Runtime.getRuntime().gc();
1346
San Mehatb1043402010-02-05 08:26:50 -08001347 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001348 try {
San Mehatd9709982010-02-18 11:43:03 -08001349 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -08001350 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001351 int code = e.getCode();
1352 if (code == VoldResponseCode.OpFailedStorageBusy) {
1353 rc = StorageResultCode.OperationFailedStorageBusy;
1354 } else {
1355 rc = StorageResultCode.OperationFailedInternalError;
1356 }
San Mehat02735bc2010-01-26 15:18:08 -08001357 }
San Mehata181b212010-02-11 06:50:20 -08001358
1359 if (rc == StorageResultCode.OperationSucceeded) {
1360 synchronized (mAsecMountSet) {
1361 if (mAsecMountSet.contains(id)) {
1362 mAsecMountSet.remove(id);
1363 }
1364 }
1365 }
1366
San Mehat4270e1e2010-01-29 05:32:19 -08001367 return rc;
San Mehat36972292010-01-06 11:06:32 -08001368 }
1369
San Mehat4270e1e2010-01-29 05:32:19 -08001370 public int mountSecureContainer(String id, String key, int ownerUid) {
1371 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001372 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001373 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001374
San Mehata181b212010-02-11 06:50:20 -08001375 synchronized (mAsecMountSet) {
1376 if (mAsecMountSet.contains(id)) {
1377 return StorageResultCode.OperationFailedStorageMounted;
1378 }
1379 }
1380
San Mehatb1043402010-02-05 08:26:50 -08001381 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001382 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
1383 try {
1384 mConnector.doCommand(cmd);
1385 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001386 int code = e.getCode();
1387 if (code != VoldResponseCode.OpFailedStorageBusy) {
1388 rc = StorageResultCode.OperationFailedInternalError;
1389 }
San Mehat02735bc2010-01-26 15:18:08 -08001390 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001391
1392 if (rc == StorageResultCode.OperationSucceeded) {
1393 synchronized (mAsecMountSet) {
1394 mAsecMountSet.add(id);
1395 }
1396 }
San Mehat4270e1e2010-01-29 05:32:19 -08001397 return rc;
San Mehat36972292010-01-06 11:06:32 -08001398 }
1399
San Mehatd9709982010-02-18 11:43:03 -08001400 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001401 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001402 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001403 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001404
San Mehat6cdd9c02010-02-09 14:45:20 -08001405 synchronized (mAsecMountSet) {
1406 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001407 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001408 }
1409 }
1410
Kenny Rootaa485402010-09-14 14:49:41 -07001411 /*
1412 * Force a GC to make sure AssetManagers in other threads of the
1413 * system_server are cleaned up. We have to do this since AssetManager
1414 * instances are kept as a WeakReference and it's possible we have files
1415 * open on the external storage.
1416 */
1417 Runtime.getRuntime().gc();
1418
San Mehatb1043402010-02-05 08:26:50 -08001419 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -08001420 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -08001421 try {
1422 mConnector.doCommand(cmd);
1423 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001424 int code = e.getCode();
1425 if (code == VoldResponseCode.OpFailedStorageBusy) {
1426 rc = StorageResultCode.OperationFailedStorageBusy;
1427 } else {
1428 rc = StorageResultCode.OperationFailedInternalError;
1429 }
San Mehat02735bc2010-01-26 15:18:08 -08001430 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001431
1432 if (rc == StorageResultCode.OperationSucceeded) {
1433 synchronized (mAsecMountSet) {
1434 mAsecMountSet.remove(id);
1435 }
1436 }
San Mehat4270e1e2010-01-29 05:32:19 -08001437 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001438 }
1439
San Mehat6cdd9c02010-02-09 14:45:20 -08001440 public boolean isSecureContainerMounted(String id) {
1441 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1442 waitForReady();
1443 warnOnNotMounted();
1444
1445 synchronized (mAsecMountSet) {
1446 return mAsecMountSet.contains(id);
1447 }
1448 }
1449
San Mehat4270e1e2010-01-29 05:32:19 -08001450 public int renameSecureContainer(String oldId, String newId) {
1451 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001452 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001453 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001454
San Mehata181b212010-02-11 06:50:20 -08001455 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001456 /*
1457 * Because a mounted container has active internal state which cannot be
1458 * changed while active, we must ensure both ids are not currently mounted.
1459 */
1460 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001461 return StorageResultCode.OperationFailedStorageMounted;
1462 }
1463 }
1464
San Mehatb1043402010-02-05 08:26:50 -08001465 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001466 String cmd = String.format("asec rename %s %s", oldId, newId);
1467 try {
1468 mConnector.doCommand(cmd);
1469 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001470 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001471 }
San Mehata181b212010-02-11 06:50:20 -08001472
San Mehat4270e1e2010-01-29 05:32:19 -08001473 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001474 }
1475
San Mehat4270e1e2010-01-29 05:32:19 -08001476 public String getSecureContainerPath(String id) {
1477 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001478 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001479 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001480
San Mehat2d66cef2010-03-23 11:12:52 -07001481 try {
1482 ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
1483 String []tok = rsp.get(0).split(" ");
San Mehat22dd86e2010-01-12 12:21:18 -08001484 int code = Integer.parseInt(tok[0]);
San Mehat2d66cef2010-03-23 11:12:52 -07001485 if (code != VoldResponseCode.AsecPathResult) {
1486 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1487 }
1488 return tok[1];
1489 } catch (NativeDaemonConnectorException e) {
1490 int code = e.getCode();
1491 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1492 throw new IllegalArgumentException(String.format("Container '%s' not found", id));
San Mehat22dd86e2010-01-12 12:21:18 -08001493 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001494 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001495 }
1496 }
San Mehat22dd86e2010-01-12 12:21:18 -08001497 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001498
1499 public void finishMediaUpdate() {
1500 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
1501 }
Kenny Root02c87302010-07-01 08:10:18 -07001502
Kenny Roota02b8b02010-08-05 16:14:17 -07001503 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1504 if (callerUid == android.os.Process.SYSTEM_UID) {
1505 return true;
1506 }
1507
Kenny Root02c87302010-07-01 08:10:18 -07001508 if (packageName == null) {
1509 return false;
1510 }
1511
1512 final int packageUid = mPms.getPackageUid(packageName);
1513
1514 if (DEBUG_OBB) {
1515 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1516 packageUid + ", callerUid = " + callerUid);
1517 }
1518
1519 return callerUid == packageUid;
1520 }
1521
1522 public String getMountedObbPath(String filename) {
1523 waitForReady();
1524 warnOnNotMounted();
1525
Kenny Root02c87302010-07-01 08:10:18 -07001526 try {
1527 ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
1528 String []tok = rsp.get(0).split(" ");
1529 int code = Integer.parseInt(tok[0]);
1530 if (code != VoldResponseCode.AsecPathResult) {
1531 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1532 }
1533 return tok[1];
1534 } catch (NativeDaemonConnectorException e) {
1535 int code = e.getCode();
1536 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001537 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001538 } else {
1539 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1540 }
1541 }
1542 }
1543
1544 public boolean isObbMounted(String filename) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001545 synchronized (mObbMounts) {
Kenny Root38cf8862010-09-26 14:18:51 -07001546 final ObbState obbState = mObbPathToStateMap.get(filename);
1547 if (obbState != null) {
1548 synchronized (obbState) {
1549 return obbState.mounted;
1550 }
1551 }
Kenny Root02c87302010-07-01 08:10:18 -07001552 }
Kenny Root38cf8862010-09-26 14:18:51 -07001553 return false;
Kenny Root02c87302010-07-01 08:10:18 -07001554 }
1555
Kenny Roota02b8b02010-08-05 16:14:17 -07001556 public void mountObb(String filename, String key, IObbActionListener token) {
Kenny Root02c87302010-07-01 08:10:18 -07001557 waitForReady();
1558 warnOnNotMounted();
1559
Kenny Rootf1121dc2010-09-29 07:30:53 -07001560 if (filename == null) {
1561 throw new IllegalArgumentException("filename cannot be null");
1562 } else if (token == null) {
1563 throw new IllegalArgumentException("token cannot be null");
1564 }
1565
Kenny Roota02b8b02010-08-05 16:14:17 -07001566 final ObbState obbState;
1567
1568 synchronized (mObbMounts) {
1569 if (isObbMounted(filename)) {
Kenny Root38cf8862010-09-26 14:18:51 -07001570 try {
1571 token.onObbResult(filename, Environment.MEDIA_MOUNTED);
1572 } catch (RemoteException e) {
1573 Slog.d(TAG, "Could not send unmount notification for: " + filename);
1574 }
1575 return;
Kenny Root02c87302010-07-01 08:10:18 -07001576 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001577
Kenny Roota02b8b02010-08-05 16:14:17 -07001578 final int callerUid = Binder.getCallingUid();
1579 obbState = new ObbState(filename, token, callerUid);
1580 addObbState(obbState);
Kenny Root02c87302010-07-01 08:10:18 -07001581 }
1582
Kenny Root02c87302010-07-01 08:10:18 -07001583 try {
Kenny Roota02b8b02010-08-05 16:14:17 -07001584 token.asBinder().linkToDeath(obbState, 0);
1585 } catch (RemoteException rex) {
1586 Slog.e(TAG, "Failed to link to listener death");
Kenny Root02c87302010-07-01 08:10:18 -07001587 }
1588
Kenny Root38cf8862010-09-26 14:18:51 -07001589 ObbAction action = new MountObbAction(obbState, key);
Kenny Roota02b8b02010-08-05 16:14:17 -07001590 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1591
1592 if (DEBUG_OBB)
1593 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001594 }
1595
Kenny Roota02b8b02010-08-05 16:14:17 -07001596 public void unmountObb(String filename, boolean force, IObbActionListener token) {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001597 if (filename == null) {
1598 throw new IllegalArgumentException("filename cannot be null");
1599 } else if (token == null) {
1600 throw new IllegalArgumentException("token cannot be null");
1601 }
1602
Kenny Roota02b8b02010-08-05 16:14:17 -07001603 final ObbState obbState;
Kenny Root02c87302010-07-01 08:10:18 -07001604
Kenny Roota02b8b02010-08-05 16:14:17 -07001605 synchronized (mObbMounts) {
1606 if (!isObbMounted(filename)) {
Kenny Root38cf8862010-09-26 14:18:51 -07001607 try {
1608 token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
1609 } catch (RemoteException e) {
1610 Slog.d(TAG, "Could not send unmount notification for: " + filename);
1611 }
1612 return;
Kenny Roota02b8b02010-08-05 16:14:17 -07001613 }
Kenny Root38cf8862010-09-26 14:18:51 -07001614
Kenny Roota02b8b02010-08-05 16:14:17 -07001615 obbState = mObbPathToStateMap.get(filename);
Kenny Rootf1121dc2010-09-29 07:30:53 -07001616
1617 if (Binder.getCallingUid() != obbState.callerUid) {
1618 throw new SecurityException("caller UID does not match original mount caller UID");
1619 } else if (!token.asBinder().equals(obbState.token.asBinder())) {
1620 throw new SecurityException("caller does not match original mount caller");
1621 }
Kenny Root02c87302010-07-01 08:10:18 -07001622 }
1623
Kenny Root38cf8862010-09-26 14:18:51 -07001624 ObbAction action = new UnmountObbAction(obbState, force);
Kenny Roota02b8b02010-08-05 16:14:17 -07001625 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001626
Kenny Roota02b8b02010-08-05 16:14:17 -07001627 if (DEBUG_OBB)
1628 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1629 }
1630
1631 private void addObbState(ObbState obbState) {
1632 synchronized (mObbMounts) {
Kenny Root05105f72010-09-22 17:29:43 -07001633 List<ObbState> obbStates = mObbMounts.get(obbState.token);
1634 if (obbStates == null) {
1635 obbStates = new ArrayList<ObbState>();
1636 mObbMounts.put(obbState.token, obbStates);
1637 }
1638 obbStates.add(obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07001639 mObbPathToStateMap.put(obbState.filename, obbState);
1640 }
1641 }
1642
1643 private void removeObbState(ObbState obbState) {
1644 synchronized (mObbMounts) {
Kenny Root05105f72010-09-22 17:29:43 -07001645 final List<ObbState> obbStates = mObbMounts.get(obbState.token);
1646 if (obbStates != null) {
1647 obbStates.remove(obbState);
1648 }
1649 if (obbStates == null || obbStates.isEmpty()) {
1650 mObbMounts.remove(obbState.token);
1651 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001652 mObbPathToStateMap.remove(obbState.filename);
1653 }
1654 }
1655
Kenny Root38cf8862010-09-26 14:18:51 -07001656 private void replaceObbState(ObbState oldObbState, ObbState newObbState) {
1657 synchronized (mObbMounts) {
1658 removeObbState(oldObbState);
1659 addObbState(newObbState);
1660 }
1661 }
1662
Kenny Roota02b8b02010-08-05 16:14:17 -07001663 private class ObbActionHandler extends Handler {
1664 private boolean mBound = false;
1665 private List<ObbAction> mActions = new LinkedList<ObbAction>();
1666
1667 ObbActionHandler(Looper l) {
1668 super(l);
1669 }
1670
1671 @Override
1672 public void handleMessage(Message msg) {
1673 switch (msg.what) {
1674 case OBB_RUN_ACTION: {
1675 ObbAction action = (ObbAction) msg.obj;
1676
1677 if (DEBUG_OBB)
1678 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
1679
1680 // If a bind was already initiated we don't really
1681 // need to do anything. The pending install
1682 // will be processed later on.
1683 if (!mBound) {
1684 // If this is the only one pending we might
1685 // have to bind to the service again.
1686 if (!connectToService()) {
1687 Slog.e(TAG, "Failed to bind to media container service");
1688 action.handleError();
1689 return;
1690 } else {
1691 // Once we bind to the service, the first
1692 // pending request will be processed.
1693 mActions.add(action);
1694 }
1695 } else {
1696 // Already bound to the service. Just make
1697 // sure we trigger off processing the first request.
1698 if (mActions.size() == 0) {
1699 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
1700 }
1701
1702 mActions.add(action);
1703 }
1704 break;
1705 }
1706 case OBB_MCS_BOUND: {
1707 if (DEBUG_OBB)
1708 Slog.i(TAG, "OBB_MCS_BOUND");
1709 if (msg.obj != null) {
1710 mContainerService = (IMediaContainerService) msg.obj;
1711 }
1712 if (mContainerService == null) {
1713 // Something seriously wrong. Bail out
1714 Slog.e(TAG, "Cannot bind to media container service");
1715 for (ObbAction action : mActions) {
1716 // Indicate service bind error
1717 action.handleError();
1718 }
1719 mActions.clear();
1720 } else if (mActions.size() > 0) {
1721 ObbAction action = mActions.get(0);
1722 if (action != null) {
1723 action.execute(this);
1724 }
1725 } else {
1726 // Should never happen ideally.
1727 Slog.w(TAG, "Empty queue");
1728 }
1729 break;
1730 }
1731 case OBB_MCS_RECONNECT: {
1732 if (DEBUG_OBB)
1733 Slog.i(TAG, "OBB_MCS_RECONNECT");
1734 if (mActions.size() > 0) {
1735 if (mBound) {
1736 disconnectService();
1737 }
1738 if (!connectToService()) {
1739 Slog.e(TAG, "Failed to bind to media container service");
1740 for (ObbAction action : mActions) {
1741 // Indicate service bind error
1742 action.handleError();
1743 }
1744 mActions.clear();
1745 }
1746 }
1747 break;
1748 }
1749 case OBB_MCS_UNBIND: {
1750 if (DEBUG_OBB)
1751 Slog.i(TAG, "OBB_MCS_UNBIND");
1752
1753 // Delete pending install
1754 if (mActions.size() > 0) {
1755 mActions.remove(0);
1756 }
1757 if (mActions.size() == 0) {
1758 if (mBound) {
1759 disconnectService();
1760 }
1761 } else {
1762 // There are more pending requests in queue.
1763 // Just post MCS_BOUND message to trigger processing
1764 // of next pending install.
1765 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
1766 }
1767 break;
1768 }
1769 case OBB_MCS_GIVE_UP: {
1770 if (DEBUG_OBB)
1771 Slog.i(TAG, "OBB_MCS_GIVE_UP");
1772 mActions.remove(0);
1773 break;
1774 }
1775 }
1776 }
1777
1778 private boolean connectToService() {
1779 if (DEBUG_OBB)
1780 Slog.i(TAG, "Trying to bind to DefaultContainerService");
1781
1782 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
1783 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
1784 mBound = true;
1785 return true;
1786 }
1787 return false;
1788 }
1789
1790 private void disconnectService() {
1791 mContainerService = null;
1792 mBound = false;
1793 mContext.unbindService(mDefContainerConn);
1794 }
1795 }
1796
1797 abstract class ObbAction {
1798 private static final int MAX_RETRIES = 3;
1799 private int mRetries;
1800
1801 ObbState mObbState;
1802
1803 ObbAction(ObbState obbState) {
1804 mObbState = obbState;
1805 }
1806
1807 public void execute(ObbActionHandler handler) {
1808 try {
1809 if (DEBUG_OBB)
1810 Slog.i(TAG, "Starting to execute action: " + this.toString());
1811 mRetries++;
1812 if (mRetries > MAX_RETRIES) {
1813 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
1814 mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP);
1815 handleError();
1816 return;
1817 } else {
1818 handleExecute();
1819 if (DEBUG_OBB)
1820 Slog.i(TAG, "Posting install MCS_UNBIND");
1821 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
1822 }
1823 } catch (RemoteException e) {
1824 if (DEBUG_OBB)
1825 Slog.i(TAG, "Posting install MCS_RECONNECT");
1826 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
1827 } catch (Exception e) {
1828 if (DEBUG_OBB)
1829 Slog.d(TAG, "Error handling OBB action", e);
1830 handleError();
1831 }
1832 }
1833
Kenny Root05105f72010-09-22 17:29:43 -07001834 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07001835 abstract void handleError();
Kenny Root38cf8862010-09-26 14:18:51 -07001836
1837 protected ObbInfo getObbInfo() throws IOException {
1838 ObbInfo obbInfo;
1839 try {
1840 obbInfo = mContainerService.getObbInfo(mObbState.filename);
1841 } catch (RemoteException e) {
1842 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
1843 + mObbState.filename);
1844 obbInfo = null;
1845 }
1846 if (obbInfo == null) {
1847 throw new IOException("Couldn't read OBB file: " + mObbState.filename);
1848 }
1849 return obbInfo;
1850 }
1851
1852 protected void sendNewStatusOrIgnore(String filename, String status) {
1853 try {
1854 mObbState.token.onObbResult(filename, status);
1855 } catch (RemoteException e) {
1856 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
1857 }
1858 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001859 }
1860
1861 class MountObbAction extends ObbAction {
1862 private String mKey;
1863
1864 MountObbAction(ObbState obbState, String key) {
1865 super(obbState);
1866 mKey = key;
1867 }
1868
Kenny Root38cf8862010-09-26 14:18:51 -07001869 public void handleExecute() throws IOException {
1870 final ObbInfo obbInfo = getObbInfo();
1871
1872 /*
1873 * If someone tried to trick us with some weird characters, rectify
1874 * it here.
1875 */
1876 if (!mObbState.filename.equals(obbInfo.filename)) {
1877 if (DEBUG_OBB)
1878 Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually "
1879 + obbInfo.filename);
1880
1881 synchronized (mObbMounts) {
1882 /*
1883 * If the real filename is already mounted, discard this
1884 * state and notify the caller that the OBB is already
1885 * mounted.
1886 */
1887 if (isObbMounted(obbInfo.filename)) {
1888 if (DEBUG_OBB)
1889 Slog.i(TAG, "OBB already mounted as " + obbInfo.filename);
1890
1891 removeObbState(mObbState);
1892 sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED);
1893 return;
1894 }
1895
1896 /*
1897 * It's not already mounted, so we have to replace the state
1898 * with the state containing the actual filename.
1899 */
1900 ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token,
1901 mObbState.callerUid);
1902 replaceObbState(mObbState, newObbState);
1903 mObbState = newObbState;
1904 }
Kenny Root05105f72010-09-22 17:29:43 -07001905 }
1906
Kenny Roota02b8b02010-08-05 16:14:17 -07001907 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
1908 throw new IllegalArgumentException("Caller package does not match OBB file");
1909 }
1910
1911 if (mKey == null) {
1912 mKey = "none";
1913 }
1914
Kenny Root38cf8862010-09-26 14:18:51 -07001915 boolean mounted = false;
1916 int rc;
1917 synchronized (mObbState) {
1918 if (mObbState.mounted) {
1919 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
1920 return;
1921 }
1922
1923 rc = StorageResultCode.OperationSucceeded;
1924 String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
1925 mObbState.callerUid);
1926 try {
1927 mConnector.doCommand(cmd);
1928 } catch (NativeDaemonConnectorException e) {
1929 int code = e.getCode();
1930 if (code != VoldResponseCode.OpFailedStorageBusy) {
1931 rc = StorageResultCode.OperationFailedInternalError;
1932 }
1933 }
1934
1935 if (rc == StorageResultCode.OperationSucceeded) {
1936 mObbState.mounted = mounted = true;
Kenny Roota02b8b02010-08-05 16:14:17 -07001937 }
1938 }
1939
Kenny Root38cf8862010-09-26 14:18:51 -07001940 if (mounted) {
1941 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07001942 } else {
Kenny Root05105f72010-09-22 17:29:43 -07001943 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07001944
1945 // We didn't succeed, so remove this from the mount-set.
1946 removeObbState(mObbState);
Kenny Root05105f72010-09-22 17:29:43 -07001947
Kenny Root38cf8862010-09-26 14:18:51 -07001948 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
Kenny Root02c87302010-07-01 08:10:18 -07001949 }
1950 }
1951
Kenny Roota02b8b02010-08-05 16:14:17 -07001952 public void handleError() {
1953 removeObbState(mObbState);
1954
Kenny Root38cf8862010-09-26 14:18:51 -07001955 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
Kenny Root02c87302010-07-01 08:10:18 -07001956 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001957
1958 @Override
1959 public String toString() {
1960 StringBuilder sb = new StringBuilder();
1961 sb.append("MountObbAction{");
1962 sb.append("filename=");
1963 sb.append(mObbState.filename);
1964 sb.append(",callerUid=");
1965 sb.append(mObbState.callerUid);
1966 sb.append(",token=");
1967 sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
1968 sb.append('}');
1969 return sb.toString();
1970 }
1971 }
1972
1973 class UnmountObbAction extends ObbAction {
1974 private boolean mForceUnmount;
1975
1976 UnmountObbAction(ObbState obbState, boolean force) {
1977 super(obbState);
1978 mForceUnmount = force;
1979 }
1980
Kenny Root38cf8862010-09-26 14:18:51 -07001981 public void handleExecute() throws IOException {
1982 final ObbInfo obbInfo = getObbInfo();
Kenny Roota02b8b02010-08-05 16:14:17 -07001983
Kenny Root38cf8862010-09-26 14:18:51 -07001984 /*
1985 * If someone tried to trick us with some weird characters, rectify
1986 * it here.
1987 */
1988 synchronized (mObbMounts) {
1989 if (!isObbMounted(obbInfo.filename)) {
1990 removeObbState(mObbState);
1991 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
1992 return;
1993 }
1994
1995 if (!mObbState.filename.equals(obbInfo.filename)) {
1996 removeObbState(mObbState);
1997 mObbState = mObbPathToStateMap.get(obbInfo.filename);
Kenny Roota02b8b02010-08-05 16:14:17 -07001998 }
1999 }
2000
Kenny Root38cf8862010-09-26 14:18:51 -07002001 boolean unmounted = false;
2002 synchronized (mObbState) {
2003 if (!mObbState.mounted) {
2004 sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED);
2005 return;
2006 }
2007
2008 int rc = StorageResultCode.OperationSucceeded;
2009 String cmd = String.format("obb unmount %s%s", mObbState.filename,
2010 (mForceUnmount ? " force" : ""));
2011 try {
2012 mConnector.doCommand(cmd);
2013 } catch (NativeDaemonConnectorException e) {
2014 int code = e.getCode();
2015 if (code == VoldResponseCode.OpFailedStorageBusy) {
2016 rc = StorageResultCode.OperationFailedStorageBusy;
2017 } else {
2018 rc = StorageResultCode.OperationFailedInternalError;
2019 }
2020 }
2021
2022 if (rc == StorageResultCode.OperationSucceeded) {
2023 mObbState.mounted = false;
2024 unmounted = true;
2025 }
2026 }
2027
2028 if (unmounted) {
Kenny Roota02b8b02010-08-05 16:14:17 -07002029 removeObbState(mObbState);
2030
Kenny Root38cf8862010-09-26 14:18:51 -07002031 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002032 } else {
Kenny Root38cf8862010-09-26 14:18:51 -07002033 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07002034 }
2035 }
2036
2037 public void handleError() {
2038 removeObbState(mObbState);
2039
Kenny Root38cf8862010-09-26 14:18:51 -07002040 sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07002041 }
2042
2043 @Override
2044 public String toString() {
2045 StringBuilder sb = new StringBuilder();
2046 sb.append("UnmountObbAction{");
2047 sb.append("filename=");
2048 sb.append(mObbState.filename != null ? mObbState.filename : "null");
2049 sb.append(",force=");
2050 sb.append(mForceUnmount);
2051 sb.append(",callerUid=");
2052 sb.append(mObbState.callerUid);
2053 sb.append(",token=");
2054 sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
2055 sb.append('}');
2056 return sb.toString();
2057 }
Kenny Root02c87302010-07-01 08:10:18 -07002058 }
Kenny Root38cf8862010-09-26 14:18:51 -07002059
2060 @Override
2061 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2062 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
2063 pw.println("Permission Denial: can't dump ActivityManager from from pid="
2064 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
2065 + " without permission " + android.Manifest.permission.DUMP);
2066 return;
2067 }
2068
2069 pw.println(" mObbMounts:");
2070
2071 synchronized (mObbMounts) {
2072 final Collection<List<ObbState>> obbStateLists = mObbMounts.values();
2073
2074 for (final List<ObbState> obbStates : obbStateLists) {
2075 for (final ObbState obbState : obbStates) {
2076 pw.print(" "); pw.println(obbState.toString());
2077 }
2078 }
2079 }
2080 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002081}
2082